nulib2/nulib2/ArcUtils.c

977 lines
28 KiB
C

/*
* NuLib2
* Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved.
* This is free software; you can redistribute it and/or modify it under the
* terms of the BSD License, see the file COPYING.
*
* Common archive-related utility functions.
*/
#include "NuLib2.h"
/*
* ===========================================================================
* Output pathnames
* ===========================================================================
*/
/*
* General-purpose output pathname filter, invoked by nufxlib via a
* callback. Normalizes the pathname to match the current OS requirements.
*
* If we're extracting to stdout, this fills in the "newFp" field instead.
*
* The buffer we return to the archive library will be overwritten the
* next time this function gets called. This is expected.
*/
static NuResult OutputPathnameFilter(NuArchive* pArchive, void* vproposal)
{
NuPathnameProposal* pathProposal = vproposal;
NulibState* pState;
const UNICHAR* newPathnameUNI;
NuRecordIdx renameFromIdx;
UNICHAR* renameToStrUNI;
UNICHAR* resultBufUNI;
Assert(pArchive != NULL);
(void) NuGetExtraData(pArchive, (void**) &pState);
Assert(pState != NULL);
/* handle extract-to-pipe */
if (NState_GetCommand(pState) == kCommandExtractToPipe) {
pathProposal->newDataSink = NState_GetPipeSink(pState);
NState_SetSuppressOutput(pState, true);
return kNuOK;
}
/*
* If they're trying to rename this file, do so now. We don't run
* the output through the normalizer; if the user typed it, assume
* that it's okay (and let the OS tell them if it isn't).
*/
renameToStrUNI = NState_GetRenameToStr(pState);
if (renameToStrUNI != NULL) {
renameFromIdx = NState_GetRenameFromIdx(pState);
if (renameFromIdx == pathProposal->pRecord->recordIdx) {
/* right source file, proceed with the rename */
NState_SetTempPathnameLen(pState, strlen(renameToStrUNI) +1);
resultBufUNI = NState_GetTempPathnameBuf(pState);
Assert(resultBufUNI != NULL);
strcpy(resultBufUNI, renameToStrUNI);
pathProposal->newPathnameUNI = resultBufUNI;
}
/* free up renameToStrUNI */
NState_SetRenameToStr(pState, NULL);
goto bail;
}
/*
* Convert the pathname into something suitable for the current OS.
*/
newPathnameUNI = NormalizePath(pState, pathProposal);
if (newPathnameUNI == NULL) {
ReportError(kNuErrNone, "unable to convert pathname");
return kNuAbort;
}
pathProposal->newPathnameUNI = newPathnameUNI;
bail:
return kNuOK;
}
/*
* ===========================================================================
* User input
* ===========================================================================
*/
/*
* Get a single character from the input.
*
* For portability, I'm just getting a line of input and keeping the
* first character. A fancier version would play with line disciplines
* so you wouldn't have to hit "return".
*/
static char GetReplyChar(char defaultReply)
{
char tmpBuf[32];
if (fgets(tmpBuf, sizeof(tmpBuf), stdin) == NULL)
return defaultReply;
if (tmpBuf[0] == '\n' || tmpBuf[0] == '\r')
return defaultReply;
return tmpBuf[0];
}
#define kMaxInputLen 128
/*
* Get a string back from the user.
*
* String returned should be freed by the caller.
*/
static char* GetReplyString(const char* prompt)
{
char buf[kMaxInputLen];
char* result;
int len;
printf("%s", prompt);
fflush(stdout);
result = fgets(buf, sizeof(buf), stdin);
if (result == NULL || feof(stdin) || ferror(stdin) || buf[0] == '\0' ||
buf[0] == '\n')
{
return NULL;
}
/* nuke the terminating '\n', which is lots of fun in filenames */
len = strlen(buf);
if (buf[len-1] == '\n')
buf[len-1] = '\0';
return strdup(buf);
}
/*
* Get a one-line comment from the user, of at most "maxLen" bytes.
*
* If the user enters a blank line, return "NULL".
*
* A pointer to a newly-allocated buffer is returned.
*/
char* GetSimpleComment(NulibState* pState, const char* pathname, int maxLen)
{
char* buf = NULL;
char* result;
int len;
buf = Malloc(maxLen);
if (buf == NULL)
return NULL;
printf("Enter one-line comment for '%s'\n: ", pathname);
fflush(stdout);
result = fgets(buf, maxLen, stdin);
if (result == NULL || feof(stdin) || ferror(stdin) || buf[0] == '\0' ||
buf[0] == '\n')
{
Free(buf);
return NULL;
}
/* nuke the terminating '\n', which we don't need */
len = strlen(buf);
if (buf[len-1] == '\n')
buf[len-1] = '\0';
return buf;
}
/*
* ===========================================================================
* Callbacks (progress updates, error handling, record selection)
* ===========================================================================
*/
#define kMaxDisplayLen 60
/*
* Returns "true" if the filespec in "spec" matches what's in "pRecord".
*
* (Someday "spec" might be a regexp.)
*/
static Boolean SpecMatchesRecord(NulibState* pState, const char* spec,
const NuRecord* pRecord)
{
#ifdef NU_CASE_SENSITIVE
if (NState_GetModRecurse(pState))
return (strncmp(spec, pRecord->filename, strlen(spec)) == 0);
else
return (strcmp(spec, pRecord->filename) == 0);
#else
if (NState_GetModRecurse(pState))
return (strncasecmp(spec, pRecord->filenameMOR, strlen(spec)) == 0);
else
return (strcasecmp(spec, pRecord->filenameMOR) == 0);
#endif
}
/*
* Determine whether the current record we're examining is described by
* the file specification given on the command line.
*
* If no filespec was provided, then all records are "specified".
*
* We pass the entire NuRecord in because we may want to allow
* extraction by criteria other than name, e.g. all text files or all
* files archived before a certain date.
*/
Boolean IsSpecified(NulibState* pState, const NuRecord* pRecord)
{
char* const* pSpec;
int i;
if (!NState_GetFilespecCount(pState))
return true;
pSpec = NState_GetFilespecPointer(pState);
for (i = NState_GetFilespecCount(pState); i > 0; i--, pSpec++) {
if (SpecMatchesRecord(pState, *pSpec, pRecord))
return true;
}
return false;
}
/*
* General-purpose selection filter, invoked as a callback. Compares the
* selection proposal with the filenames in "filespec".
*/
NuResult SelectionFilter(NuArchive* pArchive, void* vproposal)
{
const NuSelectionProposal* selProposal = vproposal;
NulibState* pState;
Assert(pArchive != NULL);
(void) NuGetExtraData(pArchive, (void**) &pState);
Assert(pState != NULL);
if (IsSpecified(pState, selProposal->pRecord)) {
NState_IncMatchCount(pState);
/* we don't get progress notifications for delete, so do it here */
if (NState_GetCommand(pState) == kCommandDelete) {
printf("Deleting %s\n", selProposal->pRecord->filenameMOR);
}
return kNuOK;
} else {
return kNuSkip;
}
}
/*
* Print a three-digit progress percentage; range is 0% to 100%.
*/
void PrintPercentage(uint32_t total, uint32_t progress)
{
uint32_t perc;
if (!total) {
/*printf(" %%");*/
printf(" ");
return;
}
if (total < 21474836) {
perc = (progress * 100 + 50) / total;
if (perc > 100)
perc = 100;
} else {
perc = progress / (total / 100);
if (perc > 100)
perc = 100;
}
printf("%3d%%", perc);
}
/*
* Show our progress, unless we're expanding to a pipe. Invoked as a
* callback by nufxlib.
*/
NuResult ProgressUpdater(NuArchive* pArchive, void* vProgress)
{
const NuProgressData* pProgress = vProgress;
NulibState* pState;
const char* percStr;
const char* actionStr;
char nameBuf[kMaxDisplayLen+1];
Boolean showName, eolConv;
Assert(pArchive != NULL);
(void) NuGetExtraData(pArchive, (void**) &pState);
Assert(pState != NULL);
if (NState_GetSuppressOutput(pState))
return kNuOK;
percStr = NULL;
showName = false;
eolConv = false;
switch (pProgress->operation) {
case kNuOpAdd:
switch (pProgress->state) {
case kNuProgressPreparing:
case kNuProgressOpening:
actionStr = "adding ";
showName = true;
break;
case kNuProgressAnalyzing:
actionStr = "analyzing ";
break;
case kNuProgressCompressing:
actionStr = "compressing";
break;
case kNuProgressStoring:
actionStr = "storing ";
break;
default:
actionStr = "?????? ";
break;
}
break;
case kNuOpExtract:
switch (pProgress->state) {
case kNuProgressPreparing:
case kNuProgressOpening:
actionStr = "extracting";
showName = true;
break;
case kNuProgressExpanding:
actionStr = "expanding ";
break;
case kNuProgressCopying:
actionStr = "extracting";
break;
default:
actionStr = "?????? ";
break;
}
if (pProgress->expand.convertEOL == kNuConvertOn)
eolConv = true;
break;
case kNuOpTest:
switch (pProgress->state) {
case kNuProgressPreparing:
case kNuProgressOpening:
showName = true;
/* no break */
case kNuProgressExpanding:
case kNuProgressCopying:
actionStr = "verifying";
break;
default:
actionStr = "?????? ";
break;
}
break;
default:
Assert(0);
actionStr = "????";
}
switch (pProgress->state) {
case kNuProgressDone:
actionStr = NULL;
percStr = "DONE\n";
break;
case kNuProgressSkipped:
actionStr = NULL;
percStr = "SKIP\n";
break;
case kNuProgressAborted: /* not currently possible in NuLib2 */
actionStr = NULL;
percStr = "CNCL\n";
break;
case kNuProgressFailed:
actionStr = NULL;
percStr = "FAIL\n";
break;
default:
break;
}
if (showName) {
/*
* Use "pathname" (whole thing) rather than "filename" (file part).
* Could also use "origPathname", but I like to show what they're
* getting instead of what they're giving.
*/
int len = strlen(pProgress->pathnameUNI);
if (len < sizeof(nameBuf)) {
strcpy(nameBuf, pProgress->pathnameUNI);
} else {
nameBuf[0] = nameBuf[1] = '.';
strncpy(nameBuf+2,
pProgress->pathnameUNI + len - (sizeof(nameBuf)-3),
sizeof(nameBuf)-3);
nameBuf[sizeof(nameBuf)-1] = '\0';
}
}
if (actionStr == NULL && percStr != NULL) {
printf("\r%s", percStr);
} else if (actionStr != NULL && percStr == NULL) {
if (percStr == NULL) {
putc('\r', stdout);
PrintPercentage(pProgress->uncompressedLength,
pProgress->uncompressedProgress);
if (showName)
printf(" %s%c %s", actionStr, eolConv ? '+' : ' ', nameBuf);
else
printf(" %s%c", actionStr, eolConv ? '+' : ' ');
}
} else {
Assert(0);
printf("????\n");
}
fflush(stdout);
/*printf(" %ld \n", pProgress->uncompressedProgress);*/
/*usleep(250000);*/
return kNuOK;
}
/*
* Decide whether or not to replace an existing file (during extract)
* or record (during add).
*/
static NuResult HandleReplaceExisting(NulibState* pState, NuArchive* pArchive,
const NuErrorStatus* pErrorStatus)
{
NuResult result = kNuOK;
char* renameName;
char reply;
Assert(pState != NULL);
Assert(pErrorStatus != NULL);
Assert(pErrorStatus->pathnameUNI != NULL);
Assert(pErrorStatus->canOverwrite);
Assert(pErrorStatus->canSkip);
Assert(pErrorStatus->canAbort);
if (NState_GetInputUnavailable(pState)) {
putc('\n', stdout);
ReportError(pErrorStatus->err, "Giving up");
result = kNuAbort;
goto bail;
}
while (1) {
printf("\n Replace %s? [y]es, [n]o, [A]ll, [N]one",
pErrorStatus->pathnameUNI);
if (pErrorStatus->canRename) /* renaming record adds not allowed */
printf(", [r]ename: ");
else
printf(": ");
fflush(stdout);
reply = GetReplyChar('n');
switch (reply) {
case 'y':
result = kNuOverwrite;
goto bail;
case 'n':
result = kNuSkip;
goto bail;
case 'A':
(void) NuSetValue(pArchive, kNuValueHandleExisting,
kNuAlwaysOverwrite);
result = kNuOverwrite;
goto bail;
case 'N':
(void) NuSetValue(pArchive, kNuValueHandleExisting,
kNuNeverOverwrite);
result = kNuSkip;
goto bail;
case 'r':
if (!pErrorStatus->canRename) {
printf("Response not acceptable\n");
break; /* continue in "while" loop */
}
renameName = GetReplyString("New name: ");
if (renameName == NULL)
break; /* continue in "while" loop */
if (pErrorStatus->pRecord == NULL) {
ReportError(kNuErrNone, "Unexpected NULL record");
break; /* continue in "while" loop */
}
NState_SetRenameFromIdx(pState,
pErrorStatus->pRecord->recordIdx);
NState_SetRenameToStr(pState, renameName);
result = kNuRename;
goto bail;
case 'q': /* stealth option to quit */
case 'Q':
result = kNuAbort;
goto bail;
default:
printf("Response not understood -- please use y/n/A/N/r\n");
break; /* continue in "while" loop */
}
}
bail:
return result;
}
/*
* Found a bad CRC... should we press onward?
*
* Note pErrorStatus->pathname may be NULL if the error was found in the
* master header or in the record header.
*/
static NuResult HandleBadCRC(NulibState* pState, NuArchive* pArchive,
const NuErrorStatus* pErrorStatus)
{
NuResult result = kNuOK;
char reply;
Assert(pState != NULL);
Assert(pErrorStatus != NULL);
if (NState_GetInputUnavailable(pState)) {
putc('\n', stderr);
ReportError(pErrorStatus->err, "Giving up");
result = kNuAbort;
goto bail;
}
while (1) {
if (pErrorStatus->pathnameUNI != NULL)
fprintf(stderr, "\n Found a bad CRC in %s\n",
pErrorStatus->pathnameUNI);
else
fprintf(stderr, "\n Found a bad CRC in the archive\n");
fprintf(stderr,
" Archive may be damaged, continue anyway? [y]es, [n]o: ");
fflush(stderr);
reply = GetReplyChar('n');
switch (reply) {
case 'y':
result = kNuIgnore;
goto bail;
case 'n':
case 'N':
result = kNuAbort;
goto bail;
default:
fprintf(stderr, "Response not understood -- please use y/n\n");
break; /* continue in "while" loop */
}
}
bail:
return result;
}
/*
* Tried to add a nonexistent file; continue?
*
* To make this happen, you need to:
* - nulib2 aer lots-of-files another-file
* - remove another-file while nulib2 is still processing lots-of-files
*
* The trick is to delete a file explicitly mentioned on the command line
* after NuFlush starts to do its thing. Otherwise, we're just using
* the system equivalent of readdir to scan a directory, so deleting a
* file just means it won't get added.
*/
static NuResult HandleAddNotFound(NulibState* pState, NuArchive* pArchive,
const NuErrorStatus* pErrorStatus)
{
NuResult result = kNuOK;
char reply;
Assert(pState != NULL);
Assert(pErrorStatus != NULL);
Assert(pErrorStatus->pathnameUNI != NULL);
if (NState_GetInputUnavailable(pState)) {
putc('\n', stdout);
ReportError(pErrorStatus->err, "Giving up");
result = kNuAbort;
goto bail;
}
while (1) {
fprintf(stderr, "\n Couldn't find %s, continue? [y]es, [n]o: ",
pErrorStatus->pathnameUNI);
fflush(stderr);
reply = GetReplyChar('n');
switch (reply) {
case 'y':
result = kNuSkip;
goto bail;
case 'n':
case 'N':
result = kNuAbort;
goto bail;
default:
fprintf(stderr, "Response not understood -- please use y/n\n");
break; /* continue in "while" loop */
}
}
bail:
return result;
}
/*
* Something failed, and the user may want to choose how to handle it.
* Invoked as a callback.
*/
NuResult ErrorHandler(NuArchive* pArchive, void* vErrorStatus)
{
const NuErrorStatus* pErrorStatus = vErrorStatus;
NulibState* pState;
NuResult result;
Assert(pArchive != NULL);
(void) NuGetExtraData(pArchive, (void**) &pState);
Assert(pState != NULL);
/* default action is to abort the current operation */
result = kNuAbort;
/*
* When extracting, the error handler callback gets invoked for several
* different problems because we might want to rename the file. Also,
* because extractions are done with "bulk" calls, returning an
* individual error message would be meaningless.
*
* When adding files, the NuAddFile and NuAddRecord calls can return
* immediate, specific results for a single add. The only reasons for
* calling here are to decide if an existing record should be replaced
* or not (without even an option to rename), or to decide what to do
* when the NuFlush call runs into a problem while adding a file.
*/
if (pErrorStatus->operation == kNuOpExtract) {
if (pErrorStatus->err == kNuErrFileExists) {
result = HandleReplaceExisting(pState, pArchive, pErrorStatus);
} else if (pErrorStatus->err == kNuErrNotNewer) {
/* if we were expecting this, it's okay */
if (NState_GetModFreshen(pState) || NState_GetModUpdate(pState)) {
printf("\rSKIP\n");
result = kNuSkip;
} else {
DBUG(("WEIRD one\n"));
}
} else if (pErrorStatus->err == kNuErrDuplicateNotFound) {
/* if we were expecting this, it's okay */
if (NState_GetModFreshen(pState)) {
printf("\rSKIP\n");
result = kNuSkip;
} else {
DBUG(("WEIRD two\n"));
}
}
} else if (pErrorStatus->operation == kNuOpAdd) {
if (pErrorStatus->err == kNuErrRecordExists) {
/* if they want to update or freshen, don't hassle them */
if (NState_GetModFreshen(pState) || NState_GetModUpdate(pState))
result = kNuOverwrite;
else
result = HandleReplaceExisting(pState, pArchive, pErrorStatus);
} else if (pErrorStatus->err == kNuErrFileNotFound) {
/* file was specified with NuAdd but removed during NuFlush */
result = HandleAddNotFound(pState, pArchive, pErrorStatus);
}
} else if (pErrorStatus->operation == kNuOpTest) {
if (pErrorStatus->err == kNuErrBadMHCRC ||
pErrorStatus->err == kNuErrBadRHCRC ||
pErrorStatus->err == kNuErrBadThreadCRC ||
pErrorStatus->err == kNuErrBadDataCRC)
{
result = HandleBadCRC(pState, pArchive, pErrorStatus);
}
}
return result;
}
#if 0
/*
* Display an error message.
*
* (This was just a test to see if it worked... NufxLib's default behavior
* is fine for NuLib2.)
*/
NuResult ErrorMessageHandler(NuArchive* pArchive, void* vErrorMessage)
{
const NuErrorMessage* pErrorMessage = (const NuErrorMessage*) vErrorMessage;
fprintf(stderr, "%s%d %3d %s:%d %s %s\n",
pArchive == NULL ? "(GLOBAL)" : "",
pErrorMessage->isDebug, pErrorMessage->err, pErrorMessage->file,
pErrorMessage->line, pErrorMessage->function, pErrorMessage->message);
return kNuOK;
}
#endif
/*
* ===========================================================================
* Open an archive
* ===========================================================================
*/
/* an archive name of "-" indicates we want to use stdin */
static const char* kStdinArchive = "-";
/*
* Determine whether the access bits on the record make it a read-only
* file or not.
*
* Uses a simplified view of the access flags.
*/
Boolean IsRecordReadOnly(const NuRecord* pRecord)
{
if (pRecord->recAccess == 0x21L || pRecord->recAccess == 0x01L)
return true;
else
return false;
}
/*
* Returns "true" if "archiveName" is the name we use to represent stdin.
*/
Boolean IsFilenameStdin(const char* archiveName)
{
Assert(archiveName != NULL);
return (strcmp(archiveName, kStdinArchive) == 0);
}
#define BailError(err) { if (err != kNuErrNone) goto bail; }
/*
* 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;
}
/*
* 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;
}