cosmopolitan/third_party/infozip/zip/macos/source/macos.c
2021-06-22 12:38:56 -07:00

1079 lines
25 KiB
C

/*
Copyright (c) 1990-1999 Info-ZIP. All rights reserved.
See the accompanying file LICENSE, version 1999-Oct-05 or later
(the contents of which are also included in zip.h) for terms of use.
If, for some reason, both of these files are missing, the Info-ZIP license
also may be found at: ftp://ftp.cdrom.com/pub/infozip/license.html
*/
/*---------------------------------------------------------------------------
macos.c
Macintosh-specific routines for use with Info-ZIP's Zip 2.3 and later.
---------------------------------------------------------------------------*/
/*****************************************************************************/
/* Includes */
/*****************************************************************************/
#include "zip.h"
#include "revision.h"
#include "crypt.h"
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sound.h>
#include <unistd.h>
#include <Strings.h>
#include <setjmp.h>
/* #include "charmap.h" */
#include "helpers.h"
#include "macstuff.h"
#include "pathname.h"
#include "recurse.h"
/*****************************************************************************/
/* Macros, typedefs */
/*****************************************************************************/
#define PATH_END MacPathEnd
/*****************************************************************************/
/* Global Vars */
/*****************************************************************************/
int error_level; /* used only in ziperr() */
/* Note: sizeof() returns the size of this allusion
13 is current length of "XtraStuf.mac:" */
extern const char ResourceMark[13]; /* var is initialized in file pathname.c */
extern jmp_buf EnvForExit;
MacZipGlobals MacZip;
unsigned long count_of_Zippedfiles = 0;
/*****************************************************************************/
/* Module level Vars */
/*****************************************************************************/
static const char MacPathEnd = ':'; /* the Macintosh dir separator */
/* Inform Progress vars */
long estTicksToFinish;
long createTime;
long updateTicks;
static char *Time_Est_strings[] = {
"Zipping Files; Items done:",
"More than 24 hours",
"More than %s hours",
"About %s hours, %s minutes",
"About an hour",
"Less than an hour",
"About %s minutes, %s seconds",
"About a minute",
"Less than a minute",
"About %s seconds",
"About a second",
"About 1 minute, %s seconds"};
/*****************************************************************************/
/* Prototypes */
/*****************************************************************************/
int DoCurrentDir(void);
void DoAboutBox(void);
void DoQuit(void);
void DoEventLoop(void);
void ZipInitAllVars(void);
void UserStop(void);
Boolean IsZipFile(char *name);
static long EstimateCompletionTime(const long progressMax,
const long progressSoFar, unsigned char percent);
static void UpdateTimeToComplete(void);
#ifdef USE_SIOUX
#include <sioux.h>
void DoWarnUserDupVol( char *FullPath );
/*****************************************************************************/
/* Functions */
/*****************************************************************************/
/*
** Standalone Unzip with Metrowerks SIOUX starts here
**
*/
int main(int argc, char **argv)
{
int return_code;
SIOUXSettings.asktosaveonclose = FALSE;
SIOUXSettings.showstatusline = TRUE;
SIOUXSettings.columns = 100;
SIOUXSettings.rows = 40;
/* 30 = MacZip Johnny Lee's; 40 = new (my) MacZip */
MacZip.MacZipMode = NewZipMode_EF;
argc = ccommand(&argv);
if (verbose) PrintArguments(argc, argv);
ZipInitAllVars();
return_code = zipmain(argc, argv);
if (verbose) printf("\n\n Finish");
return return_code;
}
/*
** SIOUX needs no extra event handling
**
*/
void UserStop(void)
{
};
/*
** Password enter function '*' printed for each char
**
*/
int macgetch(void)
{
WindowPtr whichWindow;
EventRecord theEvent;
char c; /* one-byte buffer for read() to use */
do {
SystemTask();
if (!GetNextEvent(everyEvent, &theEvent))
theEvent.what = nullEvent;
else {
switch (theEvent.what) {
case keyDown:
c = theEvent.message & charCodeMask;
break;
case mouseDown:
if (FindWindow(theEvent.where, &whichWindow) ==
inSysWindow)
SystemClick(&theEvent, whichWindow);
break;
case updateEvt:
break;
}
}
} while (theEvent.what != keyDown);
printf("*");
fflush(stdout);
return (int)c;
}
#endif
/******************************/
/* Function version_local() */
/******************************/
/*
** Print Compilers version and compile time/date
**
*/
void version_local()
{
/* prints e.g:
Compiled with Metrowerks CodeWarrior version 2000 for PowerPC Processor
compile time: Feb 4 1998 17:49:49.
*/
static ZCONST char CompiledWith[] =
"\n\nCompiled with %s %x for %s \n %s %s %s.\n\n";
printf(CompiledWith,
#ifdef __MWERKS__
" Metrowerks CodeWarrior version", __MWERKS__,
#endif
#ifdef __MC68K__
" MC68K Processor",
#else
" PowerPC Processor",
#endif
#ifdef __DATE__
"compile time: ", __DATE__, __TIME__
#else
"", "", ""
#endif
);
} /* end function version_local() */
/*
** Deletes a dir if the switch '-m' is used
**
*/
int deletedir(char *path)
{
static char Num = 0;
static FSSpec trashfolder;
static Boolean FirstCall = true;
static Boolean Immediate_File_Deletion = false;
OSErr err;
FSSpec dirToDelete;
char currpath[NAME_MAX], *envptr;
CInfoPBRec fpb;
/* init this function */
if ((path == NULL) ||
(strlen(path) == 0))
{
Num = 0;
FirstCall = true;
return -1;
}
UserStop();
GetCompletePath(currpath,path,&dirToDelete, &err);
if (FirstCall == true)
{
FirstCall = false;
envptr = getenv("Immediate_File_Deletion");
if (!(envptr == (char *)NULL || *envptr == '\0'))
{
if (stricmp(envptr,"yes") == 0)
Immediate_File_Deletion = true;
else
Immediate_File_Deletion = false;
}
err = FSpFindFolder(dirToDelete.vRefNum, kTrashFolderType,
kDontCreateFolder,&trashfolder);
printerr("FSpFindFolder:",err,err,__LINE__,__FILE__,path);
}
fpb.dirInfo.ioNamePtr = dirToDelete.name;
fpb.dirInfo.ioVRefNum = dirToDelete.vRefNum;
fpb.dirInfo.ioDrDirID = dirToDelete.parID;
fpb.dirInfo.ioFDirIndex = 0;
err = PBGetCatInfoSync(&fpb);
printerr("PBGetCatInfo deletedir ", err, err,
__LINE__, __FILE__, "");
if (fpb.dirInfo.ioDrNmFls > 0)
{
return 0; /* do not move / delete folders which are not empty */
}
if (Immediate_File_Deletion)
{
err = FSpDelete(&dirToDelete);
return err;
}
err = CatMove (dirToDelete.vRefNum, dirToDelete.parID,
dirToDelete.name, trashfolder.parID, trashfolder.name);
/* -48 = file is already existing so we have to rename it before
moving the file */
if (err == -48)
{
Num++;
if (dirToDelete.name[0] >= 28) /* cut foldername if to long */
dirToDelete.name[0] = 28;
P2CStr(dirToDelete.name);
sprintf(currpath,"%s~%d",(char *)dirToDelete.name,Num);
C2PStr(currpath);
C2PStr((char *)dirToDelete.name);
err = HRename (dirToDelete.vRefNum, dirToDelete.parID,
dirToDelete.name, (unsigned char *) currpath);
err = CatMove (dirToDelete.vRefNum, dirToDelete.parID,
(unsigned char *) currpath, trashfolder.parID,
trashfolder.name);
}
return err;
}
/*
** Set the file-type so the archive will get the correct icon, type
** and creator code.
*/
void setfiletype(char *new_f, unsigned long Creator, unsigned long Type)
{
OSErr err;
if (strcmp(zipfile, new_f) == 0)
err = FSpChangeCreatorType(&MacZip.ZipFileSpec, Creator, Type);
printerr("FSpChangeCreatorType:", err, err, __LINE__, __FILE__, new_f);
return;
}
/*
** Convert the external (native) filename into Zip's internal Unix compatible
** name space.
*/
char *ex2in(char *externalFilen, int isdir, int *pdosflag)
/* char *externalFilen external file name */
/* int isdir input: externalFilen is a directory */
/* int *pdosflag output: force MSDOS file attributes? */
/* Convert the external file name to a zip file name, returning the malloc'ed
string or NULL if not enough memory. */
{
char *internalFilen; /* internal file name (malloc'ed) */
char *t; /* shortened name */
char *Pathname;
char buffer[NAME_MAX];
int dosflag;
AssertStr(externalFilen, externalFilen)
AssertBool(isdir,"")
dosflag = dosify; /* default for non-DOS and non-OS/2 */
/* Find starting point in name before doing malloc */
for (t = externalFilen; *t == PATH_END; t++)
;
if (!MacZip.StoreFullPath)
{
Pathname = StripPartialDir(buffer, MacZip.SearchDir,t);
}
else
{
Pathname = t;
}
/* Make changes, if any, to the copied name (leave original intact) */
if (!pathput)
{
t = last(Pathname, PATH_END);
}
else t = Pathname;
/* Malloc space for internal name and copy it */
if ((internalFilen = malloc(strlen(t) + 10 + strlen(ResourceMark) )) == NULL)
return NULL;
sstrcpy(internalFilen, t);
/* we have to eliminate illegal chars:
* The name space for Mac filenames and Zip filenames (unix style names)
* do both include all printable extended-ASCII characters. The only
* difference we have to take care of is the single special character
* used as path delimiter:
* ':' on MacOS and '/' on Unix and '\' on Dos.
* So, to convert between Mac filenames and Unix filenames without any
* loss of information, we simply interchange ':' and '/'. Additionally,
* we try to convert the coding of the extended-ASCII characters into
* InfoZip's standard ISO 8859-1 codepage table.
*/
MakeCompatibleString(internalFilen, ':', '/', '/', ':',
MacZip.CurrTextEncodingBase);
/* Returned malloc'ed name */
if (pdosflag)
*pdosflag = dosflag;
if (isdir)
{
return internalFilen; /* avoid warning on unused variable */
}
if (dosify)
{
msname(internalFilen);
printf("\n ex2in: %s",internalFilen);
}
return internalFilen;
}
/*
** Collect all filenames. Go through all directories
**
*/
int wild(char *Pathpat)
/* path/pattern to match */
/* If not in exclude mode, expand the pattern based on the contents of the
file system. Return an error code in the ZE_ class. */
{
FSSpec Spec;
char fullpath[NAME_MAX];
OSErr err;
AssertStr(Pathpat, Pathpat);
if (noisy) printf("%s \n\n",GetZipVersionsInfo());
if (extra_fields == 0)
{
MacZip.DataForkOnly = true;
}
/* for switch '-R' -> '.' means current dir */
if (strcmp(Pathpat,".") == 0) sstrcpy(Pathpat,"*");
sstrcpy(MacZip.Pattern,Pathpat);
if (recurse)
{
MacZip.StoreFoldersAlso = true;
MacZip.SearchLevels = 0; /* if 0 we aren't checking levels */
}
else
{
MacZip.StoreFoldersAlso = false;
MacZip.SearchLevels = 1;
}
/* make complete path */
GetCompletePath(fullpath, MacZip.Pattern, &Spec,&err);
err = PrintUserHFSerr((err != -43) && (err != 0), err, MacZip.Pattern);
printerr("GetCompletePath:", err, err, __LINE__, __FILE__, fullpath);
/* extract the filepattern */
GetFilename(MacZip.Pattern, fullpath);
/* extract Path and get FSSpec of search-path */
/* get FSSpec of search-path ; we need a dir to start
searching for filenames */
TruncFilename(MacZip.SearchDir, fullpath);
GetCompletePath(MacZip.SearchDir, MacZip.SearchDir, &Spec,&err);
if (noisy) {
if (MacZip.SearchLevels == 0)
{
printf("\nSearch Pattern: [%s] Levels: all", MacZip.Pattern);
}
else
{
printf("\nSearch Pattern: [%s] Levels: %d", MacZip.Pattern,
MacZip.SearchLevels);
}
printf("\nSearch Path: [%s]", MacZip.SearchDir);
printf("\nZip-File: [%s] \n",MacZip.ZipFullPath);
}
/* we are working only with pathnames;
* this can cause big problems on a mac ...
*/
if (CheckMountedVolumes(MacZip.SearchDir) > 1)
DoWarnUserDupVol(MacZip.SearchDir);
/* start getting all filenames */
err = FSpRecurseDirectory(&Spec, MacZip.SearchLevels);
printerr("FSpRecurseDirectory:", err, err, __LINE__, __FILE__, "");
return ZE_OK;
}
/*
** Convert the internal filename into a external (native).
** The user will see this modified filename.
** For more performance:
** I do not completly switch back to the native macos filename.
** The user will still see directory separator '/' and the converted
** charset.
*/
char *in2ex(char *n) /* internal file name */
/* Convert the zip file name to an external file name, returning the malloc'ed
string or NULL if not enough memory. */
{
char *x; /* external file name */
AssertStr(n,n)
if ((x = malloc(strlen(n) + 1)) == NULL)
return NULL;
RfDfFilen2Real(x, n, MacZip.MacZipMode, MacZip.DataForkOnly,
&MacZip.CurrentFork);
return x;
}
/*
** Process on filenames. This function will be called to collect
** the filenames.
*/
int procname(char *filename, /* name to process */
int caseflag) /* true to force case-sensitive match
(always false on a Mac) */
/* Process a name . Return
an error code in the ZE_ class. */
{
int rc; /* matched flag */
AssertBool(caseflag,"caseflag")
AssertStr(filename,filename)
/* add or remove name of file */
rc = newname(filename, MacZip.isDirectory, caseflag);
return rc;
}
ulg filetime(
char *f, /* name of file to get info on */
ulg *a, /* return value: file attributes */
long *n, /* return value: file size */
iztimes *t) /* return value: access, modific. and creation times */
/* If file *f does not exist, return 0. Else, return the file's last
modified date and time as an MSDOS date and time. The date and
time is returned in a long with the date most significant to allow
unsigned integer comparison of absolute times. Also, if a is not
a NULL pointer, store the file attributes there, with the high two
bytes being the Unix attributes, and the low byte being a mapping
of that to DOS attributes. If n is not NULL, store the file size
there. If t is not NULL, the file's access, modification and creation
times are stored there as UNIX time_t values.
If f is "-", use standard input as the file. If f is a device, return
a file size of -1 */
{
struct stat s; /* results of stat() */
AssertStr(f,f)
if (strlen(f) == 0) return 0;
if (SSTAT(f, &s) != 0)
/* Accept about any file kind including directories
* (stored with trailing : with -r option)
*/
return 0;
if (a != NULL) {
*a = ((ulg)s.st_mode << 16) | !(s.st_mode & S_IWRITE);
if (MacZip.isDirectory) {
*a |= MSDOS_DIR_ATTR;
}
}
if (n != NULL)
*n = (s.st_mode & UNX_IFMT) == UNX_IFREG ? s.st_size : -1L;
if (t != NULL) {
t->atime = s.st_atime;
t->mtime = s.st_mtime;
t->ctime = s.st_ctime; /* on Mac, st_ctime contains creation time! */
}
return unix2dostime(&s.st_mtime);
}
void stamp(char *f, ulg d)
/* char *f; name of file to change */
/* ulg d; dos-style time to change it to */
/* Set last updated and accessed time of file f to the DOS time d. */
{
time_t u[2]; /* argument for utime() */
f = f;
/* Convert DOS time to time_t format in u */
u[0] = u[1] = dos2unixtime(d);
/* utime(f, u); */
}
/*
** return only the longest part of the path:
** second parameter: Volume:test folder:second folder:
** third parameter: Volume:test folder:second folder:third folder:file
** result will be: third folder:file
** first parameter: contains string buffer that will be used to prepend
** the "resource mark" part in front of the result when
** a resource fork is processed in "M3" mode.
**
*/
char *StripPartialDir(char *CompletePath,
const char *PartialPath, const char *FullPath)
{
const char *tmpPtr1 = PartialPath;
const char *tmpPtr2 = FullPath;
int result;
Assert_it(CompletePath,"StripPartialDir","")
AssertStrNoOverlap(FullPath,PartialPath,PartialPath)
if (MacZip.DataForkOnly)
{
tmpPtr2 += strlen(tmpPtr1);
return (char *)tmpPtr2;
}
switch (MacZip.MacZipMode)
{
case JohnnyLee_EF:
{
tmpPtr2 += strlen(tmpPtr1);
return (char *)tmpPtr2;
break;
}
case NewZipMode_EF:
{ /* determine Fork type */
result = strncmp(FullPath, ResourceMark, sizeof(ResourceMark)-2);
if (result != 0)
{ /* data fork */
MacZip.CurrentFork = DataFork;
tmpPtr2 += strlen(tmpPtr1);
return (char *)tmpPtr2;
}
else
{ /* resource fork */
MacZip.CurrentFork = ResourceFork;
sstrcpy(CompletePath, ResourceMark);
tmpPtr2 += strlen(tmpPtr1);
tmpPtr2 += sizeof(ResourceMark);
sstrcat(CompletePath, tmpPtr2);
return (char *)CompletePath;
}
break;
}
}
return NULL; /* function should never reach this point */
}
/*
** Init all global variables
** Must be called for each zip-run
*/
void ZipInitAllVars(void)
{
getcwd(MacZip.CurrentPath, sizeof(MacZip.CurrentPath));
/* MacZip.MacZipMode = JohnnyLee_EF; */
MacZip.MacZipMode = NewZipMode_EF;
MacZip.DataForkOnly = false;
MacZip.CurrentFork = NoFork;
MacZip.StoreFoldersAlso = false;
MacZip.FoundFiles = 0;
MacZip.FoundDirectories = 0;
MacZip.RawCountOfItems = 0;
MacZip.BytesOfData = 0;
MacZip.StoreFullPath = false;
MacZip.StatingProgress = false;
MacZip.IncludeInvisible = false;
MacZip.isMacStatValid = false;
MacZip.CurrTextEncodingBase = FontScript();
MacZip.HaveGMToffset = false;
createTime = TickCount();
estTicksToFinish = -1;
updateTicks = 0;
/* init some functions */
IsZipFile(NULL);
destroy(NULL);
deletedir(NULL);
ShowCounter(true);
extra_fields = 1;
error_level = 0;
count_of_Zippedfiles = 0;
}
/*
** Get the findercomment and store it as file-comment in the Zip-file
**
*/
char *GetComment(char *filename)
{
OSErr err;
static char buffer[NAME_MAX];
char buffer2[NAME_MAX];
char *tmpPtr;
if (filename == NULL) return NULL;
/* now we can convert Unix-Path in HFS-Path */
for (tmpPtr = filename; *tmpPtr; tmpPtr++)
if (*tmpPtr == '/')
*tmpPtr = ':';
if (MacZip.StoreFullPath)
{ /* filename is already a fullpath */
sstrcpy(buffer,filename);
}
else
{ /* make a fullpath */
sstrcpy(buffer,MacZip.SearchDir);
sstrcat(buffer,filename);
}
/* make fullpath and get FSSpec */
/* Unfortunately: I get only the converted filename here */
/* so filenames with extended characters can not be found */
GetCompletePath(buffer2,buffer, &MacZip.fileSpec, &err);
printerr("GetCompletePath:",(err != -43) && (err != -120) && (err != 0) ,
err,__LINE__,__FILE__,buffer);
err = FSpDTGetComment(&MacZip.fileSpec, (unsigned char *) buffer);
printerr("FSpDTGetComment:", (err != -5012) && (err != 0), err,
__LINE__, __FILE__, filename);
P2CStr((unsigned char *) buffer);
if (err == -5012) return NULL; /* no finder-comments found */
if (noisy) printf("\n%32s -> %s",filename, buffer);
/*
Beside the script change we need only to change 0x0d in 0x0a
so the last two arguments are not needed and does nothing.
*/
MakeCompatibleString(buffer, 0x0d, 0x0a, ' ', ' ',
MacZip.CurrTextEncodingBase);
return buffer;
}
/*
** Print a progress indicator for stating the files
**
*/
void PrintStatProgress(char *msg)
{
if (!noisy) return; /* do no output if noisy is false */
MacZip.StatingProgress = true;
if (strcmp(msg,"done") == 0)
{
MacZip.StatingProgress = false;
printf("\n ... done \n\n");
}
else printf("\n %s",msg);
}
void InformProgress(const long progressMax, const long progressSoFar )
{
int curr_percent;
char no_time[5] = "...";
curr_percent = percent(progressMax, progressSoFar);
if (curr_percent < 95)
{
estTicksToFinish = EstimateCompletionTime(progressMax,
progressSoFar, curr_percent);
}
else
{
rightStatusString(no_time);
leftStatusString(no_time);
}
updateTicks = TickCount() + 60;
return;
}
void ShowCounter(Boolean reset)
{
static char statusline[100];
static unsigned long filecount = 0;
if (reset)
{
filecount = 0;
return;
}
if (noisy)
{
sprintf(statusline, "%6d", filecount++);
rightStatusString(statusline);
}
}
static long EstimateCompletionTime(const long progressMax,
const long progressSoFar,
unsigned char curr_percent)
{
long max = progressMax, value = progressSoFar;
static char buf[100];
unsigned long ticksTakenSoFar = TickCount() - createTime;
float currentRate = (float) ticksTakenSoFar / (float) value;
long newEst = (long)( currentRate * (float)( max - value ));
sprintf(buf, "%d [%d%%]",progressSoFar, curr_percent);
rightStatusString(buf);
estTicksToFinish = newEst;
UpdateTimeToComplete();
return estTicksToFinish;
}
static void UpdateTimeToComplete(void)
{
short days, hours, minutes, seconds;
char estStr[255];
Str15 xx, yy;
short idx = 0;
if ( estTicksToFinish == -1 )
return;
days = estTicksToFinish / 5184000L;
hours = ( estTicksToFinish - ( days * 5184000L )) / 216000L;
minutes = ( estTicksToFinish - ( days * 5184000L ) -
( hours * 216000L )) / 3600L;
seconds = ( estTicksToFinish - ( days * 5184000L ) -
( hours * 216000L ) - ( minutes * 3600L )) / 60L;
xx[0] = 0;
yy[0] = 0;
if ( days )
{
/* "more than 24 hours" */
idx = 1;
goto setEstTimeStr;
}
if ( hours >= 8 )
{
/* "more than x hours" */
NumToString( hours, xx );
idx = 2;
goto setEstTimeStr;
}
if ( estTicksToFinish > 252000L ) /* > 1hr, 10 minutes */
{
/* "about x hours, y minutes" */
NumToString( hours, xx );
NumToString( minutes, yy );
idx = 3;
goto setEstTimeStr;
}
if ( estTicksToFinish > 198000L ) /* > 55 minutes */
{
/* "about an hour" */
idx = 4;
goto setEstTimeStr;
}
if ( estTicksToFinish > 144000L ) /* > 40 minutes */
{
/* "less than an hour" */
idx = 5;
goto setEstTimeStr;
}
if ( estTicksToFinish > 4200L ) /* > 1 minute, 10 sec */
{
/* "about x minutes, y seconds */
NumToString( minutes, xx );
NumToString( seconds, yy );
if ( minutes == 1 )
idx = 11;
else
idx = 6;
goto setEstTimeStr;
}
if ( estTicksToFinish > 3000L ) /* > 50 seconds */
{
/* "about a minute" */
idx = 7;
goto setEstTimeStr;
}
if ( estTicksToFinish > 1500L ) /* > 25 seconds */
{
/* "less than a minute" */
idx = 8;
goto setEstTimeStr;
}
if ( estTicksToFinish > 120L ) /* > 2 seconds */
{
NumToString( seconds, xx );
idx = 9;
goto setEstTimeStr;
}
idx = 10;
setEstTimeStr:
sprintf(estStr,Time_Est_strings[idx],P2CStr(xx),P2CStr(yy));
leftStatusString((char *)estStr);
}
/*
** Just return the zip version
**
*/
char *GetZipVersionsInfo(void)
{
static char ZipVersion[100];
sprintf(ZipVersion, "Zip Module\n%d.%d%d%s of %s", Z_MAJORVER, Z_MINORVER,
Z_PATCHLEVEL, Z_BETALEVEL, REVDATE);
return ZipVersion;
}
#ifndef USE_SIOUX
/*
** Just return the copyright message
**
*/
char *GetZipCopyright(void)
{
static char CopyR[300];
sstrcpy(CopyR, copyright[0]);
sstrcat(CopyR, copyright[1]);
sstrcat(CopyR, "\r\rPlease send bug reports to the authors at\r"\
"Zip-Bugs@lists.wku.edu");
return CopyR;
}
/*
** Just return the compilers date/time
**
*/
char *GetZipVersionLocal(void)
{
static char ZipVersionLocal[50];
sprintf(ZipVersionLocal, "[%s %s]", __DATE__, __TIME__);
return ZipVersionLocal;
}
#endif /* #ifndef USE_SIOUX */