nulib2/nulib2/Filename.c

663 lines
20 KiB
C
Raw Normal View History

2000-05-23 01:55:31 +00:00
/*
Distinguish Unicode and Mac OS Roman strings NufxLib has historically made no effort to distinguish between the character set used for filenames on the local disk, and for filenames stored within the archive. Now all Unicode filename strings use the UNICHAR type and have "UNI" in the name, and all Mac OS Roman strings have "MOR" in the name. (The naming convention makes it obvious when you're assigning the wrong thing; on Linux both formats are char*, so the compiler won't tell you if you get it wrong.) The distinction is necessary because filesystems generally support Unicode these days, but on Windows you need to use a separate set of wide-character file I/O functions. (On Linux it all works with "narrow" strings, and the UTF-8 encoding is interpreted by applications.) The character set used for NuFX archive filenames is MOR, matching what GS/OS + HFS supported, and we want to be able to convert back and forth between MOR and a Unicode representation. This change updates the various character types and string names, adds conversion functions, and updates NuLib2 for proper execution on Linux. It does not include the (probably extensive) changes required for Windows UTF-16 support. Instead, the conversion functions are no-ops, which should result in NuLib2 for Windows continuing to behave in the same slightly broken way. This adds "test-names", which exercises Unicode filenames a bit. It will not pass on Win32. Also, tweaked the Linux makefiles to have explicit dependencies, rather than empty space and an expectation that "makedepend" exists. Also, minor source code cleanups. While this probably doesn't affect binary compatibility -- it's mainly a matter of naming and string interpretation -- there's enough going on that it should be considered an API revision, so this updates the version to 3.0.0.
2014-12-24 19:14:32 +00:00
* NuLib2
2007-02-19 23:11:55 +00:00
* Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved.
2000-05-23 01:55:31 +00:00
* This is free software; you can redistribute it and/or modify it under the
2007-02-19 23:11:55 +00:00
* terms of the BSD License, see the file COPYING.
2000-05-23 01:55:31 +00:00
*
* Filename manipulation, including file type preservation.
*/
Distinguish Unicode and Mac OS Roman strings NufxLib has historically made no effort to distinguish between the character set used for filenames on the local disk, and for filenames stored within the archive. Now all Unicode filename strings use the UNICHAR type and have "UNI" in the name, and all Mac OS Roman strings have "MOR" in the name. (The naming convention makes it obvious when you're assigning the wrong thing; on Linux both formats are char*, so the compiler won't tell you if you get it wrong.) The distinction is necessary because filesystems generally support Unicode these days, but on Windows you need to use a separate set of wide-character file I/O functions. (On Linux it all works with "narrow" strings, and the UTF-8 encoding is interpreted by applications.) The character set used for NuFX archive filenames is MOR, matching what GS/OS + HFS supported, and we want to be able to convert back and forth between MOR and a Unicode representation. This change updates the various character types and string names, adds conversion functions, and updates NuLib2 for proper execution on Linux. It does not include the (probably extensive) changes required for Windows UTF-16 support. Instead, the conversion functions are no-ops, which should result in NuLib2 for Windows continuing to behave in the same slightly broken way. This adds "test-names", which exercises Unicode filenames a bit. It will not pass on Win32. Also, tweaked the Linux makefiles to have explicit dependencies, rather than empty space and an expectation that "makedepend" exists. Also, minor source code cleanups. While this probably doesn't affect binary compatibility -- it's mainly a matter of naming and string interpretation -- there's enough going on that it should be considered an API revision, so this updates the version to 3.0.0.
2014-12-24 19:14:32 +00:00
#include "NuLib2.h"
2000-05-23 01:55:31 +00:00
#include <ctype.h>
/*
* ===========================================================================
* Common definitions
2000-05-23 01:55:31 +00:00
* ===========================================================================
*/
#define kPreserveIndic '#' /* use # rather than $ for hex indication */
#define kFilenameExtDelim '.' /* separates extension from filename */
#define kResourceFlag 'r'
#define kDiskImageFlag 'i'
#define kMaxExtLen 5 /* ".1234" */
#define kResourceStr "_rsrc_"
2000-05-23 01:55:31 +00:00
/* must be longer then strlen(kResourceStr)... no problem there */
#define kMaxPathGrowth (sizeof("#XXXXXXXXYYYYYYYYZ")-1 + kMaxExtLen+1)
2000-05-23 01:55:31 +00:00
/* ProDOS file type names; must be entirely in upper case */
static const char gFileTypeNames[256][4] = {
"NON", "BAD", "PCD", "PTX", "TXT", "PDA", "BIN", "FNT",
"FOT", "BA3", "DA3", "WPF", "SOS", "$0D", "$0E", "DIR",
"RPD", "RPI", "AFD", "AFM", "AFR", "SCL", "PFS", "$17",
"$18", "ADB", "AWP", "ASP", "$1C", "$1D", "$1E", "$1F",
"TDM", "$21", "$22", "$23", "$24", "$25", "$26", "$27",
"$28", "$29", "8SC", "8OB", "8IC", "8LD", "P8C", "$2F",
"$30", "$31", "$32", "$33", "$34", "$35", "$36", "$37",
"$38", "$39", "$3A", "$3B", "$3C", "$3D", "$3E", "$3F",
"DIC", "OCR", "FTD", "$43", "$44", "$45", "$46", "$47",
"$48", "$49", "$4A", "$4B", "$4C", "$4D", "$4E", "$4F",
"GWP", "GSS", "GDB", "DRW", "GDP", "HMD", "EDU", "STN",
"HLP", "COM", "CFG", "ANM", "MUM", "ENT", "DVU", "FIN",
"$60", "$61", "$62", "$63", "$64", "$65", "$66", "$67",
"$68", "$69", "$6A", "BIO", "$6C", "TDR", "PRE", "HDV",
"$70", "$71", "$72", "$73", "$74", "$75", "$76", "$77",
"$78", "$79", "$7A", "$7B", "$7C", "$7D", "$7E", "$7F",
"$80", "$81", "$82", "$83", "$84", "$85", "$86", "$87",
"$88", "$89", "$8A", "$8B", "$8C", "$8D", "$8E", "$8F",
"$90", "$91", "$92", "$93", "$94", "$95", "$96", "$97",
"$98", "$99", "$9A", "$9B", "$9C", "$9D", "$9E", "$9F",
"WP ", "$A1", "$A2", "$A3", "$A4", "$A5", "$A6", "$A7",
"$A8", "$A9", "$AA", "GSB", "TDF", "BDF", "$AE", "$AF",
"SRC", "OBJ", "LIB", "S16", "RTL", "EXE", "PIF", "TIF",
"NDA", "CDA", "TOL", "DVR", "LDF", "FST", "$BE", "DOC",
"PNT", "PIC", "ANI", "PAL", "$C4", "OOG", "SCR", "CDV",
"FON", "FND", "ICN", "$CB", "$CC", "$CD", "$CE", "$CF",
"$D0", "$D1", "$D2", "$D3", "$D4", "MUS", "INS", "MDI",
"SND", "$D9", "$DA", "DBM", "$DC", "DDD", "$DE", "$DF",
"LBR", "$E1", "ATK", "$E3", "$E4", "$E5", "$E6", "$E7",
"$E8", "$E9", "$EA", "$EB", "$EC", "$ED", "R16", "PAS",
"CMD", "$F1", "$F2", "$F3", "$F4", "$F5", "$F6", "$F7",
"$F8", "OS ", "INT", "IVR", "BAS", "VAR", "REL", "SYS"
2000-05-23 01:55:31 +00:00
};
/*
* Some file extensions we recognize. When adding files with "extended"
* preservation mode, we try to assign types to files that weren't
* explicitly preserved, but nevertheless have a recognizeable type.
*
* geoff @ gwlink.net points out that this really ought to be in an external
2000-05-23 01:55:31 +00:00
* file rather than a hard-coded table. Ought to fix that someday.
*/
static const struct {
const char* label;
uint8_t fileType;
uint16_t auxType;
2000-05-23 01:55:31 +00:00
} gRecognizedExtensions[] = {
{ "ASM", 0xb0, 0x0003 }, /* APW assembly source */
{ "C", 0xb0, 0x000a }, /* APW C source */
{ "H", 0xb0, 0x000a }, /* APW C header */
{ "CPP", 0xb0, 0x0000 }, /* generic source file */
{ "BNY", 0xe0, 0x8000 }, /* Binary II lib */
{ "BQY", 0xe0, 0x8000 }, /* Binary II lib, w/ compress */
{ "BXY", 0xe0, 0x8000 }, /* Binary II wrap around SHK */
{ "BSE", 0xe0, 0x8000 }, /* Binary II wrap around SEA */
{ "SEA", 0xb3, 0xdb07 }, /* GSHK SEA */
{ "TEXT", 0x04, 0x0000 }, /* ASCII text */
{ "GIF", 0xc0, 0x8006 }, /* GIF image */
{ "JPG", 0x06, 0x0000 }, /* JPEG (nicer than 'NON') */
{ "JPEG", 0x06, 0x0000 }, /* JPEG (nicer than 'NON') */
{ "SHK", 0xe0, 0x8002 }, /* ShrinkIt archive */
2000-05-23 01:55:31 +00:00
};
/*
* Return a pointer to the three-letter representation of the file type name.
*/
const char* GetFileTypeString(uint32_t fileType)
2000-05-23 01:55:31 +00:00
{
if (fileType < NELEM(gFileTypeNames))
return gFileTypeNames[fileType];
else
return "???";
2000-05-23 01:55:31 +00:00
}
/*
* ===========================================================================
* File type preservation
2000-05-23 01:55:31 +00:00
* ===========================================================================
*/
/*
* Add a preservation string.
*
* "pathBuf" is assumed to have enough space to hold the current path
* plus kMaxPathGrowth more. It will be modified in place.
*/
static void AddPreservationString(NulibState* pState,
const NuPathnameProposal* pPathProposal, char* pathBuf)
2000-05-23 01:55:31 +00:00
{
char extBuf[kMaxPathGrowth +1];
const NuRecord* pRecord;
const NuThread* pThread;
NuThreadID threadID;
char* cp;
2014-12-22 02:17:23 +00:00
Assert(pState != NULL);
Assert(pPathProposal != NULL);
Assert(pathBuf != NULL);
Assert(NState_GetModPreserveType(pState));
pRecord = pPathProposal->pRecord;
pThread = pPathProposal->pThread;
2014-12-22 02:17:23 +00:00
Assert(pRecord != NULL);
Assert(pThread != NULL);
cp = extBuf;
/*
* Cons up a preservation string. On some platforms "sprintf" doesn't
* return the #of characters written, so we add it up manually.
*/
if (pRecord->recFileType < 0x100 && pRecord->recExtraType < 0x10000) {
sprintf(cp, "%c%02x%04x", kPreserveIndic, pRecord->recFileType,
pRecord->recExtraType);
cp += 7;
} else {
sprintf(cp, "%c%08x%08x", kPreserveIndic, pRecord->recFileType,
pRecord->recExtraType);
cp += 17;
}
threadID = NuMakeThreadID(pThread->thThreadClass, pThread->thThreadKind);
if (threadID == kNuThreadIDRsrcFork)
*cp++ = kResourceFlag;
else if (threadID == kNuThreadIDDiskImage)
*cp++ = kDiskImageFlag;
/*
* If they've asked for "extended" type preservation, then we need
* to retain either the existing extension or append an extension
* based on the ProDOS file type.
*/
if (NState_GetModPreserveTypeExtended(pState)) {
const char* pExt;
char* end;
/*
* Find extension. Note FindExtension guarantees there's at least
* one char after '.'.
*
* It's hard to know when this is right and when it isn't. It's
* fairly likely that a text file really ought to end in ".txt",
* and it's fairly unlikely that a BIN file should be ".bin", but
* where do the rest fall in? We might want to force TXT files
* to be ".txt", and perhaps do something clever for some others.
*/
if (pRecord->recFileType == 0x04 || threadID == kNuThreadIDDiskImage)
2014-12-22 02:17:23 +00:00
pExt = NULL;
else
pExt = FindExtension(pState, pathBuf);
2014-12-22 02:17:23 +00:00
if (pExt != NULL) {
pExt++; /* skip past the '.' */
if (strlen(pExt) >= kMaxExtLen) {
/* too long, forget it */
2014-12-22 02:17:23 +00:00
pExt = NULL;
} else {
/* if strictly decimal-numeric, don't use it (.1, .2, etc) */
(void) strtoul(pExt, &end, 10);
if (*end == '\0') {
2014-12-22 02:17:23 +00:00
pExt = NULL;
} else {
/* if '#' appears in it, don't use it -- it'll confuse us */
const char* ccp = pExt;
while (*ccp != '\0') {
if (*ccp == '#') {
2014-12-22 02:17:23 +00:00
pExt = NULL;
break;
}
ccp++;
}
}
}
} else {
/*
* There's no extension on the filename. Use the standard
* ProDOS type, if one exists for this entry. We don't use
* the table if it's "NON" or a hex value.
*/
if (threadID == kNuThreadIDDiskImage)
pExt = "PO"; /* indicate it's a ProDOS-ordered image */
else if (pRecord->recFileType) {
pExt = GetFileTypeString(pRecord->recFileType);
if (pExt[0] == '?' || pExt[0] == '$')
2014-12-22 02:17:23 +00:00
pExt = NULL;
}
}
2014-12-22 02:17:23 +00:00
if (pExt != NULL) {
*cp++ = kFilenameExtDelim;
strcpy(cp, pExt);
cp += strlen(pExt);
}
}
/* make sure it's terminated */
*cp = '\0';
Assert(cp - extBuf <= kMaxPathGrowth);
strcat(pathBuf, extBuf);
2000-05-23 01:55:31 +00:00
}
/*
* Normalize a path for the conventions on the output filesystem. This
* adds optional file type preservation.
*
* The path from the archive is in "pPathProposal". Thew new pathname
* will be placed in the "new pathname" section of "pPathProposal".
*
* The new pathname may be shorter (because characters were removed) or
* longer (if we add a "#XXYYYYZ" extension or replace chars with '%' codes).
*
* This returns the new pathname, which is held in NulibState's temporary
2000-05-23 01:55:31 +00:00
* pathname buffer.
*/
const char* NormalizePath(NulibState* pState, NuPathnameProposal* pPathProposal)
2000-05-23 01:55:31 +00:00
{
NuError err = kNuErrNone;
char* pathBuf;
const char* startp;
const char* endp;
char* dstp;
char localFssep;
int newBufLen;
2014-12-22 02:17:23 +00:00
Assert(pState != NULL);
Assert(pPathProposal != NULL);
Distinguish Unicode and Mac OS Roman strings NufxLib has historically made no effort to distinguish between the character set used for filenames on the local disk, and for filenames stored within the archive. Now all Unicode filename strings use the UNICHAR type and have "UNI" in the name, and all Mac OS Roman strings have "MOR" in the name. (The naming convention makes it obvious when you're assigning the wrong thing; on Linux both formats are char*, so the compiler won't tell you if you get it wrong.) The distinction is necessary because filesystems generally support Unicode these days, but on Windows you need to use a separate set of wide-character file I/O functions. (On Linux it all works with "narrow" strings, and the UTF-8 encoding is interpreted by applications.) The character set used for NuFX archive filenames is MOR, matching what GS/OS + HFS supported, and we want to be able to convert back and forth between MOR and a Unicode representation. This change updates the various character types and string names, adds conversion functions, and updates NuLib2 for proper execution on Linux. It does not include the (probably extensive) changes required for Windows UTF-16 support. Instead, the conversion functions are no-ops, which should result in NuLib2 for Windows continuing to behave in the same slightly broken way. This adds "test-names", which exercises Unicode filenames a bit. It will not pass on Win32. Also, tweaked the Linux makefiles to have explicit dependencies, rather than empty space and an expectation that "makedepend" exists. Also, minor source code cleanups. While this probably doesn't affect binary compatibility -- it's mainly a matter of naming and string interpretation -- there's enough going on that it should be considered an API revision, so this updates the version to 3.0.0.
2014-12-24 19:14:32 +00:00
Assert(pPathProposal->pathnameUNI != NULL);
localFssep = NState_GetSystemPathSeparator(pState);
/*
* Set up temporary buffer space. The maximum possible expansion
* requires converting all chars to '%' codes and adding the longest
* possible preservation string.
*/
Distinguish Unicode and Mac OS Roman strings NufxLib has historically made no effort to distinguish between the character set used for filenames on the local disk, and for filenames stored within the archive. Now all Unicode filename strings use the UNICHAR type and have "UNI" in the name, and all Mac OS Roman strings have "MOR" in the name. (The naming convention makes it obvious when you're assigning the wrong thing; on Linux both formats are char*, so the compiler won't tell you if you get it wrong.) The distinction is necessary because filesystems generally support Unicode these days, but on Windows you need to use a separate set of wide-character file I/O functions. (On Linux it all works with "narrow" strings, and the UTF-8 encoding is interpreted by applications.) The character set used for NuFX archive filenames is MOR, matching what GS/OS + HFS supported, and we want to be able to convert back and forth between MOR and a Unicode representation. This change updates the various character types and string names, adds conversion functions, and updates NuLib2 for proper execution on Linux. It does not include the (probably extensive) changes required for Windows UTF-16 support. Instead, the conversion functions are no-ops, which should result in NuLib2 for Windows continuing to behave in the same slightly broken way. This adds "test-names", which exercises Unicode filenames a bit. It will not pass on Win32. Also, tweaked the Linux makefiles to have explicit dependencies, rather than empty space and an expectation that "makedepend" exists. Also, minor source code cleanups. While this probably doesn't affect binary compatibility -- it's mainly a matter of naming and string interpretation -- there's enough going on that it should be considered an API revision, so this updates the version to 3.0.0.
2014-12-24 19:14:32 +00:00
newBufLen = strlen(pPathProposal->pathnameUNI)*3 + kMaxPathGrowth +1;
NState_SetTempPathnameLen(pState, newBufLen);
pathBuf = NState_GetTempPathnameBuf(pState);
2014-12-22 02:17:23 +00:00
Assert(pathBuf != NULL);
if (pathBuf == NULL)
return NULL;
Distinguish Unicode and Mac OS Roman strings NufxLib has historically made no effort to distinguish between the character set used for filenames on the local disk, and for filenames stored within the archive. Now all Unicode filename strings use the UNICHAR type and have "UNI" in the name, and all Mac OS Roman strings have "MOR" in the name. (The naming convention makes it obvious when you're assigning the wrong thing; on Linux both formats are char*, so the compiler won't tell you if you get it wrong.) The distinction is necessary because filesystems generally support Unicode these days, but on Windows you need to use a separate set of wide-character file I/O functions. (On Linux it all works with "narrow" strings, and the UTF-8 encoding is interpreted by applications.) The character set used for NuFX archive filenames is MOR, matching what GS/OS + HFS supported, and we want to be able to convert back and forth between MOR and a Unicode representation. This change updates the various character types and string names, adds conversion functions, and updates NuLib2 for proper execution on Linux. It does not include the (probably extensive) changes required for Windows UTF-16 support. Instead, the conversion functions are no-ops, which should result in NuLib2 for Windows continuing to behave in the same slightly broken way. This adds "test-names", which exercises Unicode filenames a bit. It will not pass on Win32. Also, tweaked the Linux makefiles to have explicit dependencies, rather than empty space and an expectation that "makedepend" exists. Also, minor source code cleanups. While this probably doesn't affect binary compatibility -- it's mainly a matter of naming and string interpretation -- there's enough going on that it should be considered an API revision, so this updates the version to 3.0.0.
2014-12-24 19:14:32 +00:00
startp = pPathProposal->pathnameUNI;
dstp = pathBuf;
while (*startp == pPathProposal->filenameSeparator) {
/* ignore leading path sep; always extract to current dir */
startp++;
}
/* normalize all directory components and the filename component */
2014-12-22 02:17:23 +00:00
while (startp != NULL) {
endp = strchr(startp, pPathProposal->filenameSeparator);
2014-12-22 02:17:23 +00:00
if (endp != NULL) {
/* normalize directory component */
err = NormalizeDirectoryName(pState, startp, endp - startp,
pPathProposal->filenameSeparator, &dstp,
NState_GetTempPathnameLen(pState));
if (err != kNuErrNone)
goto bail;
*dstp++ = localFssep;
startp = endp +1;
} else {
/* normalize filename */
err = NormalizeFileName(pState, startp, strlen(startp),
pPathProposal->filenameSeparator, &dstp,
NState_GetTempPathnameLen(pState));
if (err != kNuErrNone)
goto bail;
/* add/replace extension if necessary */
*dstp++ = '\0';
if (NState_GetModPreserveType(pState)) {
AddPreservationString(pState, pPathProposal, pathBuf);
} else if (NuGetThreadID(pPathProposal->pThread) == kNuThreadIDRsrcFork)
{
#ifndef HAS_RESOURCE_FORKS
/* add this in lieu of the preservation extension */
strcat(pathBuf, kResourceStr);
#endif
}
2014-12-22 02:17:23 +00:00
startp = NULL; /* we're done */
}
}
Distinguish Unicode and Mac OS Roman strings NufxLib has historically made no effort to distinguish between the character set used for filenames on the local disk, and for filenames stored within the archive. Now all Unicode filename strings use the UNICHAR type and have "UNI" in the name, and all Mac OS Roman strings have "MOR" in the name. (The naming convention makes it obvious when you're assigning the wrong thing; on Linux both formats are char*, so the compiler won't tell you if you get it wrong.) The distinction is necessary because filesystems generally support Unicode these days, but on Windows you need to use a separate set of wide-character file I/O functions. (On Linux it all works with "narrow" strings, and the UTF-8 encoding is interpreted by applications.) The character set used for NuFX archive filenames is MOR, matching what GS/OS + HFS supported, and we want to be able to convert back and forth between MOR and a Unicode representation. This change updates the various character types and string names, adds conversion functions, and updates NuLib2 for proper execution on Linux. It does not include the (probably extensive) changes required for Windows UTF-16 support. Instead, the conversion functions are no-ops, which should result in NuLib2 for Windows continuing to behave in the same slightly broken way. This adds "test-names", which exercises Unicode filenames a bit. It will not pass on Win32. Also, tweaked the Linux makefiles to have explicit dependencies, rather than empty space and an expectation that "makedepend" exists. Also, minor source code cleanups. While this probably doesn't affect binary compatibility -- it's mainly a matter of naming and string interpretation -- there's enough going on that it should be considered an API revision, so this updates the version to 3.0.0.
2014-12-24 19:14:32 +00:00
pPathProposal->newPathnameUNI = pathBuf;
pPathProposal->newFilenameSeparator = localFssep;
/* check for overflow */
Assert(dstp - pathBuf <= newBufLen);
/*
* If "junk paths" is set, drop everything but the last component.
*/
if (NState_GetModJunkPaths(pState)) {
char* lastFssep;
lastFssep = strrchr(pathBuf, localFssep);
2014-12-22 02:17:23 +00:00
if (lastFssep != NULL) {
Assert(*(lastFssep+1) != '\0'); /* should already have been caught*/
memmove(pathBuf, lastFssep+1, strlen(lastFssep+1)+1);
}
}
2000-05-23 01:55:31 +00:00
bail:
if (err != kNuErrNone)
2014-12-22 02:17:23 +00:00
return NULL;
return pathBuf;
2000-05-23 01:55:31 +00:00
}
/*
* ===========================================================================
* File type restoration
2000-05-23 01:55:31 +00:00
* ===========================================================================
*/
/*
* Try to figure out what file type is associated with a filename extension.
*
* This checks the standard list of ProDOS types (which should catch things
* like "TXT" and "BIN") and the separate list of recognized extensions.
*/
static void LookupExtension(NulibState* pState, const char* ext,
uint32_t* pFileType, uint32_t* pAuxType)
2000-05-23 01:55:31 +00:00
{
char uext3[4];
int i, extLen;
extLen = strlen(ext);
Assert(extLen > 0);
/*
* First step is to try to find it in the recognized types list.
*/
for (i = 0; i < NELEM(gRecognizedExtensions); i++) {
if (strcasecmp(ext, gRecognizedExtensions[i].label) == 0) {
*pFileType = gRecognizedExtensions[i].fileType;
*pAuxType = gRecognizedExtensions[i].auxType;
goto bail;
}
}
/*
* Second step is to try to find it in the ProDOS types list.
*
* The extension is converted to upper case and padded with spaces.
*
* [do we want to obstruct matching on things like '$f7' here?]
*/
if (extLen <= 3) {
for (i = 2; i >= extLen; i--)
uext3[i] = ' ';
for ( ; i >= 0; i--)
uext3[i] = toupper(ext[i]);
uext3[3] = '\0';
/*printf("### converted '%s' to '%s'\n", ext, uext3);*/
for (i = 0; i < NELEM(gFileTypeNames); i++) {
if (strcmp(uext3, gFileTypeNames[i]) == 0) {
*pFileType = i;
goto bail;
}
}
}
2000-05-23 01:55:31 +00:00
bail:
return;
2000-05-23 01:55:31 +00:00
}
/*
* Try to associate some meaning with the file extension.
*/
void InterpretExtension(NulibState* pState, const char* pathName,
uint32_t* pFileType, uint32_t* pAuxType)
2000-05-23 01:55:31 +00:00
{
const char* pExt;
2000-05-23 01:55:31 +00:00
2014-12-22 02:17:23 +00:00
Assert(pState != NULL);
Assert(pathName != NULL);
Assert(pFileType != NULL);
Assert(pAuxType != NULL);
2000-05-23 01:55:31 +00:00
pExt = FindExtension(pState, pathName);
2014-12-22 02:17:23 +00:00
if (pExt != NULL)
LookupExtension(pState, pExt+1, pFileType, pAuxType);
2000-05-23 01:55:31 +00:00
}
/*
* Check to see if there's a preservation string on the filename. If so,
* set the filetype and auxtype information, and trim the preservation
* string off.
*
* We have to be careful not to trip on false-positive occurrences of '#'
* in the filename.
*/
Boolean ExtractPreservationString(NulibState* pState, char* pathname,
uint32_t* pFileType, uint32_t* pAuxType, NuThreadID* pThreadID)
2000-05-23 01:55:31 +00:00
{
char numBuf[9];
uint32_t fileType, auxType;
NuThreadID threadID;
char* pPreserve;
char* cp;
int digitCount;
2014-12-22 02:17:23 +00:00
Assert(pState != NULL);
Assert(pathname != NULL);
Assert(pFileType != NULL);
Assert(pAuxType != NULL);
Assert(pThreadID != NULL);
pPreserve = strrchr(pathname, kPreserveIndic);
2014-12-22 02:17:23 +00:00
if (pPreserve == NULL)
return false;
/* count up the #of hex digits */
digitCount = 0;
for (cp = pPreserve+1; *cp != '\0' && isxdigit((int)*cp); cp++)
digitCount++;
/* extract the file and aux type */
switch (digitCount) {
case 6:
/* ProDOS 1-byte type and 2-byte aux */
memcpy(numBuf, pPreserve+1, 2);
numBuf[2] = 0;
fileType = strtoul(numBuf, &cp, 16);
Assert(cp == numBuf + 2);
auxType = strtoul(pPreserve+3, &cp, 16);
Assert(cp == pPreserve + 7);
break;
case 16:
/* HFS 4-byte type and 4-byte creator */
memcpy(numBuf, pPreserve+1, 8);
numBuf[8] = 0;
fileType = strtoul(numBuf, &cp, 16);
Assert(cp == numBuf + 8);
auxType = strtoul(pPreserve+9, &cp, 16);
Assert(cp == pPreserve + 17);
break;
default:
/* not valid */
return false;
}
/* check for a threadID specifier */
threadID = kNuThreadIDDataFork;
switch (*cp) {
case kResourceFlag:
threadID = kNuThreadIDRsrcFork;
cp++;
break;
case kDiskImageFlag:
threadID = kNuThreadIDDiskImage;
cp++;
break;
default:
/* do nothing... yet */
break;
}
/* make sure we were the very last component */
switch (*cp) {
case kFilenameExtDelim: /* redundant "-ee" extension */
case '\0': /* end of string! */
break;
default:
return false;
}
/* truncate the original string, and return what we got */
*pPreserve = '\0';
*pFileType = fileType;
*pAuxType = auxType;
*pThreadID = threadID;
return true;
2000-05-23 01:55:31 +00:00
}
/*
* Remove NuLib2's normalization magic (e.g. "%2f" for '/').
*
* This always results in the filename staying the same length or getting
* smaller, so we can do it in place in the buffer.
*/
void DenormalizePath(NulibState* pState, char* pathBuf)
2000-05-23 01:55:31 +00:00
{
const char* srcp;
char* dstp;
char ch;
srcp = pathBuf;
dstp = pathBuf;
while (*srcp != '\0') {
if (*srcp == kForeignIndic) {
srcp++;
if (*srcp == kForeignIndic) {
*dstp++ = kForeignIndic;
srcp++;
} else if (isxdigit((int)*srcp)) {
ch = HexDigit(*srcp) << 4;
srcp++;
if (isxdigit((int)*srcp)) {
/* valid, output char */
ch += HexDigit(*srcp);
if (ch != '\0') /* used by Win32 converter */
*dstp++ = ch;
srcp++;
} else {
/* bogus '%' with trailing hex digit found! */
*dstp++ = kForeignIndic;
*dstp++ = *(srcp-1);
}
} else {
/* bogus lone '%s' found! */
*dstp++ = kForeignIndic;
}
} else {
*dstp++ = *srcp++;
}
}
*dstp = '\0';
Assert(dstp <= srcp);
2000-05-23 01:55:31 +00:00
}
/*
* ===========================================================================
* Misc utils
2000-05-23 01:55:31 +00:00
* ===========================================================================
*/
/*
* Find the filename component of a local pathname. Uses the fssep defined
* in pState.
*
2014-12-22 02:17:23 +00:00
* Always returns a pointer to a string; never returns NULL.
2000-05-23 01:55:31 +00:00
*/
const char* FilenameOnly(NulibState* pState, const char* pathname)
2000-05-23 01:55:31 +00:00
{
const char* retstr;
const char* pSlash;
2014-12-22 02:17:23 +00:00
char* tmpStr = NULL;
2014-12-22 02:17:23 +00:00
Assert(pState != NULL);
Assert(pathname != NULL);
pSlash = strrchr(pathname, NState_GetSystemPathSeparator(pState));
2014-12-22 02:17:23 +00:00
if (pSlash == NULL) {
retstr = pathname; /* whole thing is the filename */
goto bail;
}
pSlash++;
if (*pSlash == '\0') {
if (strlen(pathname) < 2) {
retstr = pathname; /* the pathname is just "/"? Whatever */
goto bail;
}
/* some bonehead put an fssep on the very end; back up before it */
/* (not efficient, but this should be rare, and I'm feeling lazy) */
tmpStr = strdup(pathname);
tmpStr[strlen(pathname)-1] = '\0';
pSlash = strrchr(tmpStr, NState_GetSystemPathSeparator(pState));
2014-12-22 02:17:23 +00:00
if (pSlash == NULL) {
retstr = pathname; /* just a filename with a '/' after it */
goto bail;
}
pSlash++;
if (*pSlash == '\0') {
retstr = pathname; /* I give up! */
goto bail;
}
retstr = pathname + (pSlash - tmpStr);
} else {
retstr = pSlash;
}
2000-05-23 01:55:31 +00:00
bail:
Free(tmpStr);
return retstr;
2000-05-23 01:55:31 +00:00
}
/*
* Return the filename extension found in a full pathname.
*
* An extension is the stuff following the last '.' in the filename. If
* there is nothing following the last '.', then there is no extension.
*
2014-12-22 02:17:23 +00:00
* Returns a pointer to the '.' preceding the extension, or NULL if no
2000-05-23 01:55:31 +00:00
* extension was found.
*/
const char* FindExtension(NulibState* pState, const char* pathname)
2000-05-23 01:55:31 +00:00
{
const char* pFilename;
const char* pExt;
/*
* We have to isolate the filename so that we don't get excited
* about "/foo.bar/file".
*/
pFilename = FilenameOnly(pState, pathname);
2014-12-22 02:17:23 +00:00
Assert(pFilename != NULL);
pExt = strrchr(pFilename, kFilenameExtDelim);
/* also check for "/blah/foo.", which doesn't count */
2014-12-22 02:17:23 +00:00
if (pExt != NULL && *(pExt+1) != '\0')
return pExt;
2014-12-22 02:17:23 +00:00
return NULL;
2000-05-23 01:55:31 +00:00
}