/* * Find an existing thread somewhere in the archive. If the "copy" set * exists it will be searched. If not, the "orig" set is searched, and * if an entry is found a "copy" set will be created. * * The record and thread returned will always be from the "copy" set. An * error result is returned if the record and thread aren't found. */ static NuError Nu_FindThreadForWriteByIdx(NuArchive* pArchive, NuThreadIdx threadIdx, NuRecord** ppFoundRecord, NuThread** ppFoundThread) { NuError err; if (Nu_RecordSet_GetLoaded(&pArchive->copyRecordSet)) { err = Nu_RecordSet_FindByThreadIdx(&pArchive->copyRecordSet, threadIdx, ppFoundRecord, ppFoundThread); } else { Assert(Nu_RecordSet_GetLoaded(&pArchive->origRecordSet)); err = Nu_RecordSet_FindByThreadIdx(&pArchive->origRecordSet, threadIdx, ppFoundRecord, ppFoundThread); *ppFoundThread = NULL; /* can't delete from here, wipe ptr */ } BailError(err); /* * The thread exists. If we were looking in the "orig" set, we have * to create a "copy" set, and delete it from that. */ if (*ppFoundThread == NULL) { err = Nu_RecordSet_Clone(pArchive, &pArchive->copyRecordSet, &pArchive->origRecordSet); BailError(err); err = Nu_RecordSet_FindByThreadIdx(&pArchive->copyRecordSet, threadIdx, ppFoundRecord, ppFoundThread); Assert(err == kNuErrNone && *ppFoundThread != NULL); /* must succeed */ BailError(err); } bail: return err; }
PicHandle QTEffects_GetPosterPictFromFirstVideoTrackInMovieFile (FSSpec *theSpec) { PicHandle myPict = NULL; Movie myMovie = NULL; Track myTrack = NULL; short myRefNum = 0; OSErr myErr = noErr; myErr = OpenMovieFile(theSpec, &myRefNum, fsRdPerm); BailError(myErr); myErr = NewMovieFromFile(&myMovie, myRefNum, NULL, NULL, 0, NULL); BailError(myErr); SetMoviePlayHints(myMovie, hintsHighQuality, hintsHighQuality); myErr = CloseMovieFile(myRefNum); BailError(myErr); myErr = QTEffects_GetFirstVideoTrackInMovie(myMovie, &myTrack); BailNil(myTrack); myPict = GetTrackPict(myTrack, GetMoviePosterTime(myMovie)); bail: if (myMovie != NULL) DisposeMovie(myMovie); return(myPict); }
/* * Extract a thread, given the IDs and a data sink. */ NuError Nu_ExtractThread(NuArchive* pArchive, NuThreadIdx threadIdx, NuDataSink* pDataSink) { NuError err; NuRecord* pRecord; NuThread* pThread; if (Nu_IsStreaming(pArchive)) return kNuErrUsage; if (threadIdx == 0 || pDataSink == NULL) return kNuErrInvalidArg; err = Nu_GetTOCIfNeeded(pArchive); BailError(err); /* find the correct record and thread by index */ err = Nu_RecordSet_FindByThreadIdx(&pArchive->origRecordSet, threadIdx, &pRecord, &pThread); BailError(err); Assert(pRecord != NULL); /* extract away */ err = Nu_ExtractThreadCommon(pArchive, pRecord, pThread, pDataSink); BailError(err); bail: return err; }
/* * Determine if it's okay to add a thread of the type specified by * "threadID" into "pRecord". * * Returns with an error (kNuErrThreadAdd) if it's not okay. */ NuError Nu_OkayToAddThread(NuArchive* pArchive, const NuRecord* pRecord, NuThreadID threadID) { NuError err = kNuErrNone; /* * Check for class conflicts (can't mix data and control threads). */ if (NuThreadIDGetClass(threadID) == kNuThreadClassData) { err = Nu_FindNoFutureThreadClass(pArchive, pRecord, kNuThreadClassControl); BailError(err); } else if (NuThreadIDGetClass(threadID) == kNuThreadClassControl) { err = Nu_FindNoFutureThreadClass(pArchive, pRecord, kNuThreadClassData); BailError(err); } /* * Check for specific type conflicts. */ if (threadID == kNuThreadIDDataFork) { err = Nu_FindNoFutureThread(pArchive, pRecord, kNuThreadIDDataFork); BailError(err); err = Nu_FindNoFutureThread(pArchive, pRecord, kNuThreadIDDiskImage); BailError(err); } else if (threadID == kNuThreadIDRsrcFork) { err = Nu_FindNoFutureThread(pArchive, pRecord, kNuThreadIDRsrcFork); BailError(err); err = Nu_FindNoFutureThread(pArchive, pRecord, kNuThreadIDDiskImage); BailError(err); } else if (threadID == kNuThreadIDDiskImage) { err = Nu_FindNoFutureThread(pArchive, pRecord, kNuThreadIDDataFork); BailError(err); err = Nu_FindNoFutureThread(pArchive, pRecord, kNuThreadIDRsrcFork); BailError(err); err = Nu_FindNoFutureThread(pArchive, pRecord, kNuThreadIDDiskImage); BailError(err); } else if (threadID == kNuThreadIDFilename) { err = Nu_FindNoFutureThread(pArchive, pRecord, kNuThreadIDFilename); BailError(err); } bail: return err; }
/* * Delete an individual thread. * * You aren't allowed to delete threads that have been updated. Deleting * newly-added threads isn't possible, since they aren't really threads yet. * * Don't worry about deleting filename threads here; we take care of that * later on. Besides, it's sort of handy to hang on to the filename for * as long as possible. */ NuError Nu_DeleteThread(NuArchive* pArchive, NuThreadIdx threadIdx) { NuError err; NuThreadMod* pThreadMod = NULL; NuRecord* pFoundRecord; NuThread* pFoundThread; if (Nu_IsReadOnly(pArchive)) return kNuErrArchiveRO; err = Nu_GetTOCIfNeeded(pArchive); BailError(err); /* * Find the thread in the "copy" set. (If there isn't a copy set, * make one.) */ err = Nu_FindThreadForWriteByIdx(pArchive, threadIdx, &pFoundRecord, &pFoundThread); BailError(err); /* * Deletion of modified threads (updates or previous deletes) isn't * allowed. Deletion of threads from deleted records can't happen, * because deleted records are completely removed from the "copy" set. */ if (Nu_ThreadMod_FindByThreadIdx(pFoundRecord, threadIdx) != NULL) { DBUG(("--- Tried to delete a deleted or modified thread\n")); err = kNuErrModThreadChange; goto bail; } /* * Looks good. Add a new "delete" ThreadMod to the list. */ err = Nu_ThreadModDelete_New(pArchive, threadIdx, NuGetThreadID(pFoundThread), &pThreadMod); BailError(err); Nu_RecordAddThreadMod(pFoundRecord, pThreadMod); pThreadMod = NULL; /* successful, don't free */ bail: Nu_ThreadModFree(pArchive, pThreadMod); 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 thread to the specified file pointer. * * If the archive is a stream, the stream must be positioned at the * start of pThread's data. If not, it will be seeked first. */ static NuError Nu_ExtractThreadToDataSink(NuArchive* pArchive, const NuRecord* pRecord, const NuThread* pThread, NuProgressData* pProgress, NuDataSink* pDataSink) { NuError err; NuFunnel* pFunnel = NULL; /* if it's not a stream, seek to the appropriate spot in the file */ if (!Nu_IsStreaming(pArchive)) { err = Nu_SeekArchive(pArchive, pArchive->archiveFp, pThread->fileOffset, SEEK_SET); if (err != kNuErrNone) { Nu_ReportError(NU_BLOB, err, "Unable to seek input to %ld", pThread->fileOffset); goto bail; } } /* * Set up an output funnel to write to. */ err = Nu_FunnelNew(pArchive, pDataSink, Nu_DataSinkGetConvertEOL(pDataSink), pArchive->valEOL, pProgress, &pFunnel); BailError(err); /* * Write it. */ err = Nu_ExpandStream(pArchive, pRecord, pThread, pArchive->archiveFp, pFunnel); if (err != kNuErrNone) { if (err != kNuErrSkipped && err != kNuErrAborted) Nu_ReportError(NU_BLOB, err, "ExpandStream failed"); goto bail; } bail: (void) Nu_FunnelFree(pArchive, pFunnel); return err; }
/* * Write the thread headers for the record at the current file position. * * Note this doesn't care whether a thread was "fake" or not. In * effect, we promote all threads to "real" status. We update the * "fake" count in pRecord accordingly. */ NuError Nu_WriteThreadHeaders(NuArchive* pArchive, NuRecord* pRecord, FILE* fp, uint16_t* pCrc) { NuError err = kNuErrNone; NuThread* pThread; int idx; for (idx = 0; idx < (int)pRecord->recTotalThreads; idx++) { pThread = Nu_GetThread(pRecord, idx); Assert(pThread != NULL); err = Nu_WriteThreadHeader(pArchive, pThread, fp, pCrc); BailError(err); } if (pRecord->fakeThreads != 0) { DBUG(("+++ promoting %ld fake threads to real\n",pRecord->fakeThreads)); pRecord->fakeThreads = 0; } bail: 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; }
/* * Skip past some or all of the thread data in the archive. For file * archives, we scan all the threads, but for streaming archives we only * want to scan up to the filename thread. (If the filename thread comes * after one of the data threads, we have a problem!) * * The tricky part here is that we don't want to skip over a filename * thread. We actually want to read it in, so that we have something to * show to the application. (Someday I'll get AndyN for putting me * through this...) */ NuError Nu_ScanThreads(NuArchive* pArchive, NuRecord* pRecord, long numThreads) { NuError err = kNuErrNone; NuThread* pThread; FILE* fp; Assert(pArchive != NULL); Assert(pRecord != NULL); fp = pArchive->archiveFp; Assert(numThreads <= (long)pRecord->recTotalThreads); pThread = pRecord->pThreads; while (numThreads--) { if (pRecord->threadFilenameMOR == NULL && NuMakeThreadID(pThread->thThreadClass, pThread->thThreadKind) == kNuThreadIDFilename) { /* it's the first filename thread, read the whole thing */ if (pThread->thCompThreadEOF > kNuReasonableFilenameLen) { err = kNuErrBadRecord; Nu_ReportError(NU_BLOB, err, "Bad thread filename len (%u)", pThread->thCompThreadEOF); goto bail; } pRecord->threadFilenameMOR = Nu_Malloc(pArchive, pThread->thCompThreadEOF +1); BailAlloc(pRecord->threadFilenameMOR); /* note there is no CRC on a filename thread */ (void) Nu_ReadBytes(pArchive, fp, pRecord->threadFilenameMOR, pThread->thCompThreadEOF); if ((err = Nu_HeaderIOFailed(pArchive, fp)) != kNuErrNone) { Nu_ReportError(NU_BLOB, err, "Failed reading filename thread"); goto bail; } /* null-terminate on the actual len, not the buffer len */ pRecord->threadFilenameMOR[pThread->thThreadEOF] = '\0'; Nu_StripHiIfAllSet(pRecord->threadFilenameMOR); /* prefer this one over the record one, but only one should exist */ if (pRecord->filenameMOR != NULL) { DBUG(("--- HEY: got record filename and thread filename\n")); } pRecord->filenameMOR = pRecord->threadFilenameMOR; } else { /* not a filename (or not first filename), skip past it */ err = Nu_SeekArchive(pArchive, pArchive->archiveFp, pThread->thCompThreadEOF, SEEK_CUR); BailError(err); } pThread++; } /* * Should've had one by now. Supposedly, older versions of ShrinkIt * wouldn't prompt for a disk image name on DOS 3.3 volumes, so you'd * end up with a disk image that had no name attached. This will tend * to confuse things, so we go ahead and give it a name. */ if (pRecord->filenameMOR == NULL) { DBUG(("+++ no filename found, using default record name\n")); pRecord->filenameMOR = kNuDefaultRecordName; } pArchive->currentOffset += pRecord->totalCompLength; if (!Nu_IsStreaming(pArchive)) { Assert(pArchive->currentOffset == ftell(pArchive->archiveFp)); } bail: return err; }
/* * Read the threads from the current archive file position. * * The storage for the threads is allocated here, in one block. We could * have used a linked list like NuLib, but that doesn't really provide any * benefit for us, and adds complexity. */ NuError Nu_ReadThreadHeaders(NuArchive* pArchive, NuRecord* pRecord, uint16_t* pCrc) { NuError err = kNuErrNone; NuThread* pThread; long count; Boolean needFakeData, needFakeRsrc; needFakeData = true; needFakeRsrc = (pRecord->recStorageType == kNuStorageExtended); Assert(pArchive != NULL); Assert(pRecord != NULL); Assert(pCrc != NULL); if (!pRecord->recTotalThreads) { /* not sure if this is reasonable, but we can handle it */ DBUG(("--- WEIRD: no threads in the record?\n")); goto bail; } pRecord->pThreads = Nu_Malloc(pArchive, pRecord->recTotalThreads * sizeof(NuThread)); BailAlloc(pRecord->pThreads); count = pRecord->recTotalThreads; pThread = pRecord->pThreads; while (count--) { err = Nu_ReadThreadHeader(pArchive, pThread, pCrc); BailError(err); if (pThread->thThreadClass == kNuThreadClassData) { if (pThread->thThreadKind == kNuThreadKindDataFork) { needFakeData = false; } else if (pThread->thThreadKind == kNuThreadKindRsrcFork) { needFakeRsrc = false; } else if (pThread->thThreadKind == kNuThreadKindDiskImage) { /* needFakeRsrc shouldn't be set, but clear anyway */ needFakeData = needFakeRsrc = false; } } /* * Some versions of ShrinkIt write an invalid thThreadEOF for disks, * so we have to figure out what it's supposed to be. */ if (NuMakeThreadID(pThread->thThreadClass, pThread->thThreadKind) == kNuThreadIDDiskImage) { if (pRecord->recStorageType <= 13) { /* supposed to be block size, but SHK v3.0.1 stored it wrong */ pThread->actualThreadEOF = pRecord->recExtraType * 512; } else if (pRecord->recStorageType == 256 && pRecord->recExtraType == 280 && pRecord->recFileSysID == kNuFileSysDOS33) { /* * Fix for less-common ShrinkIt problem: looks like an old * version of GS/ShrinkIt used 256 as the block size when * compressing DOS 3.3 images from 5.25" disks. If that * appears to be the case here, crank up the block size. */ DBUG(("--- no such thing as a 70K disk image!\n")); pThread->actualThreadEOF = pRecord->recExtraType * 512; } else { pThread->actualThreadEOF = pRecord->recExtraType * pRecord->recStorageType; } } else { pThread->actualThreadEOF = pThread->thThreadEOF; } pThread->used = false; pThread++; } /* * If "mask threadless" is set, create "fake" threads with empty * data and resource forks as needed. */ if ((needFakeData || needFakeRsrc) && pArchive->valMaskDataless) { int firstNewThread = pRecord->recTotalThreads; if (needFakeData) { pRecord->recTotalThreads++; pRecord->fakeThreads++; } if (needFakeRsrc) { pRecord->recTotalThreads++; pRecord->fakeThreads++; } pRecord->pThreads = Nu_Realloc(pArchive, pRecord->pThreads, pRecord->recTotalThreads * sizeof(NuThread)); BailAlloc(pRecord->pThreads); pThread = pRecord->pThreads + firstNewThread; if (needFakeData) { pThread->thThreadClass = kNuThreadClassData; pThread->thThreadFormat = kNuThreadFormatUncompressed; 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; pThread->threadIdx = Nu_GetNextThreadIdx(pArchive); pThread->actualThreadEOF = 0; pThread->fileOffset = -99999999; pThread->used = false; } } bail: return err; }
/* * Open the archive in read-write mode, for purposes of adding, deleting, * or updating files. We don't plan on extracting anything with this. * * "Streaming mode" isn't allowed. */ NuError OpenArchiveReadWrite(NulibState* pState) { NuError err = kNuErrNone; NuArchive* pArchive = NULL; char* tempName = NULL; Assert(pState != NULL); Assert(IsFilenameStdin(NState_GetArchiveFilename(pState)) == false); tempName = MakeTempArchiveName(pState); if (tempName == NULL) goto bail; DBUG(("TEMP NAME = '%s'\n", tempName)); err = NuOpenRW(NState_GetArchiveFilename(pState), tempName, kNuOpenCreat, &pArchive); if (err != kNuErrNone) { ReportError(err, "unable to open '%s'", NState_GetArchiveFilename(pState)); goto bail; } /* introduce them */ NState_SetNuArchive(pState, pArchive); err = NuSetExtraData(pArchive, pState); BailError(err); NuSetSelectionFilter(pArchive, SelectionFilter); NuSetProgressUpdater(pArchive, ProgressUpdater); NuSetErrorHandler(pArchive, ErrorHandler); /*NuSetErrorMessageHandler(pArchive, ErrorMessageHandler);*/ /* handle "-0" flag */ if (NState_GetModNoCompression(pState)) { err = NuSetValue(pArchive, kNuValueDataCompression, kNuCompressNone); BailError(err); } /* handle "-z" flag */ if (NState_GetModCompressDeflate(pState)) { err = NuSetValue(pArchive, kNuValueDataCompression, kNuCompressDeflate); BailError(err); } /* handle "-zz" flag */ if (NState_GetModCompressBzip2(pState)) { err = NuSetValue(pArchive, kNuValueDataCompression, kNuCompressBzip2); BailError(err); } /* handle "-f" and "-u" flags */ /* (BUG: if "-f" is set, creating a new archive is impossible) */ if (NState_GetModFreshen(pState) || NState_GetModUpdate(pState)) { err = NuSetValue(pArchive, kNuValueOnlyUpdateOlder, true); BailError(err); } if (NState_GetModFreshen(pState)) { err = NuSetValue(pArchive, kNuValueHandleExisting, kNuMustOverwrite); BailError(err); } DBUG(("--- enabling ShrinkIt compatibility mode\n")); err = NuSetValue(pArchive, kNuValueMimicSHK, true); BailError(err); /* this probably isn't needed here, but set it anyway */ if (strcmp(SYSTEM_DEFAULT_EOL, "\r") == 0) err = NuSetValue(pArchive, kNuValueEOL, kNuEOLCR); else if (strcmp(SYSTEM_DEFAULT_EOL, "\n") == 0) err = NuSetValue(pArchive, kNuValueEOL, kNuEOLLF); else if (strcmp(SYSTEM_DEFAULT_EOL, "\r\n") == 0) err = NuSetValue(pArchive, kNuValueEOL, kNuEOLCRLF); else { Assert(0); err = kNuErrInternal; ReportError(err, "Unknown SYSTEM_DEFAULT_EOL '%s'", SYSTEM_DEFAULT_EOL); goto bail; } BailError(err); /*(void) NuSetValue(pArchive, kNuValueAllowDuplicates, true);*/ bail: Free(tempName); if (err != kNuErrNone && pArchive != NULL) { /* clean up */ NuAbort(pArchive); (void) NuClose(pArchive); NState_SetNuArchive(pState, NULL); } return err; }
/* * Add a new thread to a record. * * In some cases, you aren't allowed to add a thread whose type matches * one that already exists. This applies to data threads and filenames, * but not to comments, control threads, or IIgs icons. You also can't * add a disk image thread when there are data-class threads, or vice-versa. * * This is the first and last place we do this sort of checking. If * an illegal situation gets past this function, it will either get * caught with a fatal assert or (if NDEBUG is defined) not at all. * * On success, the NuThreadIdx of the newly-created record will be placed * in "*pThreadIdx", and "pDataSource" will be owned by NufxLib. */ NuError Nu_AddThread(NuArchive* pArchive, NuRecordIdx recIdx, NuThreadID threadID, NuDataSource* pDataSource, NuThreadIdx* pThreadIdx) { NuError err; NuRecord* pRecord; NuThreadMod* pThreadMod = NULL; NuThreadFormat threadFormat; /* okay for pThreadIdx to be NULL */ if (recIdx == 0 || pDataSource == NULL) return kNuErrInvalidArg; if (Nu_IsReadOnly(pArchive)) return kNuErrArchiveRO; err = Nu_GetTOCIfNeeded(pArchive); BailError(err); /* * Find the record. If it doesn't exist in the copy set, check to * see if it's in the "new" set. */ err = Nu_FindRecordForWriteByIdx(pArchive, recIdx, &pRecord); if (err == kNuErrRecIdxNotFound && Nu_RecordSet_GetLoaded(&pArchive->newRecordSet)) { err = Nu_RecordSet_FindByIdx(&pArchive->newRecordSet, recIdx, &pRecord); } BailError(err); Assert(pRecord != NULL); /* * Do some tests, looking for specific types of threads that conflict * with what we're trying to add. */ err = Nu_OkayToAddThread(pArchive, pRecord, threadID); BailError(err); /* * Decide if we want to compress the data from this source. If the * data is already compressed (as indicated by the data source) or * this type of thread isn't compressible (e.g. it's a filename), then * we don't compress it. Otherwise, we use whatever compression mode * is currently configured. */ if (Nu_DataSourceGetThreadFormat(pDataSource) == kNuThreadFormatUncompressed && Nu_IsCompressibleThreadID(threadID)) { threadFormat = Nu_ConvertCompressValToFormat(pArchive, pArchive->valDataCompression); } else { threadFormat = kNuThreadFormatUncompressed; } DBUG(("--- using threadFormat = %d\n", threadFormat)); /* create a new ThreadMod (which makes a copy of the data source) */ err = Nu_ThreadModAdd_New(pArchive, threadID, threadFormat, pDataSource, &pThreadMod); BailError(err); Assert(pThreadMod != NULL); /* add the thread mod to the record */ Nu_RecordAddThreadMod(pRecord, pThreadMod); if (pThreadIdx != NULL) *pThreadIdx = pThreadMod->entry.add.threadIdx; pThreadMod = NULL; /* successful, don't free */ /* * If we've got a header filename and we're adding a filename thread, * we don't want to write the record header name when we reconstruct * the record. */ if (threadID == kNuThreadIDFilename && pRecord->recFilenameLength) { DBUG(("+++ gonna drop the filename\n")); pRecord->dropRecFilename = true; } bail: if (pThreadMod != NULL) Nu_ThreadModFree(pArchive, pThreadMod); if (err == kNuErrNone && pDataSource != NULL) { /* on success, we have ownership of the data source. ThreadMod made its own copy, so get rid of this one */ Nu_DataSourceFree(pDataSource); } return err; }
void QTEffects_RespondToDialogSelection (OSErr theErr) { Boolean myDialogWasCancelled = false; short myResID = movieInDataForkResID; UInt16 myMovieIter; short mySrcMovieRefNum = 0; Movie myPrevSrcMovie = NULL; Track myPrevSrcTrack = NULL; Movie myNextSrcMovie = NULL; Track myNextSrcTrack = NULL; short myDestMovieRefNum = 0; FSSpec myFile; Boolean myIsSelected = false; Boolean myIsReplacing = false; StringPtr myPrompt = QTUtils_ConvertCToPascalString(kEffectsSaveMoviePrompt); StringPtr myFileName = QTUtils_ConvertCToPascalString(kEffectsSaveMovieFileName); Movie myDestMovie = NULL; Fixed myDestMovieWidth, myDestMovieHeight; ImageDescriptionHandle myDesc = NULL; Track videoTrackFX, videoTrackA, videoTrackB; Media videoMediaFX, videoMediaA, videoMediaB; TimeValue myCurrentDuration = 0; TimeValue myReturnedDuration; Boolean isFirstTransition = true; TimeValue myMediaTransitionDuration; TimeValue myMediaFXStartTime, myMediaFXDuration; OSType myEffectCode; long myFlags = createMovieFileDeleteCurFile | createMovieFileDontCreateResFile; long myLong; OSErr myErr = noErr; // standard parameter box has been dismissed, so remember that fact gEffectsDialog = 0L; myDialogWasCancelled = (theErr == userCanceledErr); // we're finished with the effect list and movie posters QTDisposeAtomContainer(gEffectList); if (gPosterA != NULL) KillPicture(gPosterA); if (gPosterB != NULL) KillPicture(gPosterB); // when the sign says stop, then stop if (myDialogWasCancelled) goto bail; // add atoms naming the sources to gEffectSample myLong = EndianU32_NtoB(kSourceOneName); QTInsertChild(gEffectSample, kParentAtomIsContainer, kEffectSourceName, 1, 0, sizeof(myLong), &myLong, NULL); myLong = EndianU32_NtoB(kSourceTwoName); QTInsertChild(gEffectSample, kParentAtomIsContainer, kEffectSourceName, 2, 0, sizeof(myLong), &myLong, NULL); // extract the 'what' atom to find out what kind of effect it is { QTAtom myEffectAtom; QTAtomID myEffectAtomID; long myEffectCodeSize; Ptr myEffectCodePtr; myEffectAtom = QTFindChildByIndex(gEffectSample, kParentAtomIsContainer, kParameterWhatName, kParameterWhatID, &myEffectAtomID); myErr = QTLockContainer(gEffectSample); BailError(myErr); myErr = QTGetAtomDataPtr(gEffectSample, myEffectAtom, &myEffectCodeSize, &myEffectCodePtr); BailError(myErr); if (myEffectCodeSize != sizeof(OSType)) { myErr = paramErr; goto bail; } myEffectCode = *(OSType *)myEffectCodePtr; // "tsk" myEffectCode = EndianU32_BtoN(myEffectCode); // because the data is read from an atom container myErr = QTUnlockContainer(gEffectSample); BailError(myErr); } // ask the user for the name of the new movie file QTFrame_PutFile(myPrompt, myFileName, &myFile, &myIsSelected, &myIsReplacing); if (!myIsSelected) goto bail; // deal with user cancelling // create a movie file for the destination movie myErr = CreateMovieFile(&myFile, FOUR_CHAR_CODE('TVOD'), 0, myFlags, &myDestMovieRefNum, &myDestMovie); BailError(myErr); // open the first file as a movie; call the first movie myPrevSrcMovie myErr = OpenMovieFile(&gSpecList[0], &mySrcMovieRefNum, fsRdPerm); BailError(myErr); myErr = NewMovieFromFile(&myPrevSrcMovie, mySrcMovieRefNum, NULL, NULL, 0, NULL); BailError(myErr); myErr = CloseMovieFile(mySrcMovieRefNum); BailError(myErr); // if the movie is shorter than kMinimumDuration, scale it to that length SetMovieTimeScale(myPrevSrcMovie, kTimeScale); myErr = QTEffects_GetFirstVideoTrackInMovie(myPrevSrcMovie, &myPrevSrcTrack); BailNil(myPrevSrcTrack); if (GetTrackDuration(myPrevSrcTrack) < kMinimumDuration) { myErr = ScaleTrackSegment(myPrevSrcTrack, 0, GetTrackDuration(myPrevSrcTrack), kMinimumDuration); BailError(myErr); } // find out how big the first movie is; we'll use it as the size of all our tracks GetTrackDimensions(myPrevSrcTrack, &myDestMovieWidth, &myDestMovieHeight); #if USES_MAKE_IMAGE_DESC_FOR_EFFECT // create a new sample description for the effect, // which is just an image description specifying the effect and its dimensions myErr = MakeImageDescriptionForEffect(myEffectCode, &myDesc); if (myErr != noErr) BailError(myErr); #else // create a new sample description for the effect, // which is just an image description specifying the effect and its dimensions myDesc = (ImageDescriptionHandle)NewHandleClear(sizeof(ImageDescription)); BailNil(myDesc); (**myDesc).idSize = sizeof(ImageDescription); (**myDesc).cType = myEffectCode; (**myDesc).hRes = 72L << 16; (**myDesc).vRes = 72L << 16; (**myDesc).dataSize = 0L; (**myDesc).frameCount = 1; (**myDesc).depth = 0; (**myDesc).clutID = -1; #endif // fill in the fields of the sample description (**myDesc).vendor = kAppleManufacturer; (**myDesc).temporalQuality = codecNormalQuality; (**myDesc).spatialQuality = codecNormalQuality; (**myDesc).width = FixRound(myDestMovieWidth); (**myDesc).height = FixRound(myDestMovieHeight); // add three video tracks to the destination movie: // - videoTrackFX is where the effects and stills live; it's user-visible. // - videoTrackA is where the "source A"s for effects live; it's hidden by the input map // - videoTrackB is where the "source B"s for effects live; it's hidden by the input map videoTrackFX = NewMovieTrack(myDestMovie, myDestMovieWidth, myDestMovieHeight, 0); BailNil(videoTrackFX); videoMediaFX = NewTrackMedia(videoTrackFX, VideoMediaType, kTimeScale, NULL, 0); BailNil(videoMediaFX); myErr = BeginMediaEdits(videoMediaFX); BailError(myErr); videoTrackA = NewMovieTrack(myDestMovie, myDestMovieWidth, myDestMovieHeight, 0); BailNil(videoTrackA); videoMediaA = NewTrackMedia(videoTrackA, VideoMediaType, kTimeScale, NULL, 0); BailNil(videoMediaA); videoTrackB = NewMovieTrack(myDestMovie, myDestMovieWidth, myDestMovieHeight, 0); BailNil(videoTrackB); videoMediaB = NewTrackMedia(videoTrackB, VideoMediaType, kTimeScale, NULL, 0); BailNil(videoMediaB); // create the input map { long myRefIndex1, myRefIndex2; QTAtomContainer myInputMap; QTAtom myInputAtom; OSType myInputType; QTNewAtomContainer(&myInputMap); // first input if (videoTrackA) { AddTrackReference(videoTrackFX, videoTrackA, kTrackModifierReference, &myRefIndex1); QTInsertChild(myInputMap, kParentAtomIsContainer, kTrackModifierInput, myRefIndex1, 0, 0, NULL, &myInputAtom); myInputType = EndianU32_NtoB(kTrackModifierTypeImage); QTInsertChild(myInputMap, myInputAtom, kTrackModifierType, 1, 0, sizeof(myInputType), &myInputType, NULL); myLong = EndianU32_NtoB(kSourceOneName); QTInsertChild(myInputMap, myInputAtom, kEffectDataSourceType, 1, 0, sizeof(myLong), &myLong, NULL); } // second input if (videoTrackB) { AddTrackReference(videoTrackFX, videoTrackB, kTrackModifierReference, &myRefIndex2); QTInsertChild(myInputMap, kParentAtomIsContainer, kTrackModifierInput, myRefIndex2, 0, 0, NULL, &myInputAtom); myInputType = EndianU32_NtoB(kTrackModifierTypeImage); QTInsertChild(myInputMap, myInputAtom, kTrackModifierType, 1, 0, sizeof(myInputType), &myInputType, NULL); myLong = EndianU32_NtoB(kSourceTwoName); QTInsertChild(myInputMap, myInputAtom, kEffectDataSourceType, 1, 0, sizeof(myLong), &myLong, NULL); } // set that map SetMediaInputMap(GetTrackMedia(videoTrackFX), myInputMap); QTDisposeAtomContainer(myInputMap); } myCurrentDuration = 0; #if MAKE_STILL_SECTIONS // copy the first sample of the first video track of the first movie to videoTrackFX, with duration kStillDuration. myErr = CopyPortionOfTrackToTrack(myPrevSrcTrack, eStartPortion + eMiddlePortion, videoTrackFX, myCurrentDuration, &myReturnedDuration); BailError(myErr); myCurrentDuration += myReturnedDuration; #endif // now process any remaining files myMovieIter = 1; while (myMovieIter < gSpecCount) { // open the next file as a movie; call it nextSourceMovie myErr = OpenMovieFile(&gSpecList[myMovieIter], &mySrcMovieRefNum, fsRdPerm); BailError(myErr); myErr = NewMovieFromFile(&myNextSrcMovie, mySrcMovieRefNum, NULL, NULL, 0, NULL); BailError(myErr); // we're done with the movie file, so close it myErr = CloseMovieFile(mySrcMovieRefNum); BailError(myErr); // if the movie is shorter than kMinimumDuration, scale it to that length SetMovieTimeScale(myNextSrcMovie, kTimeScale); myErr = QTEffects_GetFirstVideoTrackInMovie(myNextSrcMovie, &myNextSrcTrack); BailNil(myNextSrcTrack); if (GetTrackDuration(myNextSrcTrack) < kMinimumDuration) { myErr = ScaleTrackSegment(myNextSrcTrack, 0, GetTrackDuration(myNextSrcTrack), kMinimumDuration); BailError(myErr); } // create a transition effect from the previous source movie's first video sample to the next source movie's first video sample // (the effect should have duration kEffectDuration); // this involves adding one sample to each of the three video tracks: // sample from previous source movie -> videoTrackA myErr = QTEffects_CopyPortionOfTrackToTrack(myPrevSrcTrack, eFinishPortion, videoTrackA, myCurrentDuration, &myReturnedDuration); BailError(myErr); // sample from next source movie -> videoTrackB myErr = QTEffects_CopyPortionOfTrackToTrack(myNextSrcTrack, eStartPortion, videoTrackB, myCurrentDuration, &myReturnedDuration); BailError(myErr); // effect sample -> videoTrackFX if (isFirstTransition) { myMediaTransitionDuration = myReturnedDuration; myMediaFXStartTime = GetMediaDuration(videoMediaFX); myErr = AddMediaSample(videoMediaFX, gEffectSample, 0, GetHandleSize(gEffectSample), myMediaTransitionDuration, (SampleDescriptionHandle)myDesc, 1, 0, NULL); BailError(myErr); myMediaFXDuration = GetMediaDuration(videoMediaFX) - myMediaFXStartTime; isFirstTransition = false; } myErr = InsertMediaIntoTrack(videoTrackFX, myCurrentDuration, myMediaFXStartTime, myMediaFXDuration, FixRatio(myReturnedDuration, myMediaTransitionDuration)); BailError(myErr); myCurrentDuration += myReturnedDuration; #if MAKE_STILL_SECTIONS // copy the first video sample of myNextSrcMovie to videoTrackFX, with duration kStillDuration. myErr = QTEffects_CopyPortionOfTrackToTrack(myNextSrcTrack, eMiddlePortion + (myMovieIter + 1 == theSpecCount) ? eFinishPortion : 0, videoTrackFX, myCurrentDuration, &myReturnedDuration); BailError(myErr); myCurrentDuration += myReturnedDuration; #endif // MAKE_STILL_SECTIONS // dispose of previous source movie. DisposeMovie(myPrevSrcMovie); myPrevSrcMovie = myNextSrcMovie; myPrevSrcTrack = myNextSrcTrack; myNextSrcMovie = NULL; myNextSrcTrack = NULL; myMovieIter++; } // while myErr = EndMediaEdits(videoMediaFX); BailError(myErr); myErr = AddMovieResource(myDestMovie, myDestMovieRefNum, &myResID, "\pMovie 1"); BailError(myErr); CloseMovieFile(myDestMovieRefNum); if (myPrevSrcMovie != NULL) DisposeMovie(myPrevSrcMovie); DisposeMovie(myDestMovie); bail: free(myPrompt); free(myFileName); QTDisposeAtomContainer(gEffectSample); DisposeHandle((Handle)myDesc); return; }
OSErr QTEffects_DisplayDialogForSources (FSSpec *theSpecList, UInt16 theSpecCount) { OSErr myErr = noErr; // make sure that there are enough sources: you can't make an omelette without enough eggs if (theSpecCount < 2) { myErr = paramErr; goto bail; } // assign source count to a global, so QTEffects_RespondToDialogSelection has access to it gSpecCount = theSpecCount; // get a poster frame for the first two movies if (theSpecCount >= 1) gPosterA = QTEffects_GetPosterPictFromFirstVideoTrackInMovieFile(&theSpecList[0]); if (theSpecCount >= 2) gPosterB = QTEffects_GetPosterPictFromFirstVideoTrackInMovieFile(&theSpecList[1]); // ask the user to select a two-source effect myErr = QTNewAtomContainer(&gEffectSample); BailError(myErr); myErr = QTGetEffectsList(&gEffectList, 2, 2, 0); // min == max == 2 BailError(myErr); myErr = QTCreateStandardParameterDialog(gEffectList, gEffectSample, 0, &gEffectsDialog); BailError(myErr); // insert poster frames into dialog if (gPosterA != NULL) { QTParamPreviewRecord pr; pr.sourcePicture = gPosterA; pr.sourceID = 1; QTStandardParameterDialogDoAction(gEffectsDialog, pdActionSetPreviewPicture, &pr); } if (gPosterB != NULL) { QTParamPreviewRecord pr; pr.sourcePicture = gPosterB; pr.sourceID = 2; QTStandardParameterDialogDoAction(gEffectsDialog, pdActionSetPreviewPicture, &pr); } // now, the frontmost window is the standard effects parameter dialog box; // on the Mac, we call QTEffects_HandleEffectsDialogEvents in our main event loop // to find and process events targeted at the effects parameter dialog box; on Windows, // we need to use a different strategy: we install a modeless dialog callback procedure // that is called internally by QTML #if TARGET_OS_WIN32 gDoneWithDialog = false; // force the dialog box to be drawn { EventRecord myEvent = {0}; QTEffects_EffectsDialogCallback(&myEvent, FrontWindow(), 0); } SetModelessDialogCallbackProc(FrontWindow(), (QTModelessCallbackUPP)QTEffects_EffectsDialogCallback); QTMLSetWindowWndProc(FrontWindow(), QTEffects_CustomDialogWndProc); #endif bail: return(myErr); }
/* * Update the contents of a pre-sized thread, such as a filename or * comment thread. * * The data from the source must fit within the limits of the existing * thread. The source data is never compressed, and must not come from * a compressed source. * * You aren't allowed to update threads that have been deleted. Updating * newly-added threads isn't possible, since they aren't really threads yet. */ NuError Nu_UpdatePresizedThread(NuArchive* pArchive, NuThreadIdx threadIdx, NuDataSource* pDataSource, int32_t* pMaxLen) { NuError err; NuThreadMod* pThreadMod = NULL; NuRecord* pFoundRecord; NuThread* pFoundThread; if (pDataSource == NULL) { err = kNuErrInvalidArg; goto bail; } /* presized threads always contain uncompressed data */ if (Nu_DataSourceGetThreadFormat(pDataSource) != kNuThreadFormatUncompressed) { err = kNuErrBadFormat; Nu_ReportError(NU_BLOB, err, "presized threads can't hold compressed data"); goto bail; } if (Nu_IsReadOnly(pArchive)) return kNuErrArchiveRO; err = Nu_GetTOCIfNeeded(pArchive); BailError(err); /* * Find the thread in the "copy" set. (If there isn't a copy set, * make one.) */ err = Nu_FindThreadForWriteByIdx(pArchive, threadIdx, &pFoundRecord, &pFoundThread); BailError(err); if (!Nu_IsPresizedThreadID(NuGetThreadID(pFoundThread)) || !(pFoundThread->thCompThreadEOF >= pFoundThread->thThreadEOF)) { err = kNuErrNotPreSized; Nu_ReportError(NU_BLOB, err, "invalid thread for update"); goto bail; } if (pMaxLen != NULL) *pMaxLen = pFoundThread->thCompThreadEOF; /* * Check to see if somebody is trying to delete this, or has already * updated it. */ if (Nu_ThreadMod_FindByThreadIdx(pFoundRecord, threadIdx) != NULL) { DBUG(("--- Tried to modify a deleted or modified thread\n")); err = kNuErrModThreadChange; goto bail; } /* * Verify that "otherLen" in the data source is less than or equal * to our len, if we can. If the data source is a file on disk, * we're not really supposed to look at it until we flush. We * could sneak a peek right now, which would prevent us from aborting * the entire operation when it turns out the file won't fit, but * that violates our semantics (and besides, the application really * should've done that already). * * If the data source is from a file, we just assume it'll fit and * let the chips fall where they may later on. */ if (Nu_DataSourceGetType(pDataSource) != kNuDataSourceFromFile) { if (pFoundThread->thCompThreadEOF < Nu_DataSourceGetOtherLen(pDataSource)) { err = kNuErrPreSizeOverflow; Nu_ReportError(NU_BLOB, err, "can't put %u bytes into %u", Nu_DataSourceGetOtherLen(pDataSource), pFoundThread->thCompThreadEOF); goto bail; } /* check for zero-length and excessively long filenames */ if (NuGetThreadID(pFoundThread) == kNuThreadIDFilename && (Nu_DataSourceGetOtherLen(pDataSource) == 0 || Nu_DataSourceGetOtherLen(pDataSource) > kNuReasonableFilenameLen)) { err = kNuErrInvalidFilename; Nu_ReportError(NU_BLOB, err, "invalid filename (%u bytes)", Nu_DataSourceGetOtherLen(pDataSource)); goto bail; } } /* * Looks like it'll fit, and it's the right kind of data. Create * an "update" threadMod. Note this copies the data source. */ Assert(pFoundThread->thThreadFormat == kNuThreadFormatUncompressed); err = Nu_ThreadModUpdate_New(pArchive, threadIdx, pDataSource, &pThreadMod); BailError(err); Assert(pThreadMod != NULL); /* add the thread mod to the record */ Nu_RecordAddThreadMod(pFoundRecord, pThreadMod); /* * NOTE: changes to filename threads will be picked up later and * incorporated into the record's threadFilename. We don't worry * about the record header filename, because we might be doing an * update-in-place and that prevents us from removing the filename * (doing so would change the size of the archive). No need to * do any filename-specific changes here. */ bail: return err; }
/* * Open the archive in read-only mode. We use "file mode" for a file, or * "streaming mode" for stdin. */ NuError OpenArchiveReadOnly(NulibState* pState) { NuError err; NuArchive* pArchive = NULL; Assert(pState != NULL); if (IsFilenameStdin(NState_GetArchiveFilename(pState))) { err = NuStreamOpenRO(stdin, &pArchive); if (err != kNuErrNone) { ReportError(err, "unable to open stdin archive"); if (err == kNuErrIsBinary2) err = kNuErrNotNuFX; /* we can't seek back, so forget BNY */ goto bail; } /* * Since the archive is on stdin, we can't ask the user questions. * On a UNIX system we could open /dev/tty, but that's not portable, * and I don't think archives on stdin are going to be popular * enough to make this worth doing. */ NState_SetInputUnavailable(pState, true); } else { err = NuOpenRO(NState_GetArchiveFilename(pState), &pArchive); if (err != kNuErrNone) { if (err != kNuErrIsBinary2) { ReportError(err, "unable to open '%s'", NState_GetArchiveFilename(pState)); } goto bail; } } /* introduce them */ NState_SetNuArchive(pState, pArchive); err = NuSetExtraData(pArchive, pState); NuSetSelectionFilter(pArchive, SelectionFilter); NuSetOutputPathnameFilter(pArchive, OutputPathnameFilter); NuSetProgressUpdater(pArchive, ProgressUpdater); NuSetErrorHandler(pArchive, ErrorHandler); /*NuSetErrorMessageHandler(pArchive, ErrorMessageHandler);*/ /* set the EOL conversion */ if (NState_GetModConvertAll(pState)) err = NuSetValue(pArchive, kNuValueConvertExtractedEOL, kNuConvertOn); else if (NState_GetModConvertText(pState)) err = NuSetValue(pArchive, kNuValueConvertExtractedEOL, kNuConvertAuto); else err = NuSetValue(pArchive, kNuValueConvertExtractedEOL, kNuConvertOff); BailError(err); /* if we're converting EOL, we probably ought to do this too */ err = NuSetValue(pArchive, kNuValueStripHighASCII, true); BailError(err); /* handle "-s" flag */ if (NState_GetModOverwriteExisting(pState)) { err = NuSetValue(pArchive, kNuValueHandleExisting, kNuAlwaysOverwrite); BailError(err); } /* handle "-f" and "-u" flags (this overrides "-s" during extraction) */ if (NState_GetModFreshen(pState) || NState_GetModUpdate(pState)) { err = NuSetValue(pArchive, kNuValueOnlyUpdateOlder, true); BailError(err); } if (NState_GetModFreshen(pState)) { err = NuSetValue(pArchive, kNuValueHandleExisting, kNuMustOverwrite); BailError(err); } DBUG(("--- enabling ShrinkIt compatibility mode\n")); err = NuSetValue(pArchive, kNuValueMimicSHK, true); BailError(err); /* handy for some malformed archives */ err = NuSetValue(pArchive, kNuValueHandleBadMac, true); BailError(err); /* DBUG(("--- enabling 'mask dataless' mode\n")); err = NuSetValue(pArchive, kNuValueMaskDataless, true); BailError(err); */ if (strcmp(SYSTEM_DEFAULT_EOL, "\r") == 0) err = NuSetValue(pArchive, kNuValueEOL, kNuEOLCR); else if (strcmp(SYSTEM_DEFAULT_EOL, "\n") == 0) err = NuSetValue(pArchive, kNuValueEOL, kNuEOLLF); else if (strcmp(SYSTEM_DEFAULT_EOL, "\r\n") == 0) err = NuSetValue(pArchive, kNuValueEOL, kNuEOLCRLF); else { Assert(0); err = kNuErrInternal; ReportError(err, "Unknown SYSTEM_DEFAULT_EOL '%s'", SYSTEM_DEFAULT_EOL); goto bail; } BailError(err); bail: if (err != kNuErrNone && pArchive != NULL) { /* clean up */ (void) NuClose(pArchive); NState_SetNuArchive(pState, NULL); } return err; }