/* * gr - get record attributes */ static NuError GetRecordFunc(ExerciserState* pState, int argc, char** argv) { NuError err; const NuRecord* pRecord; (void) pState, (void) argc, (void) argv; /* shut up, gcc */ assert(ExerciserState_GetNuArchive(pState) != nil); assert(argc == 2); err = NuGetRecord(ExerciserState_GetNuArchive(pState), strtol(argv[1], nil, 0), &pRecord); if (err == kNuErrNone) { printf("Exerciser: success, call returned:\n"); printf("\tfileSysID : %d\n", pRecord->recFileSysID); printf("\tfileSysInfo : 0x%04x ('%c')\n", pRecord->recFileSysInfo, NuGetSepFromSysInfo(pRecord->recFileSysInfo)); printf("\taccess : 0x%02lx\n", pRecord->recAccess); printf("\tfileType : 0x%04lx\n", pRecord->recFileType); printf("\textraType : 0x%04lx\n", pRecord->recExtraType); printf("\tcreateWhen : ...\n"); printf("\tmodWhen : ...\n"); /* too lazy */ printf("\tarchiveWhen : ...\n"); } return err; }
/* * Extract a thread from the archive as part of a "bulk" extract operation. * * Streaming archives must be properly positioned. */ NuError Nu_ExtractThreadBulk(NuArchive* pArchive, const NuRecord* pRecord, const NuThread* pThread) { NuError err; NuDataSink* pDataSink = NULL; UNICHAR* recFilenameStorageUNI = NULL; NuValue eolConv; /* * Create a file data sink for the file. We use whatever EOL conversion * is set as the default for the entire archive. (If you want to * specify your own EOL conversion for each individual file, you will * need to extract them individually, creating a data sink for each.) * * One exception: we turn EOL conversion off for disk image threads. * It's *very* unlikely this would be desirable, and could be a problem * if the user is extracting a collection of disks and files. */ eolConv = pArchive->valConvertExtractedEOL; if (NuGetThreadID(pThread) == kNuThreadIDDiskImage) eolConv = kNuConvertOff; recFilenameStorageUNI = Nu_CopyMORToUNI(pRecord->filenameMOR); err = Nu_DataSinkFile_New(true, eolConv, recFilenameStorageUNI, NuGetSepFromSysInfo(pRecord->recFileSysInfo), &pDataSink); BailError(err); err = Nu_ExtractThreadCommon(pArchive, pRecord, pThread, pDataSink); BailError(err); bail: if (pDataSink != NULL) { NuError err2 = Nu_DataSinkFree(pDataSink); if (err == kNuErrNone) err = err2; } Nu_Free(pArchive, recFilenameStorageUNI); return err; }
/* * Extract the specified thread to "pDataSink". If the sink is to a file, * this will take care of opening (and, if appropriate, creating) the file. * * If we're operating on a streaming archive, the file pointer must be * positioned at the start of the thread's data. If not, it will be * seeked appropriately. * * This calls the "should we extract" and "what pathname should we use" * filters for every thread, which means we can reject specific kinds * of forks and/or give them different names. This is a good thing. */ static NuError Nu_ExtractThreadCommon(NuArchive* pArchive, const NuRecord* pRecord, const NuThread* pThread, NuDataSink* pDataSink) { NuError err = kNuErrNone; NuSelectionProposal selProposal; NuPathnameProposal pathProposal; NuProgressData progressData; NuProgressData* pProgressData; NuDataSink* pOrigDataSink; UNICHAR* newPathStorageUNI = NULL; UNICHAR* recFilenameStorageUNI = NULL; const UNICHAR* newPathnameUNI; NuResult result; uint8_t newFssep; Boolean doFreeSink = false; Assert(pRecord != NULL); Assert(pThread != NULL); Assert(pDataSink != NULL); memset(&progressData, 0, sizeof(progressData)); pProgressData = NULL; /* * If we're just trying to verify the archive contents, create a * data sink that goes nowhere at all. */ if (pArchive->testMode) { err = Nu_DataSinkVoid_New( Nu_DataSinkGetDoExpand(pDataSink), Nu_DataSinkGetConvertEOL(pDataSink), &pDataSink); BailError(err); doFreeSink = true; } pOrigDataSink = pDataSink; /* save a copy for the "retry" loop */ /* * Decide if we want to extract this thread. This is mostly for * use by the "bulk" extract, not the per-thread extract, but it * still applies if they so desire. */ if (pArchive->selectionFilterFunc != NULL) { selProposal.pRecord = pRecord; selProposal.pThread = pThread; result = (*pArchive->selectionFilterFunc)(pArchive, &selProposal); if (result == kNuSkip) return Nu_SkipThread(pArchive, pRecord, pThread); if (result == kNuAbort) { err = kNuErrAborted; goto bail; } } newPathnameUNI = NULL; newFssep = 0; recFilenameStorageUNI = Nu_CopyMORToUNI(pRecord->filenameMOR); retry_name: if (Nu_DataSinkGetType(pDataSink) == kNuDataSinkToFile) { /* * We're extracting. Figure out the name of the file to write it to. * If they want to use the sleazy FILE* back door, create a new * data sink and use that instead. * * Start by resetting everything to defaults, in case this isn't * our first time through the "rename" loop. */ newPathnameUNI = Nu_DataSinkFile_GetPathname(pDataSink); newFssep = Nu_DataSinkFile_GetFssep(pDataSink); pDataSink = pOrigDataSink; /* if they don't have a pathname func defined, we just use default */ if (pArchive->outputPathnameFunc != NULL) { pathProposal.pathnameUNI = recFilenameStorageUNI; pathProposal.filenameSeparator = NuGetSepFromSysInfo(pRecord->recFileSysInfo); pathProposal.pRecord = pRecord; pathProposal.pThread = pThread; pathProposal.newPathnameUNI = NULL; pathProposal.newFilenameSeparator = '\0'; /*pathProposal.newStorage = (NuThreadID)-1;*/ pathProposal.newDataSink = NULL; result = (*pArchive->outputPathnameFunc)(pArchive, &pathProposal); if (result == kNuSkip) return Nu_SkipThread(pArchive, pRecord, pThread); if (result == kNuAbort) { err = kNuErrAborted; goto bail; } /* we don't own this string, so make a copy */ if (pathProposal.newPathnameUNI != NULL) { Nu_Free(pArchive, newPathStorageUNI); newPathStorageUNI = strdup(pathProposal.newPathnameUNI); newPathnameUNI = newPathStorageUNI; } else { newPathnameUNI = NULL; } if (pathProposal.newFilenameSeparator != '\0') newFssep = pathProposal.newFilenameSeparator; /* if they want to send this somewhere else, let them */ if (pathProposal.newDataSink != NULL) pDataSink = pathProposal.newDataSink; } /* at least one of these must be set */ Assert(!(newPathnameUNI == NULL && pathProposal.newDataSink == NULL)); } /* * Prepare the progress data if this is a data thread. */ if (newPathnameUNI == NULL) { /* using a data sink; get the pathname out of the record */ newPathnameUNI = recFilenameStorageUNI; newFssep = NuGetSepFromSysInfo(pRecord->recFileSysInfo); } if (pThread->thThreadClass == kNuThreadClassData) { pProgressData = &progressData; err = Nu_ProgressDataInit_Expand(pArchive, pProgressData, pRecord, newPathnameUNI, newFssep, recFilenameStorageUNI, Nu_DataSinkGetConvertEOL(pOrigDataSink)); BailError(err); /* send initial progress so they see the right name if "open" fails */ pProgressData->state = kNuProgressOpening; err = Nu_SendInitialProgress(pArchive, pProgressData); BailError(err); } if (Nu_DataSinkGetType(pDataSink) == kNuDataSinkToFile) { /* * We're extracting to a file. Open it, creating it if necessary and * allowed. */ FILE* fileFp = NULL; err = Nu_OpenOutputFile(pArchive, pRecord, pThread, newPathnameUNI, newFssep, &fileFp); if (err == kNuErrRename) { /* they want to rename; the OutputPathname callback handles this */ Nu_Free(pArchive, newPathStorageUNI); newPathStorageUNI = NULL; /* reset these just to be careful */ newPathnameUNI = NULL; fileFp = NULL; goto retry_name; } else if (err != kNuErrNone) { goto bail; } Assert(fileFp != NULL); (void) Nu_DataSinkFile_SetFP(pDataSink, fileFp); DBUG(("+++ EXTRACTING 0x%08lx from '%s' at offset %0ld to '%s'\n", NuMakeThreadID(pThread->thThreadClass, pThread->thThreadKind), pRecord->filename, pThread->fileOffset, newPathname)); } else { DBUG(("+++ EXTRACTING 0x%08lx from '%s' at offset %0ld to sink\n", NuMakeThreadID(pThread->thThreadClass, pThread->thThreadKind), pRecord->filename, pThread->fileOffset)); } /* extract to the file */ err = Nu_ExtractThreadToDataSink(pArchive, pRecord, pThread, pProgressData, pDataSink); BailError(err); if (Nu_DataSinkGetType(pDataSink) == kNuDataSinkToFile) { /* * Close the file, adjusting the modification date and access * permissions as appropriate. */ err = Nu_CloseOutputFile(pArchive, pRecord, Nu_DataSinkFile_GetFP(pDataSink), newPathnameUNI); Nu_DataSinkFile_SetFP(pDataSink, NULL); BailError(err); } bail: if (err != kNuErrNone && pProgressData != NULL) { /* send a final progress message, indicating failure */ if (err == kNuErrSkipped) pProgressData->state = kNuProgressSkipped; else if (err == kNuErrAborted) pProgressData->state = kNuProgressAborted; else pProgressData->state = kNuProgressFailed; (void) Nu_SendInitialProgress(pArchive, pProgressData); } /* if this was an ordinary file, and it's still open, close it */ if (Nu_DataSinkGetType(pDataSink) == kNuDataSinkToFile) Nu_DataSinkFile_Close(pDataSink); Nu_Free(pArchive, newPathStorageUNI); Nu_Free(pArchive, recFilenameStorageUNI); if (doFreeSink) Nu_DataSinkFree(pDataSink); return err; }