Update NufxLib with recent 3.1.0 changes

This commit is contained in:
Andy McFadden 2015-12-26 16:50:42 -08:00
parent 7596c950fa
commit 6bcf388c3e
9 changed files with 198 additions and 85 deletions

View File

@ -1,4 +1,8 @@
2005/01/09 ***** v3.0.0 shipped *****
2015/12/26 fadden
- Fix handling of entries with missing threads.
- Improve handling of Mac OS X file type attributes.
2015/01/09 ***** v3.0.0 shipped *****
2015/01/03 fadden
- Mac OS X: replace Carbon FinderInfo calls with BSD xattr.

View File

@ -228,6 +228,13 @@ typedef struct NuFileInfo {
/*
* Determine whether the record has both data and resource forks.
*
* TODO: if we're not using "mask dataless", scanning threads may not
* get the right answer, because GSHK omits theads for zero-length forks.
* We could check pRecord->recStorageType, though we have to be careful
* because that's overloaded for disk images. In any event, the result
* from this method isn't relevant unless we're trying to use forked
* files on the native filesystem.
*/
static Boolean Nu_IsForkedFile(NuArchive* pArchive, const NuRecord* pRecord)
{
@ -320,20 +327,44 @@ static NuError Nu_SetFinderInfo(NuArchive* pArchive, const NuRecord* pRecord,
return kNuErrFile;
}
/* build type and creator from 8-bit type and 16-bit aux type */
uint32_t fileType, creator;
fileType = ('p' << 24) | ((pRecord->recFileType & 0xff) << 16) |
(pRecord->recExtraType & 0xffff);
creator = 'pdos';
uint8_t proType = (uint8_t) pRecord->recFileType;
uint16_t proAux = (uint16_t) pRecord->recExtraType;
fiBuf[0] = fileType >> 24;
fiBuf[1] = fileType >> 16;
fiBuf[2] = fileType >> 8;
fiBuf[3] = fileType;
fiBuf[4] = creator >> 24;
fiBuf[5] = creator >> 16;
fiBuf[6] = creator >> 8;
fiBuf[7] = creator;
/*
* Attempt to use one of the convenience types. If nothing matches,
* use the generic pdos/pXYZ approach. Note that PSYS/PS16 will
* lose the file's aux type.
*
* I'm told this is from page 336 of _Programmer's Reference for
* System 6.0_.
*/
uint8_t* fileTypeBuf = fiBuf;
uint8_t* creatorBuf = fiBuf + 4;
memcpy(creatorBuf, "pdos", 4);
if (proType == 0x00 && proAux == 0x0000) {
memcpy(fileTypeBuf, "BINA", 4);
} else if (proType == 0x04 && proAux == 0x0000) {
memcpy(fileTypeBuf, "TEXT", 4);
} else if (proType == 0xff) {
memcpy(fileTypeBuf, "PSYS", 4);
} else if (proType == 0xb3 && (proAux & 0xff00) != 0xdb00) {
memcpy(fileTypeBuf, "PS16", 4);
} else if (proType == 0xd7 && proAux == 0x0000) {
memcpy(fileTypeBuf, "MIDI", 4);
} else if (proType == 0xd8 && proAux == 0x0000) {
memcpy(fileTypeBuf, "AIFF", 4);
} else if (proType == 0xd8 && proAux == 0x0001) {
memcpy(fileTypeBuf, "AIFC", 4);
} else if (proType == 0xe0 && proAux == 0x0005) {
memcpy(creatorBuf, "dCpy", 4);
memcpy(fileTypeBuf, "dImg", 4);
} else {
fileTypeBuf[0] = 'p';
fileTypeBuf[1] = proType;
fileTypeBuf[2] = (uint8_t) (proAux >> 8);
fileTypeBuf[3] = (uint8_t) proAux;
}
if (setxattr(pathnameUNI, XATTR_FINDERINFO_NAME, fiBuf, sizeof(fiBuf),
0, 0) != 0)

View File

@ -33,7 +33,7 @@ extern "C" {
* fixes. Unless, of course, your code depends upon that fix.
*/
#define kNuVersionMajor 3
#define kNuVersionMinor 0
#define kNuVersionMinor 1
#define kNuVersionBug 0
@ -201,13 +201,16 @@ typedef uint32_t NuThreadID;
#define kNuThreadClassControl 0x0001
#define kNuThreadClassData 0x0002
#define kNuThreadClassFilename 0x0003
#define kNuThreadKindDataFork 0x0000 /* when class=data */
#define kNuThreadKindDiskImage 0x0001 /* when class=data */
#define kNuThreadKindRsrcFork 0x0002 /* when class=data */
#define kNuThreadIDOldComment NuMakeThreadID(kNuThreadClassMessage, 0x0000)
#define kNuThreadIDComment NuMakeThreadID(kNuThreadClassMessage, 0x0001)
#define kNuThreadIDIcon NuMakeThreadID(kNuThreadClassMessage, 0x0002)
#define kNuThreadIDMkdir NuMakeThreadID(kNuThreadClassControl, 0x0000)
#define kNuThreadIDDataFork NuMakeThreadID(kNuThreadClassData, 0x0000)
#define kNuThreadIDDiskImage NuMakeThreadID(kNuThreadClassData, 0x0001)
#define kNuThreadIDRsrcFork NuMakeThreadID(kNuThreadClassData, 0x0002)
#define kNuThreadIDDataFork NuMakeThreadID(kNuThreadClassData, kNuThreadKindDataFork)
#define kNuThreadIDDiskImage NuMakeThreadID(kNuThreadClassData, kNuThreadKindDiskImage)
#define kNuThreadIDRsrcFork NuMakeThreadID(kNuThreadClassData, kNuThreadKindRsrcFork)
#define kNuThreadIDFilename NuMakeThreadID(kNuThreadClassFilename, 0x0000)
#define kNuThreadIDWildcard NuMakeThreadID(0xffff, 0xffff)

View File

@ -1524,7 +1524,7 @@ NuError Nu_StreamExtract(NuArchive* pArchive)
{
NuError err = kNuErrNone;
NuRecord tmpRecord;
Boolean hasInterestingThread;
Boolean needFakeData, needFakeRsrc;
uint32_t count;
long idx;
@ -1573,7 +1573,8 @@ NuError Nu_StreamExtract(NuArchive* pArchive)
/*Nu_DebugDumpRecord(&tmpRecord);
printf("\n");*/
hasInterestingThread = false;
needFakeData = true;
needFakeRsrc = (tmpRecord.recStorageType == kNuStorageExtended);
/* extract all relevant (remaining) threads */
pArchive->lastFileCreatedUNI = NULL;
@ -1581,7 +1582,11 @@ NuError Nu_StreamExtract(NuArchive* pArchive)
const NuThread* pThread = Nu_GetThread(&tmpRecord, idx);
if (pThread->thThreadClass == kNuThreadClassData) {
hasInterestingThread = true;
if (pThread->thThreadKind == kNuThreadKindDataFork) {
needFakeData = false;
} else if (pThread->thThreadKind == kNuThreadKindRsrcFork) {
needFakeRsrc = false;
}
err = Nu_ExtractThreadBulk(pArchive, &tmpRecord, pThread);
if (err == kNuErrSkipped) {
err = Nu_SkipThread(pArchive, &tmpRecord, pThread);
@ -1595,7 +1600,8 @@ NuError Nu_StreamExtract(NuArchive* pArchive)
if (NuGetThreadID(pThread) != kNuThreadIDComment &&
NuGetThreadID(pThread) != kNuThreadIDFilename)
{
hasInterestingThread = true;
/* unknown stuff in record, skip thread fakery */
needFakeData = needFakeRsrc = false;
}
err = Nu_SkipThread(pArchive, &tmpRecord, pThread);
BailError(err);
@ -1603,19 +1609,19 @@ NuError Nu_StreamExtract(NuArchive* pArchive)
}
/*
* If we're trying to be compatible with ShrinkIt, and the record
* had nothing in it but comments and filenames, then we need to
* create a zero-byte data file (and possibly a resource fork).
*
* See notes in previous instance, above.
* As in Nu_ExtractRecordByPtr, we need to synthesize empty forks for
* cases where GSHK omitted the data thread entirely.
*/
if (/*pArchive->valMaskDataless &&*/ !hasInterestingThread) {
err = Nu_FakeZeroExtract(pArchive, &tmpRecord, 0x0000);
Assert(!pArchive->valMaskDataless || (!needFakeData && !needFakeRsrc));
if (needFakeData) {
err = Nu_FakeZeroExtract(pArchive, &tmpRecord,
kNuThreadKindDataFork);
BailError(err);
}
if (needFakeRsrc) {
err = Nu_FakeZeroExtract(pArchive, &tmpRecord,
kNuThreadKindRsrcFork);
BailError(err);
if (tmpRecord.recStorageType == kNuStorageExtended) {
err = Nu_FakeZeroExtract(pArchive, &tmpRecord, 0x0002);
BailError(err);
}
}
/* dispose of the entry */
@ -1698,20 +1704,26 @@ bail:
static NuError Nu_ExtractRecordByPtr(NuArchive* pArchive, NuRecord* pRecord)
{
NuError err = kNuErrNone;
Boolean hasInterestingThread;
Boolean needFakeData, needFakeRsrc;
uint32_t idx;
needFakeData = true;
needFakeRsrc = (pRecord->recStorageType == kNuStorageExtended);
Assert(!Nu_IsStreaming(pArchive)); /* we don't skip things we don't read */
Assert(pRecord != NULL);
/* extract all relevant threads */
hasInterestingThread = false;
pArchive->lastFileCreatedUNI = NULL;
for (idx = 0; idx < pRecord->recTotalThreads; idx++) {
const NuThread* pThread = Nu_GetThread(pRecord, idx);
if (pThread->thThreadClass == kNuThreadClassData) {
hasInterestingThread = true;
if (pThread->thThreadKind == kNuThreadKindDataFork) {
needFakeData = false;
} else if (pThread->thThreadKind == kNuThreadKindRsrcFork) {
needFakeRsrc = false;
}
err = Nu_ExtractThreadBulk(pArchive, pRecord, pThread);
if (err == kNuErrSkipped) {
err = Nu_SkipThread(pArchive, pRecord, pThread);
@ -1722,7 +1734,13 @@ static NuError Nu_ExtractRecordByPtr(NuArchive* pArchive, NuRecord* pRecord)
if (NuGetThreadID(pThread) != kNuThreadIDComment &&
NuGetThreadID(pThread) != kNuThreadIDFilename)
{
hasInterestingThread = true;
/*
* This record has a thread we don't recognize. Disable
* the thread fakery to avoid doing anything weird -- we
* should only need to create zero-length files for
* simple file records.
*/
needFakeData = needFakeRsrc = false;
}
DBUG(("IGNORING 0x%08lx from '%s'\n",
NuMakeThreadID(pThread->thThreadClass, pThread->thThreadKind),
@ -1731,28 +1749,27 @@ static NuError Nu_ExtractRecordByPtr(NuArchive* pArchive, NuRecord* pRecord)
}
/*
* If we're trying to be compatible with ShrinkIt, and the record
* had nothing in it but comments and filenames, then we need to
* create a zero-byte file.
* GSHK creates empty threads for zero-length forks. It doesn't always
* handle them correctly when extracting, so it appears this behavior
* may not be intentional.
*
* (GSHK handles empty data and resource forks by not storing a
* thread at all. It doesn't correctly deal with them when extracting
* though, so it appears this behavior wasn't entirely expected.)
* We need to create an empty file for whichever forks are missing.
* Could be the data fork, resource fork, or both. The only way to
* know what's expected is to examine the file's storage type.
*
* If it's a forked file, we also need to create an empty rsrc file.
*
* If valMaskDataless is enabled, this won't fire, because we "forge"
* appropriate threads.
* If valMaskDataless is enabled, this won't fire, because we will have
* "forged" the appropriate threads.
*
* Note there's another one of these below, in Nu_StreamExtract.
*/
if (/*pArchive->valMaskDataless &&*/ !hasInterestingThread) {
err = Nu_FakeZeroExtract(pArchive, pRecord, 0x0000 /*data*/);
Assert(!pArchive->valMaskDataless || (!needFakeData && !needFakeRsrc));
if (needFakeData) {
err = Nu_FakeZeroExtract(pArchive, pRecord, kNuThreadKindDataFork);
BailError(err);
}
if (needFakeRsrc) {
err = Nu_FakeZeroExtract(pArchive, pRecord, kNuThreadKindRsrcFork);
BailError(err);
if (pRecord->recStorageType == kNuStorageExtended) {
err = Nu_FakeZeroExtract(pArchive, pRecord, 0x0002 /*rsrc*/);
BailError(err);
}
}
bail:

View File

@ -193,7 +193,10 @@ NuError Nu_ReadThreadHeaders(NuArchive* pArchive, NuRecord* pRecord,
NuError err = kNuErrNone;
NuThread* pThread;
long count;
Boolean hasData = false;
Boolean needFakeData, needFakeRsrc;
needFakeData = true;
needFakeRsrc = (pRecord->recStorageType == kNuStorageExtended);
Assert(pArchive != NULL);
Assert(pRecord != NULL);
@ -215,8 +218,13 @@ NuError Nu_ReadThreadHeaders(NuArchive* pArchive, NuRecord* pRecord,
err = Nu_ReadThreadHeader(pArchive, pThread, pCrc);
BailError(err);
if (pThread->thThreadClass == kNuThreadClassData)
hasData = true;
if (pThread->thThreadClass == kNuThreadClassData) {
if (pThread->thThreadKind == kNuThreadKindDataFork) {
needFakeData = false;
} else if (pThread->thThreadKind == kNuThreadKindRsrcFork) {
needFakeRsrc = false;
}
}
/*
* Some versions of ShrinkIt write an invalid thThreadEOF for disks,
@ -258,13 +266,14 @@ NuError Nu_ReadThreadHeaders(NuArchive* pArchive, NuRecord* pRecord,
* If "mask threadless" is set, create "fake" threads with empty
* data and resource forks as needed.
*/
if (!hasData && pArchive->valMaskDataless) {
Boolean needRsrc = (pRecord->recStorageType == kNuStorageExtended);
if ((needFakeData || needFakeRsrc) && pArchive->valMaskDataless) {
int firstNewThread = pRecord->recTotalThreads;
pRecord->recTotalThreads++;
pRecord->fakeThreads++;
if (needRsrc) {
if (needFakeData) {
pRecord->recTotalThreads++;
pRecord->fakeThreads++;
}
if (needFakeRsrc) {
pRecord->recTotalThreads++;
pRecord->fakeThreads++;
}
@ -275,22 +284,23 @@ NuError Nu_ReadThreadHeaders(NuArchive* pArchive, NuRecord* pRecord,
pThread = pRecord->pThreads + firstNewThread;
pThread->thThreadClass = kNuThreadClassData;
pThread->thThreadFormat = kNuThreadFormatUncompressed;
pThread->thThreadKind = 0x0000; /* data fork */
pThread->thThreadCRC = kNuInitialThreadCRC;
pThread->thThreadEOF = 0;
pThread->thCompThreadEOF = 0;
pThread->threadIdx = Nu_GetNextThreadIdx(pArchive);
pThread->actualThreadEOF = 0;
pThread->fileOffset = -99999999;
pThread->used = false;
if (needRsrc) {
pThread++;
if (needFakeData) {
pThread->thThreadClass = kNuThreadClassData;
pThread->thThreadFormat = kNuThreadFormatUncompressed;
pThread->thThreadKind = 0x0002; /* rsrc fork */
pThread->thThreadKind = kNuThreadKindDataFork;
pThread->thThreadCRC = kNuInitialThreadCRC;
pThread->thThreadEOF = 0;
pThread->thCompThreadEOF = 0;
pThread->threadIdx = Nu_GetNextThreadIdx(pArchive);
pThread->actualThreadEOF = 0;
pThread->fileOffset = -99999999;
pThread->used = false;
pThread++;
}
if (needFakeRsrc) {
pThread->thThreadClass = kNuThreadClassData;
pThread->thThreadFormat = kNuThreadFormatUncompressed;
pThread->thThreadKind = kNuThreadKindRsrcFork;
pThread->thThreadCRC = kNuInitialThreadCRC;
pThread->thThreadEOF = 0;
pThread->thCompThreadEOF = 0;

View File

@ -24,6 +24,15 @@ static const char kFssep = PATH_SEP;
#define kTempFile "exer-temp"
#ifndef HAVE_STRCASECMP
static int strcasecmp(const char *str1, const char *str2)
{
while (*str1 && *str2 && toupper(*str1) == toupper(*str2))
str1++, str2++;
return (toupper(*str1) - toupper(*str2));
}
#endif
/*
* ===========================================================================
@ -364,13 +373,25 @@ static NuError AbortFunc(ExerciserState* pState, int argc, char** argv)
static NuError AddFileFunc(ExerciserState* pState, int argc, char** argv)
{
NuFileDetails nuFileDetails;
int fromRsrc = false;
(void) pState, (void) argc, (void) argv; /* shut up, gcc */
assert(ExerciserState_GetNuArchive(pState) != NULL);
assert(argc == 2);
assert(argc == 3);
if (strcasecmp(argv[2], "true") == 0) {
fromRsrc = true;
} else if (strcasecmp(argv[2], "false") != 0) {
fprintf(stderr, "WARNING: fromRsrc should be 'true' or 'false'\n");
/* ignore */
}
memset(&nuFileDetails, 0, sizeof(nuFileDetails));
nuFileDetails.threadID = kNuThreadIDDataFork;
if (fromRsrc) {
nuFileDetails.threadID = kNuThreadIDRsrcFork;
} else {
nuFileDetails.threadID = kNuThreadIDDataFork;
}
nuFileDetails.storageNameMOR = argv[1];
nuFileDetails.fileSysID = kNuFileSysUnknown;
nuFileDetails.fileSysInfo = (short) kFssep;
@ -378,7 +399,7 @@ static NuError AddFileFunc(ExerciserState* pState, int argc, char** argv)
/* fileType, extraType, storageType, dates */
return NuAddFile(ExerciserState_GetNuArchive(pState), argv[1],
&nuFileDetails, false, NULL);
&nuFileDetails, fromRsrc, NULL);
}
/*
@ -1039,7 +1060,7 @@ static const struct {
{ "ab", AbortFunc, 0, "", kFlagArchiveReq,
"Abort current changes" },
{ "af", AddFileFunc, 1, "filename", kFlagArchiveReq,
{ "af", AddFileFunc, 2, "filename fromRsrc", kFlagArchiveReq,
"Add file" },
{ "ar", AddRecordFunc, 1, "storageName", kFlagArchiveReq,
"Add record" },

View File

@ -16,8 +16,7 @@
#include "Common.h"
#ifndef HAVE_STRCASECMP
static int
strcasecmp(const char *str1, const char *str2)
static int strcasecmp(const char *str1, const char *str2)
{
while (*str1 && *str2 && toupper(*str1) == toupper(*str2))
str1++, str2++;

View File

@ -298,7 +298,8 @@ int Test_AddStuff(NuArchive* pArchive)
/*
* Create an empty file with a rather non-empty name.
* Create an empty file with a rather non-empty name. Add it as
* a resource fork.
*/
printf("... add 'long' record\n");
err = NuCreateDataSourceForBuffer(kNuThreadFormatUncompressed,
@ -454,7 +455,8 @@ failed:
/*
* Selection callback filter for "test". This gets called once per record.
* Selection callback filter for "test". This gets called once per record,
* twice per record for forked files.
*/
NuResult VerifySelectionCallback(NuArchive* pArchive, void* vpProposal)
{
@ -523,7 +525,9 @@ int Test_Verify(NuArchive* pArchive)
goto failed;
}
if (count != kNumEntries) {
/* the count will be one higher than the number of records because
the last entry is forked, and each fork is tested separately */
if (count != kNumEntries + 1) {
fprintf(stderr, "ERROR: verified %ld when expecting %d\n", count,
kNumEntries);
goto failed;

View File

@ -184,6 +184,7 @@ CRCList* GatherCRCs(NuArchive* pArchive)
goto bail;
}
int rsrcCrcIdx = -1;
for (i = 0; i < (int)NuRecordGetNumThreads(pRecord); i++) {
pThread = NuGetThread(pRecord, i);
if (pThread->thThreadClass == kNuThreadClassData) {
@ -194,7 +195,30 @@ CRCList* GatherCRCs(NuArchive* pArchive)
goto bail;
}
pEntries[crcIdx++] = pThread->thThreadCRC;
/*
* Ensure that the data fork CRC comes first. Otherwise
* we can fail if it gets rearranged. This is only a
* problem for GSHK-created archives that don't have
* threads for every fork, so "mask dataless" is create
* fake entries.
*
* The correct way to do this is to store a tuple
* { thread-kind, crc }, but that's more work.
*/
if (pThread->thThreadKind == kNuThreadKindRsrcFork) {
rsrcCrcIdx = crcIdx;
}
if (pThread->thThreadKind == kNuThreadKindDataFork &&
rsrcCrcIdx != -1)
{
/* this is the data fork, we've already seen the
resource fork; swap entries */
pEntries[crcIdx++] = pEntries[rsrcCrcIdx];
pEntries[rsrcCrcIdx] = pThread->thThreadCRC;
} else {
pEntries[crcIdx++] = pThread->thThreadCRC;
}
}
}
}