/*****************************************************************************/ /* MapOds.c Functions supporting mapping of URL-style specifications to VMS file system specifications and back. Can process ODS-2, ODS-5 (EFS), SRI (MultiNet NFS), PATHWORKS (v4/5) and Advanced Server (PATHWORKS V6) / Samba encodings. VERSION HISTORY --------------- 14-OCT-2018 MGD bugfix; MapOdsUrlToOds5Vms() semicolon only after period 22-JUN-2016 MGD bugfix; MapOdsUrlToOds5Vms() URLs will not contain '^'-escaped sequences so just '^'-escape them 18-JUL-2013 MGD bugfix; MapOds5VmsToUrl() et.al. allow for ".][" 05-SEP-2013 MGD bugfix; identify MFD using "000000]" and "000000." 28-DEC-2010 MGD MapOdsUrlTo..() consecutive '/' into a single a la Unix 29-APR-2010 MGD bugfix; MapOdsUrlToOds5Vms() MapOdsElementsToVms() include '|' and '%' as ODS-5 escaped characters 20-JAN-2009 MGD bugfix; MapOdsUrlToOds5Vms and MapOds5VmsToUrl() encode the quotation character 30-APR-2005 MGD bugfix; DECnet access string may contain a space 17-MAR-2005 MGD MapOdsElementsToVms() avoid accidentally creating a DECnet ("::") file specification when generating device 10-DEC-2004 MGD bugfix; MapOdsVmsToUnix() empty if empty 07-NOV-2004 MGD MapOdsVmsToUnix() to support script=SYNTAX=unix 07-OCT-2004 MGD unbundled from MAPURL.C */ /*****************************************************************************/ #ifdef WASD_VMS_V7 #undef _VMS__V6__SOURCE #define _VMS__V6__SOURCE #undef __VMS_VER #undef __VMS_VER #undef __CRTL_VER #define __CRTL_VER 70000000 #endif /* VMS related header files */ #include #include #include #include /* application header file */ #include "wasd.h" #define WASD_MODULE "MAPODS" /**********/ /* macros */ /**********/ #define MAPURL_RMS_SUBSTITUTION_DEFAULT '$' #define WATCH_MODULE_DETAIL (WATCH_MODULE(WATCH_MOD_MAPURL) && \ WATCH_MODULE(WATCH_MOD__DETAIL)) /********************/ /* external storage */ /********************/ extern int EfnWait; extern unsigned long SysPrvMask[]; extern char *FaoUrlEncodeTable[]; extern int ToLowerCase[], ToUpperCase[]; extern WATCH_STRUCT Watch; /*****************************************************************************/ /* Convert a URL-style specification into a RMS-style specification, example: "/disk/dir1/dir2/file.txt" into "disk:[dir1.dir2]file.txt". Can process ODS-2, ODS-5 (EFS), SRI (MultiNet NFS), PATHWORKS (v4/5) and Advanced Server (PATHWORKS V6) / Samba encodings. Returns the length of the VMS specification. */ int MapOdsUrlToVms ( char *PathPtr, char *VmsPtr, int SizeOfVmsPtr, char RmsSubChar, BOOL MapEllipsis, int PathOds ) { int len; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_MAPURL)) WatchThis (WATCHALL, WATCH_MOD_MAPURL, "MapOdsUrlToVms() !UL !UL !&B !UL !&Z", PathOds, RmsSubChar, MapEllipsis, SizeOfVmsPtr, PathPtr); switch (PathOds) { case 0 : case MAPURL_PATH_ODS_2 : len = MapOdsUrlToOds2Vms (PathPtr, VmsPtr, SizeOfVmsPtr, MapEllipsis, RmsSubChar); break; #ifdef ODS_EXTENDED case MAPURL_PATH_ODS_5 : len = MapOdsUrlToOds5Vms (PathPtr, VmsPtr, SizeOfVmsPtr, MapEllipsis); break; #endif /* ODS_EXTENDED */ case MAPURL_PATH_ODS_ADS : case MAPURL_PATH_ODS_SMB : len = MapOdsUrlToAdsVms (PathPtr, VmsPtr, SizeOfVmsPtr, MapEllipsis); break; case MAPURL_PATH_ODS_PWK : len = MapOdsUrlToPwkVms (PathPtr, VmsPtr, SizeOfVmsPtr, MapEllipsis); break; case MAPURL_PATH_ODS_SRI : len = MapOdsUrlToSriVms (PathPtr, VmsPtr, SizeOfVmsPtr, MapEllipsis); break; default : memcpy (VmsPtr, "SANITY:[CHECK]MapOdsUrlToVms", len=30); } /* ensure it's not mistaken for an error message! */ if (!VmsPtr[0]) VmsPtr[1] = '\0'; if (WATCH_MODULE(WATCH_MOD_MAPURL)) WatchDataFormatted ("!UL !&Z\n", len, VmsPtr); return (len); } /*****************************************************************************/ /* Convert a URL-style specification into an ODS-2 RMS-style specification, example: "/disk/dir1/dir2/file.txt" into "disk:[dir1.dir2]file.txt". Forbidden characters (non-alphanumberic and non-"$_-") are converted to the 'RmsSubChar' (by default a dollar symbol). Where a specification has multiple ".", all but the final one of the file component is also converted to a 'RmsSubChar'. Returns the length of the VMS specification. */ int MapOdsUrlToOds2Vms ( char *PathPtr, char *VmsPtr, int SizeOfVmsPtr, BOOL MapEllipsis, char RmsSubChar ) { BOOL DECnet; int ecnt, len; char *cptr, *eptr, *pptr, *sptr, *zptr, *FinalPeriodPtr; char PathComponents [ODS_MAX_FILE_NAME_LENGTH]; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_MAPURL)) WatchThis (WATCHALL, WATCH_MOD_MAPURL, "MapOdsUrlToOds2Vms() !&B !UL !&Z !UL", MapEllipsis, RmsSubChar, PathPtr, SizeOfVmsPtr); pptr = PathPtr; if (!pptr || !*pptr || SAME2(pptr,'/\0')) { SET2(VmsPtr,'\0\0'); return (0); } DECnet = false; ecnt = 0; FinalPeriodPtr = NULL; zptr = (sptr = PathComponents) + sizeof(PathComponents); /* each '/' component becomes a null-terminated string */ while (*pptr && sptr < zptr) { while (*pptr == '/') pptr++; eptr = sptr; while (*pptr && *pptr != '/' && sptr < zptr) { if (isalnum (*pptr) || *pptr == '$' || *pptr == '_' || *pptr == '-' || *pptr == '*' || *pptr == '%' || *pptr == ';') { /* ODS-2 legal character (or wildcard) */ *sptr++ = TOUP(*pptr++); continue; } if (SAME3(pptr,'...')) { /* ellipsis wildcard */ *sptr++ = *pptr++; if (sptr < zptr) *sptr++ = *pptr++; if (sptr < zptr) *sptr++ = *pptr++; continue; } if (!ecnt) { /* DECnet only valid in first component */ if (*pptr == '\"' || *pptr == '=') { /* DECnet access string (allow otherwise forbidden chars) */ DECnet = true; if (SAME2(pptr,'\"~') || SAME2(pptr,'\"$')) *sptr++ = *pptr++; if (sptr < zptr) *sptr++ = *pptr++; continue; } if (SAME2(pptr,'::')) { /* DECnet (with or without access string) */ DECnet = true; *sptr++ = *pptr++; if (sptr < zptr) *sptr++ = *pptr++; continue; } if (DECnet && *pptr == ' ') { *sptr++ = *pptr++; continue; } } /* any other character substitute a question mark */ if (*pptr == '.') FinalPeriodPtr = sptr; if (sptr < zptr) *sptr++ = '?'; pptr++; } if (sptr < zptr) *sptr++ = '\0'; ecnt++; if (WATCH_MODULE_DETAIL) WatchDataFormatted ("!UL !&Z\n", ecnt, eptr); } if (sptr >= zptr) { /* storage overflowed */ if (WATCH_MODULE_DETAIL) WatchThis (WATCHALL, WATCH_MOD_MAPURL, "OVERFLOW"); SET2(VmsPtr,'\0\0'); return (0); } *sptr = '\0'; if (FinalPeriodPtr) *FinalPeriodPtr = '.'; /* convert these null-terminated elements into a VMS file specification */ len = MapOdsElementsToVms (PathComponents, ecnt, VmsPtr, SizeOfVmsPtr, MapEllipsis); /* convert the non-RMS characters into the RMS substitution character */ if (!RmsSubChar) RmsSubChar = MAPURL_RMS_SUBSTITUTION_DEFAULT; for (cptr = VmsPtr; *cptr; cptr++) { if (*cptr != '?') continue; *cptr = RmsSubChar; } if (WATCH_MODULE(WATCH_MOD_MAPURL)) WatchDataFormatted ("!UL !&Z\n", len, VmsPtr); return (len); } /*****************************************************************************/ /* Convert a URL-style specification into an ODS-5 RMS-style specification, example: "/disk/dir1/dir2/file.tar.gz" into "disk:[dir1.dir2]file^.tar.gz". For ODS-5 paths forbidden characters are ^ (character or hexadecimal) escaped and the case not altered. Refer to the EFS sections of the RMS documentaion. Returns the length of the VMS specification. There is an ambiguous specification that cannot be resolved algorithmically. The file specifications TESTING.TXT and TESTING^.TXT will both be presented to the client as TESTING.TXT but stored in the file system as TESTING.TXT; and TESTING^.TXT.; This function always assumes that TESTING^.TXT.; do not exist. */ #ifdef ODS_EXTENDED int MapOdsUrlToOds5Vms ( char *PathPtr, char *VmsPtr, int SizeOfVmsPtr, BOOL MapEllipsis ) { static char HexDigits [] = "0123456789ABCDEF"; BOOL DECnet; int ecnt, len; char *cptr, *eptr, *pptr, *sptr, *zptr, *FinalPeriodPtr, *FinalSemiColonPtr, *FinalSlashPtr; char PathComponents [ODS_MAX_FILE_NAME_LENGTH]; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_MAPURL)) WatchThis (WATCHALL, WATCH_MOD_MAPURL, "MapOdsUrlToOds5Vms() !&B !&Z !UL", MapEllipsis, PathPtr, SizeOfVmsPtr); pptr = PathPtr; if (!pptr || !*pptr || SAME2(pptr,'/\0')) { SET2(VmsPtr,'\0\0'); return (0); } DECnet = false; ecnt = 0; FinalPeriodPtr = FinalSemiColonPtr = FinalSlashPtr = NULL; zptr = (sptr = PathComponents) + sizeof(PathComponents); /* each '/' component becomes a null-terminated string */ while (*pptr && sptr < zptr) { while (*pptr == '/') pptr++; eptr = sptr; while (*pptr && *pptr != '/' && sptr < zptr) { switch (*pptr) { case '!' : case '#' : case '&' : case '\'' : case '(' : case ')' : case '+' : case '\"' : case '@' : case '{' : case '}' : case ',' : case ';' : case '[' : case ']' : case '=' : case '`' : case '|' : case '%' : case '^' : if (*pptr == ';') FinalSemiColonPtr = sptr; *sptr++ = '^'; if (sptr < zptr) *sptr++ = *pptr++; break; case ' ' : *sptr++ = '^'; if (sptr < zptr) *sptr++ = '_'; pptr++; break; case '.' : if (SAME3(pptr,'...')) { /* ellipsis wildcard */ *sptr++ = *pptr++; if (sptr < zptr) *sptr++ = *pptr++; if (sptr < zptr) *sptr++ = *pptr++; } else { FinalPeriodPtr = sptr; *sptr++ = '^'; if (sptr < zptr) *sptr++ = *pptr++; } break; default : if (*pptr == 0x07f || *pptr == 0x0a0 || *pptr == 0x0ff || (*pptr >= 0x80 && *pptr <= 0x9f)) { if (sptr+3 < zptr) { *sptr++ = '^'; *sptr++ = HexDigits[(*pptr & 0xf0) >> 4]; *sptr++ = HexDigits[*pptr++ & 0x0f]; } else sptr = zptr; } else *sptr++ = *pptr++; } if (!ecnt) { /* DECnet only valid in first component */ if (*pptr == '\"' || *pptr == '=') { /* DECnet access string (allow otherwise forbidden chars) */ DECnet = true; if (SAME2(pptr,'\"~') || SAME2(pptr,'\"$')) *sptr++ = *pptr++; if (sptr < zptr) *sptr++ = *pptr++; continue; } if (SAME2(pptr,'::')) { /* DECnet (with or without access string) */ DECnet = true; *sptr++ = *pptr++; if (sptr < zptr) *sptr++ = *pptr++; continue; } if (DECnet && *pptr == ' ') { *sptr++ = *pptr++; continue; } } /* any other character substitute a question mark */ } if (*pptr == '/') FinalSlashPtr = sptr; if (sptr < zptr) *sptr++ = '\0'; ecnt++; if (WATCH_MODULE_DETAIL) WatchDataFormatted ("!UL !&Z\n", ecnt, eptr); } if (sptr >= zptr) { /* storage overflowed */ if (WATCH_MODULE_DETAIL) WatchThis (WATCHALL, WATCH_MOD_MAPURL, "OVERFLOW"); SET2(VmsPtr,'\0\0'); return (0); } *sptr = '\0'; if (FinalSemiColonPtr && (FinalSemiColonPtr > FinalSlashPtr || !FinalSlashPtr) && (FinalSemiColonPtr > FinalPeriodPtr || !FinalPeriodPtr) && sptr < zptr) { /* a final semicolon delimitting the version must not be escaped */ cptr = (sptr = FinalSemiColonPtr) + 1; while (*cptr) *sptr++ = *cptr++; *sptr = '\0'; } if (FinalPeriodPtr && (FinalPeriodPtr > FinalSlashPtr || !FinalSlashPtr) && sptr < zptr) { /* a final period delimitting the type must not be escaped */ cptr = (sptr = FinalPeriodPtr) + 1; while (*cptr) *sptr++ = *cptr++; *sptr = '\0'; } len = MapOdsElementsToVms (PathComponents, ecnt, VmsPtr, SizeOfVmsPtr, MapEllipsis); if (WATCH_MODULE(WATCH_MOD_MAPURL)) WatchDataFormatted ("!UL !&Z\n", len, VmsPtr); return (len); } #endif /* ODS_EXTENDED */ /*****************************************************************************/ /* Convert a URL-style specification into a RMS-style specification, example: "/disk/dir1/dir2/file.tar.gz" into "disk:[dir1.dir2]file__27tar.gz" - with Advanced Server / Samba character conversion. If a character is not RMS legal (excluding the dollar which is the escape) then encode it as a double-underscore-escaped hexadecimal number. Something like this ... 1) If an ODS-2 forbidden character replace with '__xx' (i.e. two underscores plus the hex representation of the character). 2) Successive underscores have the first encoded as '__xx'. 3) If the final period occurs within the first 39 characters then it's not encoded, it becomes the ODS-2 period. If it occurs after character 39 then it's left encoded and a (required ODS-2 but name-extraneous) period is inserted at character 40. 4) Case change is indicated with a leading '__$' (begins as lower). Seems cumbersome, less versatile and less space-conservative than SRI!! For the life of me, why didn't they use SRI encoding? Returns the length of the VMS specification. */ int MapOdsUrlToAdsVms ( char *PathPtr, char *VmsPtr, int SizeOfVmsPtr, BOOL MapEllipsis ) { static char HexDigits [] = "0123456789ABCDEF"; BOOL inDevice, loCase; int ecnt, len; char *cptr, *eptr, *pptr, *sptr, *zptr, *FinalPeriodPtr, *FinalSlashPtr; char PathComponents [ODS_MAX_FILE_NAME_LENGTH]; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_MAPURL)) WatchThis (WATCHALL, WATCH_MOD_MAPURL, "MapOdsUrlToAdsVms() !&B !UL !&Z", MapEllipsis, SizeOfVmsPtr, PathPtr); pptr = PathPtr; if (!pptr || !*pptr || SAME2(pptr,'/\0')) { SET2(VmsPtr,'\0\0'); return (0); } /* any 'device' will not have any encoded characters in it's name */ for (cptr = pptr; *cptr == '/'; cptr++); while (*cptr && *cptr != '/') cptr++; if (*cptr) inDevice = true; else inDevice = false; FinalPeriodPtr = FinalSlashPtr = NULL; ecnt = 0; /* leave an extra char elbow room for inserting a period - see below */ zptr = (sptr = PathComponents) + sizeof(PathComponents)-1; eptr = sptr; /* each component becomes a null-terminated string in a character array */ while (*pptr && sptr < zptr) { while (*pptr == '/') pptr++; FinalPeriodPtr = NULL; loCase = true; eptr = sptr; while (*pptr && *pptr != '/' && sptr < zptr) { if (isalnum (*pptr) || *pptr == '-' || *pptr == '$' || *pptr == '*' || *pptr == '%' || inDevice) { if (loCase && isupper(*pptr)) { loCase = false; *sptr++ = '_'; if (sptr < zptr) *sptr++ = '_'; if (sptr < zptr) *sptr++ = '$'; if (sptr < zptr) *sptr++ = TOUP(*pptr++); continue; } if (!loCase && islower(*pptr)) { loCase = true; *sptr++ = '_'; if (sptr < zptr) *sptr++ = '_'; if (sptr < zptr) *sptr++ = '$'; if (sptr < zptr) *sptr++ = TOUP(*pptr++); continue; } *sptr++ = TOUP(*pptr++); continue; } if (*pptr == '_' && !SAME2(pptr,'__')) { /* single underscore */ *sptr++ = *pptr++; continue; } if (*pptr == '.') FinalPeriodPtr = sptr; *sptr++ = '_'; if (sptr < zptr) *sptr++ = '_'; if (sptr < zptr) *sptr++ = HexDigits[(*pptr & 0xf0) >> 4]; if (sptr < zptr) *sptr++ = HexDigits[*pptr & 0x0f]; pptr++; } if (sptr < zptr) *sptr++ = '\0'; ecnt++; inDevice = false; if (*pptr == '/') FinalSlashPtr = sptr; if (WATCH_MODULE_DETAIL) WatchDataFormatted ("!UL !&Z\n", ecnt, eptr); } if (sptr >= zptr) { /* storage overflowed */ if (WATCH_MODULE_DETAIL) WatchThis (WATCHALL, WATCH_MOD_MAPURL, "OVERFLOW"); SET2(VmsPtr,'\0\0'); return (0); } *sptr = '\0'; if (FinalPeriodPtr && FinalPeriodPtr <= eptr + 39) { /* less than required ODS-2 period, replace with 'real' period */ sptr = FinalPeriodPtr; *sptr++ = '.'; cptr = sptr + 3; while (*cptr) *sptr++ = *cptr++; *sptr = '\0'; } else if (sptr > eptr + 39) { /* greater than required ODS-2 period, insert an ODS-2 period */ cptr = sptr++; while (cptr > eptr+39) *sptr-- = *cptr--; *sptr = *cptr; *cptr = '.'; while (*sptr) sptr++; } len = MapOdsElementsToVms (PathComponents, ecnt, VmsPtr, SizeOfVmsPtr, MapEllipsis); if (WATCH_MODULE(WATCH_MOD_MAPURL)) WatchDataFormatted ("!UL !&Z\n", len, VmsPtr); return (len); } /*****************************************************************************/ /* Convert a URL-style specification into a RMS-style specification, example: "/disk/dir1/dir2/file.tar.gz" into "disk:[dir1.dir2]file$27tar.gz" - with PATHWORKS v4/5 character conversion. This is much simpler than SRI. If a character is not RMS legal (excluding the dollar which is the escape) then encode it as a dollar-escaped hexadecimal number. Returns the length of the VMS specification. */ int MapOdsUrlToPwkVms ( char *PathPtr, char *VmsPtr, int SizeOfVmsPtr, BOOL MapEllipsis ) { static char HexDigits [] = "0123456789ABCDEF"; BOOL inDevice; int ecnt, len; char *cptr, *eptr, *pptr, *sptr, *zptr, *FinalPeriodPtr, *FinalSlashPtr; char PathComponents [ODS_MAX_FILE_NAME_LENGTH]; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_MAPURL)) WatchThis (WATCHALL, WATCH_MOD_MAPURL, "MapOdsUrlToPwkVms() !&B !UL !&Z", MapEllipsis, SizeOfVmsPtr, PathPtr); pptr = PathPtr; if (!pptr || !*pptr || SAME2(pptr,'/\0')) { SET2(VmsPtr,'\0\0'); return (0); } /* any 'device' will not have any encoded characters in it's name */ for (cptr = pptr; *cptr == '/'; cptr++); while (*cptr && *cptr != '/') cptr++; if (*cptr) inDevice = true; else inDevice = false; FinalPeriodPtr = FinalSlashPtr = NULL; ecnt = 0; zptr = (sptr = PathComponents) + sizeof(PathComponents); /* each component becomes a null-terminated string in a character array */ while (*pptr && sptr < zptr) { while (*pptr == '/') pptr++; eptr = sptr; while (*pptr && *pptr != '/' && sptr < zptr) { if (isalnum (*pptr) || *pptr == '-' || *pptr == '_' || *pptr == '*' || *pptr == '%' || inDevice) { *sptr++ = *pptr++; continue; } if (*pptr == '.') FinalPeriodPtr = sptr; *sptr++ = '$'; if (sptr < zptr) *sptr++ = HexDigits[(*pptr & 0xf0) >> 4]; if (sptr < zptr) *sptr++ = HexDigits[*pptr & 0x0f]; pptr++; } if (sptr < zptr) *sptr++ = '\0'; ecnt++; inDevice = false; if (*pptr == '/') FinalSlashPtr = sptr; if (WATCH_MODULE_DETAIL) WatchDataFormatted ("!UL !&Z\n", ecnt, eptr); } if (sptr >= zptr) { /* storage overflowed */ if (WATCH_MODULE_DETAIL) WatchThis (WATCHALL, WATCH_MOD_MAPURL, "OVERFLOW"); SET2(VmsPtr,'\0\0'); return (0); } *sptr = '\0'; /* turn the final period back into one */ if (FinalPeriodPtr) { sptr = FinalPeriodPtr; *sptr++ = '.'; for (cptr = sptr + 2; *cptr; *sptr++ = *cptr++); *sptr = '\0'; } len = MapOdsElementsToVms (PathComponents, ecnt, VmsPtr, SizeOfVmsPtr, MapEllipsis); if (WATCH_MODULE(WATCH_MOD_MAPURL)) WatchDataFormatted ("!UL !&Z\n", len, VmsPtr); return (len); } /*****************************************************************************/ /* Convert a URL-style specification into a RMS-style specification, example: "/disk/dir1/dir2/file.tar.gz" into "disk:[dir1.dir2]file.tar$27gz" - with SRI character conversion. The SRI scheme is quite complex. Refer to online documentation associated with Process Software's MultiNet NFS server. Alphabetic '$' case flags reset on a per-path-component basis. Returns the length of the VMS specification. */ int MapOdsUrlToSriVms ( char *PathPtr, char *VmsPtr, int SizeOfVmsPtr, BOOL MapEllipsis ) { /* directory wildcard concessions; '%' should be "$5E" and '*' be "$5J" */ static char *SriConvTable [] = { "$6A", "$4A", "$4B", "$4C", "$4D", "$4E", "$4F", "$4G", /* 0x07 */ "$4H", "$4I", "$4J", "$4K", "$4L", "$4M", "$4N", "$4O", /* 0x0f */ "$4P", "$4Q", "$4R", "$4S", "$4T", "$4U", "$4V", "$4W", /* 0x17 */ "$4X", "$4Y", "$4Z", "$6B", "$6C", "$6D", "$6E", "$6F", /* 0x1f */ "$7A", "$5A", "$5B", "$5C", "$$", NULL, "$5F", "$5G", /* 0x27 */ "$5H", "$5I", NULL, "$5K", "$5L", NULL, "$5N", "$5O", /* 0x2f */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 0x37 */ NULL, NULL, "$5Z", "$7B", "$7C", "$7D", "$7E", "$7F", /* 0x3f */ "$8A", NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 0x47 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 0x4f */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 0x57 */ NULL, NULL, NULL, "$8B", "$8C", "$8D", "$8E", NULL, /* 0x5f */ "$9A", NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 0x67 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 0x6f */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 0x77 */ NULL, NULL, NULL, "$9B", "$9C", "$9D", "$9E", "$9F", /* 0x7f */ "$200", "$201", "$202", "$203", "$204", "$205", "$206", "$207", /* 0x87 */ "$210", "$211", "$212", "$213", "$214", "$215", "$216", "$217", /* 0x8f */ "$220", "$221", "$222", "$223", "$224", "$225", "$226", "$227", /* 0x97 */ "$230", "$231", "$232", "$233", "$234", "$235", "$236", "$237", /* 0x9f */ "$240", "$241", "$242", "$243", "$244", "$245", "$246", "$247", /* 0xa7 */ "$250", "$251", "$252", "$253", "$254", "$255", "$256", "$257", /* 0xaf */ "$260", "$261", "$262", "$263", "$264", "$265", "$266", "$267", /* 0xb7 */ "$270", "$271", "$272", "$273", "$274", "$275", "$276", "$277", /* 0xbf */ "$300", "$301", "$302", "$303", "$304", "$305", "$306", "$307", /* 0xc7 */ "$310", "$311", "$312", "$313", "$314", "$315", "$316", "$317", /* 0xcf */ "$320", "$321", "$322", "$323", "$324", "$325", "$326", "$327", /* 0xd7 */ "$330", "$331", "$332", "$333", "$334", "$335", "$336", "$337", /* 0xdf */ "$340", "$341", "$342", "$343", "$344", "$345", "$346", "$347", /* 0xe7 */ "$350", "$351", "$352", "$353", "$354", "$355", "$356", "$357", /* 0xef */ "$360", "$361", "$362", "$363", "$364", "$365", "$366", "$367", /* 0xf7 */ "$370", "$371", "$372", "$373", "$374", "$375", "$376", "$377", /* 0xff */ }; BOOL inDevice, loCase, typeHit; int ecnt, len, pcnt; char *cptr, *eptr, *pptr, *sptr, *zptr, *FinalSlashPtr; char PathComponents [ODS_MAX_FILE_NAME_LENGTH]; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_MAPURL)) WatchThis (WATCHALL, WATCH_MOD_MAPURL, "MapOdsUrlToSriVms() !&B !UL !&Z", MapEllipsis, SizeOfVmsPtr, PathPtr); pptr = PathPtr; if (!pptr || !*pptr || SAME2(pptr,'/\0')) { SET2(VmsPtr,'\0\0'); return (0); } /* any 'device' will not have any encoded characters in it's name */ for (cptr = pptr; *cptr == '/'; cptr++); while (*cptr && *cptr != '/') cptr++; if (*cptr) inDevice = true; else inDevice = false; ecnt = pcnt = 0; FinalSlashPtr = NULL; typeHit = false; zptr = (sptr = PathComponents) + sizeof(PathComponents); /* each component becomes a null-terminated string in a character array */ while (*pptr && sptr < zptr) { while (*pptr == '/') pptr++; loCase = true; eptr = sptr; while (*pptr && *pptr != '/' && sptr < zptr) { if (*pptr == '.' && !typeHit) { /* is it the final period in the specification? */ for (cptr = pptr; *cptr && *cptr != '/'; cptr++); if (!*cptr) { /* yes! */ for (cptr = pptr; *cptr && *cptr != '.'; cptr++); if (*cptr) { typeHit = true; loCase = true; } } } if (!SriConvTable[*(unsigned char*)pptr] || inDevice) { /* unencoded alpha-numeric (or other legal) character */ if (loCase && isupper(*pptr)) { loCase = false; *sptr++ = '$'; if (sptr < zptr) *sptr++ = TOUP(*pptr++); continue; } if (!loCase && islower(*pptr)) { loCase = true; *sptr++ = '$'; if (sptr < zptr) *sptr++ = TOUP(*pptr++); continue; } *sptr++ = TOUP(*pptr++); continue; } /* dollar-encoded character */ cptr = SriConvTable[*(unsigned char*)pptr]; while (*cptr && sptr < zptr) *sptr++ = *cptr++; pptr++; } if (sptr < zptr) *sptr++ = '\0'; ecnt++; inDevice = false; if (*pptr == '/') FinalSlashPtr = sptr; if (WATCH_MODULE_DETAIL) WatchDataFormatted ("!UL !&Z\n", ecnt, eptr); } if (sptr >= zptr) { /* storage overflowed */ if (WATCH_MODULE_DETAIL) WatchThis (WATCHALL, WATCH_MOD_MAPURL, "OVERFLOW"); SET2(VmsPtr,'\0\0'); return (0); } *sptr = '\0'; /* turn the first (if any) '$5N' of the file name back into a period */ if (!(sptr = FinalSlashPtr)) sptr = PathComponents; while (*sptr) { if (*sptr++ != '$') continue; if (!SAME2(sptr,'5N')) continue; sptr[-1] = '.'; for (cptr = sptr + 2; *cptr; *sptr++ = *cptr++); *sptr = '\0'; break; } len = MapOdsElementsToVms (PathComponents, ecnt, VmsPtr, SizeOfVmsPtr, MapEllipsis); if (WATCH_MODULE(WATCH_MOD_MAPURL)) WatchDataFormatted ("!UL !&Z\n", len, VmsPtr); return (len); } /*****************************************************************************/ /* 'ElementsPtr' contains a series of null-delimited strings (the '/' path components) terminated by a empty string. 'ElementsCount' the number of '/'-delimited parts of the string, including the "file" element - even if empty. Turn this into a VMS file specification in storage pointed to by 'VmsPtr'. For empty elements (consecutive '/') substitute a forbidden character that will render the specification useless. For parent directory syntax do the same thing. Returns the length of the VMS specification. */ int MapOdsElementsToVms ( char *ElementsPtr, int ElementsCount, char *VmsPtr, int SizeOfVmsPtr, BOOL MapEllipsis ) { /* character inserted into a file specification to render it useless */ #define BSCHAR '|' BOOL DECnet; int ecnt; char *cptr, *sptr, *tptr, *zptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_MAPURL)) WatchThis (WATCHALL, WATCH_MOD_MAPURL, "MapOdsElementsToVms() !UL !&B", ElementsCount, MapEllipsis); /* check the first element for DECnet syntax */ DECnet = false; for (cptr = ElementsPtr; *cptr; cptr++) { if (!SAME2(cptr,'::')) continue; DECnet = true; if (WATCH_MODULE(WATCH_MOD_MAPURL)) WatchThis (WATCHALL, WATCH_MOD_MAPURL, "DECNET"); break; } cptr = ElementsPtr; zptr = (sptr = VmsPtr) + SizeOfVmsPtr; if (DECnet) ElementsCount--; if (ElementsCount == 1) { /********************/ /* single component */ /********************/ if (*cptr) while (*cptr && sptr < zptr) *sptr++ = *cptr++; else if (sptr < zptr) *sptr++ = BSCHAR; } else if (ElementsCount == 2) { /***********************/ /* top-level directory */ /***********************/ if (DECnet) { /* copy first component as-is */ while (*cptr && sptr < zptr) *sptr++ = *cptr++; cptr++; } /* start with the first component, the "device" */ if (*cptr) while (*cptr && sptr < zptr) *sptr++ = *cptr++; else if (sptr < zptr) *sptr++ = BSCHAR; cptr++; /* append a Master File Directory component */ for (tptr = ":[000000]"; *tptr && sptr < zptr; *sptr++ = *tptr++); /* add the second component, the "file" (which can be empty) */ while (*cptr && sptr < zptr) *sptr++ = *cptr++; } else if (ElementsCount >= 3) { /****************/ /* subdirectory */ /****************/ if (DECnet) { /* copy first component as-is */ while (*cptr && sptr < zptr) *sptr++ = *cptr++; cptr++; } /* start with the first component, the "device" */ if (*cptr) while (*cptr && sptr < zptr) *sptr++ = *cptr++; else if (sptr < zptr) *sptr++ = BSCHAR; cptr++; /* avoid accidentally creating a DECnet ("::") specification */ if (*(sptr-1) != ':' && sptr < zptr) *sptr++ = ':'; if (sptr < zptr) *sptr++ = '['; if (cptr[0] == '.' && cptr[1] == '.' && cptr[2] == '.' && MapEllipsis) for (tptr = "000000"; *tptr && sptr < zptr; *sptr++ = *tptr++); for (ecnt = 2; ecnt < ElementsCount; ecnt++) { /* add the next component, a "directory" */ if (!*cptr && ecnt < ElementsCount) { /* empty element (on all but any file component) */ if (sptr < zptr) *sptr++ = BSCHAR; } else if (cptr[0] == '-') { /* check for parent directory syntax, squash if it is! */ for (tptr = cptr; *tptr == '-' || *tptr == '.'; tptr++); if (*tptr) { /* NOT parent directory syntax */ if (*(sptr-1) != '[' && sptr < zptr) *sptr++ = '.'; while (*cptr && sptr < zptr) *sptr++ = *cptr++; } else { /* parent directory syntax */ while (*cptr && sptr < zptr) { *sptr++ = BSCHAR; cptr++; } } } else { if (*(sptr-1) != '[' && *cptr != '.' && sptr < zptr) *sptr++ = '.'; while (*cptr && sptr < zptr) { if (*cptr != '.') { *sptr++ = *cptr++; continue; } if (!SAME2(cptr+1,'..') || MapEllipsis) { *sptr++ = *cptr++; continue; } /* excise the ellipsis wildcard */ cptr += 3; if (sptr < zptr) *sptr++ = BSCHAR; if (sptr < zptr) *sptr++ = BSCHAR; if (sptr < zptr) *sptr++ = BSCHAR; } } cptr++; } if (*(sptr-1) == '[') { /* the entire directory structure has been excised! */ for (tptr = "000000"; *tptr && sptr < zptr; *sptr++ = *tptr++); } if (sptr < zptr) *sptr++ = ']'; /* add the last component, the "file" (which can be empty) */ while (*cptr && sptr < zptr) *sptr++ = *cptr++; } if (sptr < zptr) *sptr = '\0'; if (sptr >= zptr) { /* storage overflowed */ SET2(VmsPtr,'\0\0'); return (0); } if (WATCH_MODULE_DETAIL) WatchDataFormatted ("!&B !UL !&Z\n", DECnet, sptr-VmsPtr, VmsPtr); return (sptr - VmsPtr); #undef BSCHAR } /*****************************************************************************/ /* Convert a VMS full file specification into a URL-style specification, example: "DEVICE:[DIR1.DIR2]FILE.TXT" into "/device/dir1/dir2/file.txt", or just a file name and type portion (ensuring it's correctly converted and escaped). The URL has URL forbidden characters URL-encoded. Can process ODS-2, ODS-5 (EFS), SRI (MultiNet NFS), PATHWORKS (v4/5) and Advanced Server (PATHWORKS V6) / Samba encodings. Returns the length of the generated URL-style path. */ int MapOdsVmsToUrl ( char *PathPtr, char *VmsPtr, int SizeOfPathPtr, BOOL AbsorbMfd, int PathOds ) { int len; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_MAPURL)) WatchThis (WATCHALL, WATCH_MOD_MAPURL, "MapOdsVmsToUrl() !UL !&B !&Z !UL", PathOds, AbsorbMfd, VmsPtr, SizeOfPathPtr); switch (PathOds) { case 0 : case MAPURL_PATH_ODS_2 : len = MapOds2VmsToUrl (PathPtr, VmsPtr, SizeOfPathPtr, AbsorbMfd); break; #ifdef ODS_EXTENDED case MAPURL_PATH_ODS_5 : len = MapOds5VmsToUrl (PathPtr, VmsPtr, SizeOfPathPtr, AbsorbMfd); break; #endif /* ODS_EXTENDED */ case MAPURL_PATH_ODS_ADS : case MAPURL_PATH_ODS_SMB : len = MapOdsAdsVmsToUrl (PathPtr, VmsPtr, SizeOfPathPtr, AbsorbMfd); break; case MAPURL_PATH_ODS_PWK : len = MapOdsPwkVmsToUrl (PathPtr, VmsPtr, SizeOfPathPtr, AbsorbMfd); break; case MAPURL_PATH_ODS_SRI : len = MapOdsSriVmsToUrl (PathPtr, VmsPtr, SizeOfPathPtr, AbsorbMfd); break; default : memcpy (PathPtr, "/sanity/check/MapOdsUrlToVms", len=30); } if (WATCH_MODULE(WATCH_MOD_MAPURL)) WatchDataFormatted ("!UL !&Z\n", len, PathPtr); return (len); } /*****************************************************************************/ /* Convert a VMS full file specification into a URL-style specification, example: "DEVICE:[DIR1.DIR2]FILE.TXT" into "/device/dir1/dir2/file.txt", or just a file name and type portion (ensuring it's correctly converted and escaped). Expects file specification to be ODS-2 compliant and forces the URL to all lower-case. Returns the length of the generated URL-style path. */ int MapOds2VmsToUrl ( char *PathPtr, char *VmsPtr, int SizeOfPathPtr, BOOL AbsorbMfd ) { char *cptr, *eptr, *sptr, *zptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_MAPURL)) WatchThis (WATCHALL, WATCH_MOD_MAPURL, "MapOds2VmsToUrl() !&B !&Z !UL", AbsorbMfd, VmsPtr, SizeOfPathPtr); zptr = (sptr = PathPtr) + SizeOfPathPtr; /* look for indications it's a full file specification */ cptr = VmsPtr; while (*cptr && !SAME2(cptr,':[') && *cptr != ']' && *cptr != '/') cptr++; if (!*cptr || *cptr == '/') { /* no VMS device/directory components detected */ cptr = VmsPtr; } else { /* copy device/directory components */ cptr = VmsPtr; if (sptr < zptr) *sptr++ = '/'; while (*cptr && sptr < zptr) { if (SAME2(cptr,'::')) { /* DECnet */ *sptr++ = *cptr++; if (sptr < zptr) *sptr++ = *cptr++; if (sptr < zptr) *sptr++ = '/'; continue; } if (SAME2(cptr,':[')) { cptr++; cptr++; /* remove any reference to a Master File Directory */ if (AbsorbMfd && (MATCH7(cptr,"000000]") || MATCH7(cptr,"000000."))) cptr += 6; else *sptr++ = '/'; continue; } if (*cptr == '.') { if (SAME3(cptr,'...')) { if (sptr < zptr) *sptr++ = *cptr++; if (sptr < zptr) *sptr++ = *cptr++; if (sptr < zptr) *sptr++ = *cptr++; } else { *sptr++ = '/'; if (SAME3(cptr,'.][')) cptr += 3; else cptr++; } continue; } if (*cptr == ']') { *sptr++ = '/'; cptr++; break; } *sptr++ = TOLO(*cptr++); } } /* copy the file component */ while (*cptr && sptr < zptr) *sptr++ = TOLO(*cptr++); if (sptr < zptr) *sptr++ = '\0'; if (sptr < zptr) *sptr++ = '\0'; if (WATCH_MODULE(WATCH_MOD_MAPURL)) WatchDataFormatted ("!&Z\n", PathPtr); if (sptr < zptr) return ((sptr - PathPtr) - 2); /* storage overflowed */ if (WATCH_MODULE_DETAIL) WatchThis (WATCHALL, WATCH_MOD_MAPURL, "OVERFLOW"); SET2(PathPtr,'\0\0'); return (0); } /*****************************************************************************/ /* Convert a VMS full file ODS-5 compliant specification into a URL-style specification, example: "DEVICE:[dir1.DIR2]a^.file.txt" into "/device/dir1/DIR2/a file.txt", or a URL-style string that contains ODS-5 escaped characters, example "/device/device/dir1/DIR2/a^.file.txt" into "/device/dir1/DIR2/a file.txt" (ensuring it's correctly converted and escaped). Will decode literal ^-escaped and hexadecimal ^-escaped characters and leaves the case as-is. Returns the length of the generated URL-style path. */ #ifdef ODS_EXTENDED int MapOds5VmsToUrl ( char *PathPtr, char *VmsPtr, int SizeOfPathPtr, BOOL AbsorbMfd ) { unsigned char ch; char *cptr, *eptr, *sptr, *zptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_MAPURL)) WatchThis (WATCHALL, WATCH_MOD_MAPURL, "MapOds5VmsToUrl() !&B !&Z !UL", AbsorbMfd, VmsPtr, SizeOfPathPtr); zptr = (sptr = PathPtr) + SizeOfPathPtr; /* look for indications it's a full file specification */ cptr = VmsPtr; while (*cptr && !SAME2(cptr,':[') && *cptr != ']' && *cptr != '/') { if (cptr[0] == '^' && isxdigit(cptr[1]) && isxdigit(cptr[2])) cptr += 3; else if (cptr[0] == '^' && cptr[1] == 'U' && isxdigit(cptr[2]) && isxdigit(cptr[3]) && isxdigit(cptr[4]) && isxdigit(cptr[5])) cptr += 6; else if (cptr[0] == '^') cptr += 2; else cptr++; } if (!*cptr || *cptr == '/') { /* no VMS device/directory components detected */ cptr = VmsPtr; } else { /* copy device/directory components */ cptr = VmsPtr; if (sptr < zptr) *sptr++ = '/'; while (*cptr && sptr < zptr) { if (SAME2(cptr,'::')) { /* DECnet */ *sptr++ = *cptr++; if (sptr < zptr) *sptr++ = *cptr++; if (sptr < zptr) *sptr++ = '/'; continue; } if (SAME2(cptr,':[')) { cptr++; cptr++; /* remove any reference to a Master File Directory */ if (AbsorbMfd && (MATCH7(cptr,"000000]") || MATCH7(cptr,"000000."))) cptr += 6; else *sptr++ = '/'; continue; } if (*cptr == '.') { if (SAME3(cptr,'...')) { if (sptr < zptr) *sptr++ = *cptr++; if (sptr < zptr) *sptr++ = *cptr++; if (sptr < zptr) *sptr++ = *cptr++; } else { *sptr++ = '/'; if (SAME3(cptr,'.][')) cptr += 3; else cptr++; } continue; } if (*cptr == ']') { *sptr++ = '/'; cptr++; break; } if (*cptr == '^') { /* ODS-5 extended specification escape character */ cptr++; switch (*cptr) { case '!' : case '#' : case '&' : case '\'' : case '\"' : case '(' : case ')' : case '+' : case '@' : case '{' : case '}' : case '.' : case ',' : case ';' : case '[' : case ']' : case '%' : case '^' : case '=' : case ' ' : case '`' : case '|' : *sptr++ = *cptr++; break; case '_' : *sptr++ = ' '; cptr++; break; default : if (!isxdigit (*cptr)) { *sptr++ = *cptr++; break; } ch = 0; if (*cptr >= '0' && *cptr <= '9') ch = (*cptr - (int)'0') << 4; else if (TOLO(*cptr) >= 'a' && TOLO(*cptr) <= 'f') ch = (TOLO(*cptr) - (int)'a' + 10) << 4; if (*cptr) cptr++; if (*cptr >= '0' && *cptr <= '9') ch += (*cptr - (int)'0'); else if (TOLO(*cptr) >= 'a' && TOLO(*cptr) <= 'f') ch += (TOLO(*cptr) - (int)'a' + 10); if (*cptr) cptr++; *sptr++ = ch; } continue; } if (sptr < zptr) *sptr++ = *cptr++; } } /* copy the file component */ while (*cptr && sptr < zptr) { if (*cptr != '^') { *sptr++ = *cptr++; continue; } /* ODS-5 extended specification escape character */ cptr++; switch (*cptr) { case '!' : case '#' : case '&' : case '\'' : case '`' : case '(' : case ')' : case '+' : case '@' : case '{' : case '}' : case '.' : case ',' : case ';' : case '[' : case ']' : case '%' : case '^' : case '=' : case ' ' : *sptr++ = *cptr++; break; case '_' : *sptr++ = ' '; cptr++; break; default : if (!isxdigit (*cptr)) { *sptr++ = *cptr++; break; } ch = 0; if (*cptr >= '0' && *cptr <= '9') ch = (*cptr - (int)'0') << 4; else if (TOLO(*cptr) >= 'a' && TOLO(*cptr) <= 'f') ch = (TOLO(*cptr) - (int)'a' + 10) << 4; if (*cptr) cptr++; if (*cptr >= '0' && *cptr <= '9') ch += (*cptr - (int)'0'); else if (TOLO(*cptr) >= 'a' && TOLO(*cptr) <= 'f') ch += (TOLO(*cptr) - (int)'a' + 10); if (*cptr) cptr++; *sptr++ = ch; } } if (sptr < zptr) *sptr++ = '\0'; if (sptr < zptr) *sptr++ = '\0'; if (WATCH_MODULE(WATCH_MOD_MAPURL)) WatchDataFormatted ("!&Z\n", PathPtr); if (sptr < zptr) return ((sptr - PathPtr) - 2); /* storage overflowed */ if (WATCH_MODULE_DETAIL) WatchThis (WATCHALL, WATCH_MOD_MAPURL, "OVERFLOW"); SET2(PathPtr,'\0\0'); return (0); } #endif /* ODS_EXTENDED */ /*****************************************************************************/ /* Convert a VMS full file specification into a URL-style specification, example: "DEVICE:[DIR1.DIR2]__$A__2A__$FILE.TXT" into "/device/dir1/dir2/A file.txt", or a URL-style specification, example "/device/dir1/dir2/A__20file.txt" into "/device/dir1/dir2/A file.txt" (ensuring it's correctly converted and escaped). Expects file specification to be Advanced Server compliant. Alphabetic '$' case flags reset on a per-path-component basis. Returns the length of the generated URL-style path. */ int MapOdsAdsVmsToUrl ( char *PathPtr, char *VmsPtr, int SizeOfPathPtr, BOOL AbsorbMfd ) { BOOL isDevice, loCase; char ch; char *cptr, *eptr, *sptr, *zptr, *NamePtr, *PeriodPtr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_MAPURL)) WatchThis (WATCHALL, WATCH_MOD_MAPURL, "MapOdsAdsVmsToUrl() !&B !&Z !UL", AbsorbMfd, VmsPtr, SizeOfPathPtr); zptr = (sptr = PathPtr) + SizeOfPathPtr; /* look for indications it's a full file specification */ cptr = VmsPtr; while (*cptr && !SAME2(cptr,':[') && *cptr != ']' && *cptr != '/') cptr++; if (!*cptr || *cptr == '/') { /* no VMS device/directory components detected */ cptr = VmsPtr; } else { /* copy device/directory components */ isDevice = loCase = true; cptr = VmsPtr; if (sptr < zptr) *sptr++ = '/'; while (*cptr && sptr < zptr) { if (SAME2(cptr,'::')) { /* DECnet */ *sptr++ = *cptr++; if (sptr < zptr) *sptr++ = *cptr++; if (sptr < zptr) *sptr++ = '/'; loCase = true; continue; } if (!SAME2(cptr,':[')) { cptr++; cptr++; /* remove any reference to a Master File Directory */ if (AbsorbMfd && (MATCH7(cptr,"000000]") || MATCH7(cptr,"000000."))) cptr += 6; else *sptr++ = '/'; isDevice = false; loCase = true; continue; } if (*cptr == '.') { if (SAME3(cptr,'...')) { if (sptr < zptr) *sptr++ = *cptr++; if (sptr < zptr) *sptr++ = *cptr++; if (sptr < zptr) *sptr++ = *cptr++; } else { *sptr++ = '/'; if (SAME3(cptr,'.][')) cptr += 3; else cptr++; } loCase = true; continue; } if (*cptr == ']') { cptr++; *sptr++ = '/'; break; } /* don't need to be concerned with periods in directories */ if (!SAME2(cptr,'__') || isDevice) { if (loCase) *sptr++ = TOLO(*cptr++); else *sptr++ = TOUP(*cptr++); continue; } /* convert from Advanced Server encoding */ cptr++; cptr++; if (*cptr == '$') { cptr++; loCase = !loCase; continue; } ch = 0; if (*cptr >= '0' && *cptr <= '9') ch = (*cptr - (int)'0') << 4; else if (TOLO(*cptr) >= 'a' && TOLO(*cptr) <= 'f') ch = (TOLO(*cptr) - (int)'a' + 10) << 4; else ch = '?'; if (*cptr) cptr++; if (*cptr >= '0' && *cptr <= '9') ch += (*cptr - (int)'0'); else if (TOLO(*cptr) >= 'a' && TOLO(*cptr) <= 'f') ch += (TOLO(*cptr) - (int)'a' + 10); else ch = '?'; if (*cptr) cptr++; if (loCase) *sptr++ = TOLO(ch); else *sptr++ = TOUP(ch); } } /* copy the file component */ PeriodPtr = NULL; NamePtr = cptr; loCase = true; while (*cptr && sptr < zptr) { if (*cptr != '_') { if (*cptr != '.') { if (loCase) *sptr++ = TOLO(*cptr++); else *sptr++ = TOUP(*cptr++); continue; } PeriodPtr = sptr; *sptr++ = *cptr++; continue; } /* need to cater for possible '__xx', '_._xx', '__.xx' */ if (!SAME2(cptr,'__') && !SAME2(cptr,'_.')) { /* an isolated underscore */ if (sptr < zptr) *sptr++ = *cptr++; continue; } cptr++; if (SAME2(cptr,'._')) { if (cptr != NamePtr + 39) { /* not an ODS-2 required period */ if (sptr < zptr) *sptr++ = *cptr++; continue; } /* step over the ODS-2 required period */ cptr++; } else if (SAME2(cptr,'_.')) { /* after two underscores can only be an ODS-2 required period */ cptr++; } cptr++; /* convert from Advanced Server encoding */ if (*cptr == '$') { cptr++; loCase = !loCase; continue; } ch = 0; if (*cptr >= '0' && *cptr <= '9') ch = (*cptr - (int)'0') << 4; else if (TOLO(*cptr) >= 'a' && TOLO(*cptr) <= 'f') ch = (TOLO(*cptr) - (int)'a' + 10) << 4; else ch = '?'; if (*cptr) cptr++; /* if it's an ODS-2 required period just ignore it (i.e. '__x.x') */ if (*cptr == '.') cptr++; if (*cptr >= '0' && *cptr <= '9') ch += (*cptr - (int)'0'); else if (TOLO(*cptr) >= 'a' && TOLO(*cptr) <= 'f') ch += (TOLO(*cptr) - (int)'a' + 10); else ch = '?'; if (*cptr) cptr++; if (loCase) *sptr++ = TOLO(ch); else *sptr++ = TOUP(ch); } if (sptr < zptr) *sptr++ = '\0'; if (sptr < zptr) *sptr++ = '\0'; if (PeriodPtr) { /* remove the ODS-2 required period if there is a trailing period */ for (cptr = PeriodPtr+1; *cptr && *cptr != '.'; cptr++); if (*cptr) { for (cptr = (sptr = PeriodPtr) + 1; *cptr; *sptr++ = *cptr++); if (sptr < zptr) *sptr++ = '\0'; if (sptr < zptr) *sptr++ = '\0'; } } if (WATCH_MODULE(WATCH_MOD_MAPURL)) WatchDataFormatted ("!&Z\n", PathPtr); if (sptr < zptr) return ((sptr - PathPtr) - 2); /* storage overflowed */ if (WATCH_MODULE_DETAIL) WatchThis (WATCHALL, WATCH_MOD_MAPURL, "OVERFLOW"); SET2(PathPtr,'\0\0'); return (0); } /*****************************************************************************/ /* The Advanced Server (PATHWORKS 6) / Samba encoding maps the period as described in the description of MapOdsUrlToAdsVms(). This function decodes the file anem plus type then find the last period in it and uses that following as the file type. */ char* MapOdsAdsFileType (char *FileType) { static char AdsFileType [96]; int Length; char *cptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_MAPURL)) WatchThis (WATCHALL, WATCH_MOD_MAPURL, "MapOdsAdsFileType() !&Z", FileType); Length = MapOdsAdsVmsToUrl (AdsFileType, FileType, sizeof(AdsFileType), true); cptr = AdsFileType + Length; while (cptr > AdsFileType && *cptr != '.') cptr--; if (WATCH_MODULE(WATCH_MOD_MAPURL)) WatchDataFormatted ("!&Z\n", cptr); return (cptr); } /*****************************************************************************/ /* Convert a VMS full file specification into a URL-style specification, example: "DEVICE:[DIR1.DIR2]A$2AFILE.TXT" into "/device/dir1/dir2/a file.txt", or a URL-style specification, example "/device/dir1/dir2/a$20file.txt" into "/device/dir1/dir2/a file.txt" (ensuring it's correctly converted and escaped). Expects file specification to be PATHWORKS v4/5 compliant and forces the URL to all lower-case. Returns the length of the generated URL-style path. */ int MapOdsPwkVmsToUrl ( char *PathPtr, char *VmsPtr, int SizeOfPathPtr, BOOL AbsorbMfd ) { BOOL isDevice; char ch; char *cptr, *eptr, *sptr, *zptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_MAPURL)) WatchThis (WATCHALL, WATCH_MOD_MAPURL, "MapOdsPwkVmsToUrl() !&B !&Z !UL", AbsorbMfd, VmsPtr, SizeOfPathPtr); zptr = (sptr = PathPtr) + SizeOfPathPtr; /* look for indications it's a full file specification */ cptr = VmsPtr; while (*cptr && !SAME2(cptr,':[') && *cptr != ']' && *cptr != '/') cptr++; if (!*cptr || *cptr == '/') { /* no VMS device/directory components detected */ cptr = VmsPtr; } else { /* copy device/directory components */ isDevice = true; cptr = VmsPtr; if (sptr < zptr) *sptr++ = '/'; while (*cptr && sptr < zptr) { if (SAME2(cptr,'::')) { /* DECnet */ *sptr++ = *cptr++; if (sptr < zptr) *sptr++ = *cptr++; if (sptr < zptr) *sptr++ = '/'; continue; } if (SAME2(cptr,':[')) { cptr++; cptr++; /* remove any reference to a Master File Directory */ if (AbsorbMfd && (MATCH7(cptr,"000000]") || MATCH7(cptr,"000000."))) cptr += 6; else *sptr++ = '/'; isDevice = false; continue; } if (*cptr == '.') { *sptr++ = '/'; if (SAME3(cptr,'...')) { if (sptr < zptr) *sptr++ = *cptr++; if (sptr < zptr) *sptr++ = *cptr++; if (sptr < zptr) *sptr++ = *cptr++; } else { *sptr++ = '/'; if (SAME3(cptr,'.][')) cptr += 3; else cptr++; } continue; } if (*cptr == ']') { cptr++; *sptr++ = '/'; break; } if (*cptr != '$' || isDevice) { *sptr++ = TOLO(*cptr++); continue; } /* convert from PATHWORKS encoding */ cptr++; ch = 0; if (*cptr >= '0' && *cptr <= '9') ch = (*cptr - (int)'0') << 4; else if (TOLO(*cptr) >= 'a' && TOLO(*cptr) <= 'f') ch = (TOLO(*cptr) - (int)'a' + 10) << 4; else ch = '?'; if (*cptr) cptr++; if (*cptr >= '0' && *cptr <= '9') ch += (*cptr - (int)'0'); else if (TOLO(*cptr) >= 'a' && TOLO(*cptr) <= 'f') ch += (TOLO(*cptr) - (int)'a' + 10); else ch = '?'; if (*cptr) cptr++; *sptr++ = ch; } } /* copy the file component */ while (*cptr && sptr < zptr) { if (*cptr != '$') { *sptr++ = TOLO(*cptr++); continue; } /* convert from PATHWORKS encoding */ cptr++; ch = 0; if (*cptr >= '0' && *cptr <= '9') ch = (*cptr - (int)'0') << 4; else if (TOLO(*cptr) >= 'a' && TOLO(*cptr) <= 'f') ch = (TOLO(*cptr) - (int)'a' + 10) << 4; else ch = '?'; if (*cptr) cptr++; if (*cptr >= '0' && *cptr <= '9') ch += (*cptr - (int)'0'); else if (TOLO(*cptr) >= 'a' && TOLO(*cptr) <= 'f') ch += (TOLO(*cptr) - (int)'a' + 10); else ch = '?'; if (*cptr) cptr++; *sptr++ = ch; } if (sptr < zptr) *sptr++ = '\0'; if (sptr < zptr) *sptr++ = '\0'; if (WATCH_MODULE(WATCH_MOD_MAPURL)) WatchDataFormatted ("!&Z\n", PathPtr); if (sptr < zptr) return ((sptr - PathPtr) - 2); /* storage overflowed */ if (WATCH_MODULE_DETAIL) WatchThis (WATCHALL, WATCH_MOD_MAPURL, "OVERFLOW"); SET2(PathPtr,'\0\0'); return (0); } /*****************************************************************************/ /* Convert a VMS full file SRI (e.g. MultiNet NFS) compliant specification into a URL-style specification, example: "DEVICE:[DIR1.DIR2]A$5NFILE.TXT" into "/device/dir1/dir2/a file.txt", or a URL-style specification, example "/device/dir1/dir2/a$5nfile.txt" into "/device/dir1/dir2/a file.txt" (ensuring it's correctly converted and escaped). Expects file specification to be SRI compliant. The SRI scheme is quite complex. Refer to online documentation associated with Process Software's MultiNet NFS server. Alphabetic '$' case flags reset on a per-path-component basis. Returns the length of the generated URL-style path. */ int MapOdsSriVmsToUrl ( char *PathPtr, char *VmsPtr, int SizeOfPathPtr, BOOL AbsorbMfd ) { BOOL isDevice, loCase; char ch; char *cptr, *eptr, *sptr, *zptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_MAPURL)) WatchThis (WATCHALL, WATCH_MOD_MAPURL, "MapOdsSriVmsToUrl() !&B !&Z !UL", AbsorbMfd, VmsPtr, SizeOfPathPtr); zptr = (sptr = PathPtr) + SizeOfPathPtr; /* look for indications it's a full file specification */ cptr = VmsPtr; while (*cptr && !SAME2(cptr,':[') && *cptr != ']' && *cptr != '/') cptr++; if (!*cptr || *cptr == '/') { /* no VMS device/directory components detected */ cptr = VmsPtr; } else { /* copy device/directory components */ isDevice = loCase = true; cptr = VmsPtr; if (sptr < zptr) *sptr++ = '/'; while (*cptr && sptr < zptr) { if (SAME2(cptr,'::')) { /* DECnet */ *sptr++ = *cptr++; if (sptr < zptr) *sptr++ = *cptr++; if (sptr < zptr) *sptr++ = '/'; loCase = true; continue; } if (SAME2(cptr,':[')) { cptr++; cptr++; /* remove any reference to a Master File Directory */ if (AbsorbMfd && (MATCH7(cptr,"000000]") || MATCH7(cptr,"000000."))) cptr += 6; else *sptr++ = '/'; isDevice = false; loCase = true; continue; } if (*cptr == '.') { *sptr++ = '/'; if (SAME3(cptr,'...')) { if (sptr < zptr) *sptr++ = *cptr++; if (sptr < zptr) *sptr++ = *cptr++; if (sptr < zptr) *sptr++ = *cptr++; } else { *sptr++ = '/'; if (SAME3(cptr,'.][')) cptr += 3; else cptr++; } loCase = true; continue; } if (*cptr == ']') { *sptr++ = '/'; cptr++; break; } if (isDevice) *sptr++ = TOLO(*cptr++); else if (*cptr == '$') *sptr++ = MapOdsSriCharVmsToUrl (&cptr, &loCase); else if (loCase) *sptr++ = TOLO(*cptr++); else *sptr++ = TOUP(*cptr++); } } /* copy the file component */ loCase = true; while (*cptr && sptr < zptr) { if (*cptr == '.') loCase = true; if (*cptr == '$') *sptr++ = MapOdsSriCharVmsToUrl (&cptr, &loCase); else if (loCase) *sptr++ = TOLO(*cptr++); else *sptr++ = TOUP(*cptr++); } if (sptr < zptr) *sptr++ = '\0'; if (sptr < zptr) *sptr++ = '\0'; if (WATCH_MODULE(WATCH_MOD_MAPURL)) WatchDataFormatted ("!&Z\n", PathPtr); if (sptr < zptr) return ((sptr - PathPtr) - 2); /* storage overflowed */ if (WATCH_MODULE_DETAIL) WatchThis (WATCHALL, WATCH_MOD_MAPURL, "OVERFLOW"); SET2(PathPtr,'\0\0'); return (0); } /*****************************************************************************/ /* Return a single character that has been decoded from the string pointed to by the storage supplied by 'stringPtrPtr'. If the decode detects an error in the encoding then it returns a question mark (crude, but hey it should be obvious to the end-user!) The SRI scheme is quite complex. Refer to online documentation associated with Process Software's MultiNet NFS server. */ char MapOdsSriCharVmsToUrl ( char **stringPtrPtr, BOOL *loCasePtr ) { static char SriDecodeTable [6][26] = { /* table $4A-Z */ { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, /* A-H */ 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, /* I-P */ 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, /* Q-X */ 0x19, 0x1a }, /* Y-Z */ /* table $5A-Z */ { 0x21, 0x22, 0x23, '?', 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, '?', 0x2e, 0x2f, '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', 0x3a }, /* table $6A-Z */ { 0x00, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, '?', '?', 0x29, '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?' }, /* table $7A-Z */ { 0x20, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, '?', '?', 0x29, '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?' }, /* table $8A-Z */ { 0x40, 0x5b, 0x5c, 0x5d, 0x5e, '?', '?', '?', 0x29, '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?' }, /* table $9A-Z */ { 0x60, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, '?', '?', 0x29, '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?' }, }; char ch, idx; char *cptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_MAPURL)) WatchThis (WATCHALL, WATCH_MOD_MAPURL, "MapOdsSriCharVmsToUrl() !&B !4AZ", *loCasePtr, *stringPtrPtr); cptr = *stringPtrPtr; if (*cptr == '$') { cptr++; if (*cptr == '2' || *cptr == '3') { ch = (*cptr++ - '0') << 6; if (*cptr >= '0' && *cptr <= '7') { ch |= (*cptr++ - '0') << 3; if (*cptr >= '0' && *cptr <= '7') ch |= (*cptr++ -'0'); else { ch = '?'; cptr++; } } else { ch = '?'; cptr++; } } else if (*cptr >= '4' && *cptr <= '9') { idx = *cptr++ - '4'; if (*cptr >= 'A' && *cptr <= 'Z') ch = SriDecodeTable[idx][*(unsigned char*)cptr-'A']; else ch = '?'; cptr++; } else if (*cptr >= 'A' && *cptr <= 'Z') { *loCasePtr = !*loCasePtr; if (*loCasePtr) ch = TOLO(*cptr++); else ch = TOUP(*cptr++); } else if (*cptr == '$') ch = *cptr++; else { ch = '?'; cptr++; } } else if (*loCasePtr) ch = TOLO(*cptr++); else ch = TOUP(*cptr++); if (WATCH_MODULE_DETAIL) WatchDataFormatted ("{!UL}!-!#AZ 0x!2XL !-!&C\n", cptr - *stringPtrPtr, *stringPtrPtr, ch); *stringPtrPtr = cptr; return (ch); } /*****************************************************************************/ /* The SRI encoding maps the first period in any multi-period file type as a period and any thereafter as '$5N' sequences. Find any final '$5N' sequence and map this into a "real" file type returning a pointer to this static storage. If there is no '$5N' in the string then just return a pointer to the original string. */ char* MapOdsSriFileType (char *FileType) { static char SriFileType [96]; int Length; char *cptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_MAPURL)) WatchThis (WATCHALL, WATCH_MOD_MAPURL, "MapOdsSriFileType() !&Z", FileType); Length = MapOdsSriVmsToUrl (SriFileType, FileType, sizeof(SriFileType), true); cptr = SriFileType + Length; while (cptr > SriFileType && *cptr != '.') cptr--; if (WATCH_MODULE(WATCH_MOD_MAPURL)) WatchDataFormatted ("!&Z\n", cptr); return (cptr); } /*****************************************************************************/ /* Map something like ODS-2:[dir1.dir2]name.TYPE to /ODS-2/dir1/dir2/name.TYPE and ODS-5:[dir1.dir2]name^.dot.TYPE to /ODS-5/dir1/dir2/name.dot.TYPE suitable for use by the C-RTL. Returns a VMS status. */ int MapOdsVmsToUnix ( char *VmsFileSpec, char *UnixFileSpec, int SizeOfUnixFileSpec ) { char *cptr, *sptr, *zptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD_MAPURL)) WatchThis (WATCHALL, WATCH_MOD_MAPURL, "MapOdsVmsToUnix() !&Z !UL", VmsFileSpec, SizeOfUnixFileSpec); zptr = (sptr = UnixFileSpec) + SizeOfUnixFileSpec; if (!VmsFileSpec[0]) { *sptr = '\0'; return (SS$_NORMAL); } if (sptr < zptr) *sptr++ = '/'; cptr = VmsFileSpec; while (*cptr && *cptr != ':' && sptr < zptr) { if (*cptr == '^') cptr++; if (*cptr) *sptr++ = *cptr++; } if (*cptr == ':') cptr++; if (*cptr == '[') cptr++; if (sptr < zptr) *sptr++ = '/'; while (*cptr && *cptr != ']' && sptr < zptr) { while (*cptr && *cptr != '.' && *cptr != ']' && sptr < zptr) { if (*cptr == '^') cptr++; if (*cptr) *sptr++ = *cptr++; } if (*cptr == '.') { if (sptr < zptr) *sptr++ = '/'; cptr++; } } if (*cptr == ']') cptr++; if (sptr < zptr) *sptr++ = '/'; while (*cptr && sptr < zptr) { if (*cptr == '^') cptr++; if (*cptr) *sptr++ = *cptr++; } if (sptr >= zptr) return (SS$_RESULTOVF); *sptr = '\0'; return (SS$_NORMAL); } /*****************************************************************************/