From 0d053dca098a1567e8909d31a72de9dcda6bb071 Mon Sep 17 00:00:00 2001 From: Andy McFadden Date: Sat, 8 Feb 2003 22:35:31 +0000 Subject: [PATCH] Upped version number to v2.0.0. Fixed filename conversion issues. Specifically: - Correctly handle '%' when preservation is OFF. - Accept 4-character extensions in '-ee' without risk of buffer overflow. - Fixed broken assert when converting long %xx names. - Store "AUX" as "%00AUX" when preserving names under Win32 (vs. "_AUX"). - Always store files with ':' as path separator. - Recognize that some Win32 variants (Win2K and later at the least) will accept both '/' and '\' as pathname separators. - Correctly convert ".//foo" to "foo" instead of "/foo". Corrected definition of F_OK under Win32. --- nulib2/Filename.c | 15 +++--- nulib2/Nulib2.h | 3 ++ nulib2/State.c | 16 ++++++- nulib2/State.h | 2 + nulib2/SysDefs.h | 2 +- nulib2/SysUtils.c | 119 +++++++++++++++++++++++++++++++++++++--------- 6 files changed, 125 insertions(+), 32 deletions(-) diff --git a/nulib2/Filename.c b/nulib2/Filename.c index 3c5f3f9..b7ac765 100644 --- a/nulib2/Filename.c +++ b/nulib2/Filename.c @@ -20,7 +20,7 @@ #define kFilenameExtDelim '.' /* separates extension from filename */ #define kResourceFlag 'r' #define kDiskImageFlag 'i' -#define kMaxExtLen 4 /* ".123" */ +#define kMaxExtLen 5 /* ".1234" */ #define kResourceStr "_rsrc_" /* must be longer then strlen(kResourceStr)... no problem there */ @@ -182,7 +182,7 @@ AddPreservationString(NulibState* pState, pExt = nil; else pExt = FindExtension(pState, pathBuf); - if (pExt != nil && strlen(pExt+1) <= kMaxExtLen) { + if (pExt != nil && strlen(pExt+1) < kMaxExtLen) { pExt++; /* skip past the '.' */ /* if it's strictly decimal-numeric, don't use it (.1, .2, etc) */ @@ -250,6 +250,7 @@ NormalizePath(NulibState* pState, NuPathnameProposal* pPathProposal) const char* endp; char* dstp; char localFssep; + int newBufLen; Assert(pState != nil); Assert(pPathProposal != nil); @@ -262,8 +263,8 @@ NormalizePath(NulibState* pState, NuPathnameProposal* pPathProposal) * requires converting all chars to '%' codes and adding the longest * possible preservation string. */ - NState_SetTempPathnameLen(pState, - strlen(pPathProposal->pathname)*3 + kMaxPathGrowth +1); + newBufLen = strlen(pPathProposal->pathname)*3 + kMaxPathGrowth +1; + NState_SetTempPathnameLen(pState, newBufLen); pathBuf = NState_GetTempPathnameBuf(pState); Assert(pathBuf != nil); if (pathBuf == nil) @@ -316,8 +317,7 @@ NormalizePath(NulibState* pState, NuPathnameProposal* pPathProposal) pPathProposal->newFilenameSeparator = localFssep; /* check for overflow */ - Assert(dstp - pathBuf <= - (int)(strlen(pPathProposal->pathname) + kMaxPathGrowth)); + Assert(dstp - pathBuf <= newBufLen); /* * If "junk paths" is set, drop everything but the last component. @@ -543,7 +543,8 @@ DenormalizePath(NulibState* pState, char* pathBuf) if (isxdigit((int)*srcp)) { /* valid, output char */ ch += HexDigit(*srcp); - *dstp++ = ch; + if (ch != '\0') /* used by Win32 converter */ + *dstp++ = ch; srcp++; } else { /* bogus '%' with trailing hex digit found! */ diff --git a/nulib2/Nulib2.h b/nulib2/Nulib2.h index 71d6e72..b8d35da 100644 --- a/nulib2/Nulib2.h +++ b/nulib2/Nulib2.h @@ -20,6 +20,9 @@ /* replace unsupported chars with '%xx' */ #define kForeignIndic '%' +/* use this to separate path components in stored filenames */ +#define kStorageFssep ':' + /* make our one-line comments this big */ #define kDefaultCommentLen 200 diff --git a/nulib2/State.c b/nulib2/State.c index f2d6aab..538bdd1 100644 --- a/nulib2/State.c +++ b/nulib2/State.c @@ -9,7 +9,7 @@ #include "Nulib2.h" -static const char* gProgramVersion = "1.1.0"; +static const char* gProgramVersion = "2.0.0"; /* @@ -28,6 +28,11 @@ NState_Init(NulibState** ppState) * Initialize the contents to default values. */ (*ppState)->systemPathSeparator = PATH_SEP; +#ifdef PATH_SEP2 + (*ppState)->altSystemPathSeparator = PATH_SEP2; +#else + (*ppState)->altSystemPathSeparator = '\0'; +#endif (*ppState)->programVersion = gProgramVersion; return kNuErrNone; @@ -108,6 +113,9 @@ NState_DebugDump(const NulibState* pState) printf("NState:\n"); printf(" programVersion: '%s'\n", pState->programVersion); printf(" systemPathSeparator: '%c'\n", pState->systemPathSeparator); + if (pState->altSystemPathSeparator != '\0') + printf(" altSystemPathSeparator: '%c'\n", + pState->altSystemPathSeparator); printf(" archiveFilename: '%s'\n", pState->archiveFilename); printf(" filespec: %ld (%s ...)\n", pState->filespecCount, !pState->filespecCount ? "" : *pState->filespecPointer); @@ -160,6 +168,12 @@ NState_GetSystemPathSeparator(const NulibState* pState) return pState->systemPathSeparator; } +char +NState_GetAltSystemPathSeparator(const NulibState* pState) +{ + return pState->altSystemPathSeparator; +} + const char* NState_GetProgramVersion(const NulibState* pState) { diff --git a/nulib2/State.h b/nulib2/State.h index dd07249..d8363ff 100644 --- a/nulib2/State.h +++ b/nulib2/State.h @@ -38,6 +38,7 @@ typedef struct NulibState { /* system-specific values */ char systemPathSeparator; + char altSystemPathSeparator; /* pointer to archive we're working with */ NuArchive* pArchive; @@ -88,6 +89,7 @@ void NState_DebugDump(const NulibState* pState); #endif char NState_GetSystemPathSeparator(const NulibState* pState); +char NState_GetAltSystemPathSeparator(const NulibState* pState); const char* NState_GetProgramVersion(const NulibState* pState); NuArchive* NState_GetNuArchive(const NulibState* pState); void NState_SetNuArchive(NulibState* pState, NuArchive* pArchive); diff --git a/nulib2/SysDefs.h b/nulib2/SysDefs.h index 51669bb..e08560f 100644 --- a/nulib2/SysDefs.h +++ b/nulib2/SysDefs.h @@ -94,7 +94,7 @@ #if defined(WINDOWS_LIKE) # ifndef F_OK -# define F_OK 02 +# define F_OK 00 # endif # ifndef R_OK # define R_OK 04 diff --git a/nulib2/SysUtils.c b/nulib2/SysUtils.c index 79d98b7..362d2ed 100644 --- a/nulib2/SysUtils.c +++ b/nulib2/SysUtils.c @@ -80,9 +80,10 @@ UNIXNormalizeFileName(NulibState* pState, const char* srcp, long srcLen, while (srcLen--) { /* don't go until null found! */ Assert(*srcp != '\0'); - if (*srcp == '%') { + if (*srcp == kForeignIndic) { /* change '%' to "%%" */ - *dstp++ = *srcp; + if (NState_GetModPreserveType(pState)) + *dstp++ = *srcp; *dstp++ = *srcp++; } else if (*srcp == '/') { /* change '/' to "%2f" */ @@ -110,7 +111,9 @@ UNIXNormalizeFileName(NulibState* pState, const char* srcp, long srcLen, #elif defined(WINDOWS_LIKE) /* * You can't create files or directories with these names on a FAT filesystem, - * because they're MS-DOS "device special files". + * because they're MS-DOS "device special files". So, we either prepend + * a '_' (if we're not preserving filenames) or "%00" (if we are). The + * "%00" sequence gets stripped off during denormalization. * * The list comes from the Linux kernel's fs/msdos/namei.c. */ @@ -139,7 +142,12 @@ Win32NormalizeFileName(NulibState* pState, const char* srcp, long srcLen, for (ppcch = fatReservedNames3; *ppcch != nil; ppcch++) { if (strncasecmp(srcp, *ppcch, srcLen) == 0) { DBUG(("--- fixing '%s'\n", *ppcch)); - *dstp++ = '_'; + if (NState_GetModPreserveType(pState)) { + *dstp++ = kForeignIndic; + *dstp++ = '0'; + *dstp++ = '0'; + } else + *dstp++ = '_'; break; } } @@ -149,7 +157,12 @@ Win32NormalizeFileName(NulibState* pState, const char* srcp, long srcLen, for (ppcch = fatReservedNames4; *ppcch != nil; ppcch++) { if (strncasecmp(srcp, *ppcch, srcLen) == 0) { DBUG(("--- fixing '%s'\n", *ppcch)); - *dstp++ = '_'; + if (NState_GetModPreserveType(pState)) { + *dstp++ = kForeignIndic; + *dstp++ = '0'; + *dstp++ = '0'; + } else + *dstp++ = '_'; break; } } @@ -159,9 +172,10 @@ Win32NormalizeFileName(NulibState* pState, const char* srcp, long srcLen, while (srcLen--) { /* don't go until null found! */ Assert(*srcp != '\0'); - if (*srcp == '%') { + if (*srcp == kForeignIndic) { /* change '%' to "%%" */ - *dstp++ = *srcp; + if (NState_GetModPreserveType(pState)) + *dstp++ = *srcp; *dstp++ = *srcp++; } else if (strchr(kInvalid, *srcp) != nil) { /* change invalid char to "%2f" or '_' */ @@ -397,6 +411,22 @@ UNIXTimeToDateTime(const time_t* pWhen, NuDateTime *pDateTime) #endif #if defined(UNIX_LIKE) || defined(WINDOWS_LIKE) +/* + * Replace "oldc" with "newc". If we find an instance of "newc" already + * in the string, replace it with "newSubst". + */ +static void +ReplaceFssep(char* str, char oldc, char newc, char newSubst) +{ + while (*str != '\0') { + if (*str == oldc) + *str = newc; + else if (*str == newc) + *str = newSubst; + str++; + } +} + /* * Set the contents of a NuFileDetails structure, based on the pathname * and characteristics of the file. @@ -407,6 +437,7 @@ SetFileDetails(NulibState* pState, const char* pathname, struct stat* psb, { Boolean wasPreserved; Boolean doJunk = false; + Boolean adjusted; char* livePathStr; char slashDotDotSlash[5] = "_.._"; time_t now; @@ -421,12 +452,20 @@ SetFileDetails(NulibState* pState, const char* pathname, struct stat* psb, Assert(livePathStr != nil); strcpy(livePathStr, pathname); + /* under Win32, both '/' and '\' work... we want to settle on one */ + if (NState_GetAltSystemPathSeparator(pState) != '\0') { + ReplaceFssep(livePathStr, + NState_GetAltSystemPathSeparator(pState), + NState_GetSystemPathSeparator(pState), + NState_GetSystemPathSeparator(pState)); + } + /* init to defaults */ memset(pDetails, 0, sizeof(*pDetails)); pDetails->threadID = kNuThreadIDDataFork; pDetails->storageName = livePathStr; /* point at temp buffer */ pDetails->fileSysID = kNuFileSysUnknown; - pDetails->fileSysInfo = NState_GetSystemPathSeparator(pState); + pDetails->fileSysInfo = kStorageFssep; pDetails->fileType = 0; pDetails->extraType = 0; pDetails->storageType = kNuStorageUnknown; /* let NufxLib worry about it */ @@ -483,23 +522,40 @@ SetFileDetails(NulibState* pState, const char* pathname, struct stat* psb, } /* - * Check for other unpleasantness, such as a leading fssep. + * Strip bad chars off the front of the pathname. Every time we + * remove one thing we potentially expose another, so we have to + * loop until it's sanitized. + * + * The outer loop isn't really necessary under Win32, because you'd + * need to do something like ".\\foo", which isn't allowed. UNIX + * silently allows ".//foo", so this is a problem there. (We could + * probably do away with the inner loops, but those were already + * written when I saw the larger problem.) */ - Assert(NState_GetSystemPathSeparator(pState) != '\0'); - while (livePathStr[0] == NState_GetSystemPathSeparator(pState)) { - /* slide it down, len is strlen +1 (for null) -1 (dropping first char)*/ - memmove(livePathStr, livePathStr+1, strlen(livePathStr)); - } + do { + adjusted = false; - /* - * Remove leading "./". - */ - while (livePathStr[0] == '.' && - livePathStr[1] == NState_GetSystemPathSeparator(pState)) - { - /* slide it down, len is strlen +1 (for null) -2 (dropping two chars) */ - memmove(livePathStr, livePathStr+2, strlen(livePathStr)-1); - } + /* + * Check for other unpleasantness, such as a leading fssep. + */ + Assert(NState_GetSystemPathSeparator(pState) != '\0'); + while (livePathStr[0] == NState_GetSystemPathSeparator(pState)) { + /* slide it down, len is (strlen +1), -1 (dropping first char)*/ + memmove(livePathStr, livePathStr+1, strlen(livePathStr)); + adjusted = true; + } + + /* + * Remove leading "./". + */ + while (livePathStr[0] == '.' && + livePathStr[1] == NState_GetSystemPathSeparator(pState)) + { + /* slide it down, len is (strlen +1) -2 (dropping two chars) */ + memmove(livePathStr, livePathStr+2, strlen(livePathStr)-1); + adjusted = true; + } + } while (adjusted); /* * If there's a "/../" present anywhere in the name, junk everything @@ -517,6 +573,13 @@ SetFileDetails(NulibState* pState, const char* pathname, struct stat* psb, doJunk = true; } + /* + * Scan for and remove "/./" and trailing "/.". They're filesystem + * no-ops that work just fine under Win32 and UNIX but could confuse + * a IIgs. (Of course, the user could just omit them from the pathname.) + */ + /* TO DO 20030208 */ + /* * If "junk paths" is set, drop everything before the last fssep char. */ @@ -529,6 +592,16 @@ SetFileDetails(NulibState* pState, const char* pathname, struct stat* psb, } } + /* + * Finally, substitute our generally-accepted path separator in place of + * the local one, stomping on anything with a ':' in it as we do. The + * goal is to avoid having "subdir:foo/bar" turn into "subdir/foo/bar". + * Were we a general-purpose archiver, this might be a mistake, but + * we're not. NuFX doesn't really give us a choice. + */ + ReplaceFssep(livePathStr, NState_GetSystemPathSeparator(pState), + kStorageFssep, 'X'); + /*bail:*/ return kNuErrNone; }