mirror of
https://github.com/KolibriOS/kolibrios.git
synced 2024-11-28 03:39:35 +03:00
e9b1c1bac6
git-svn-id: svn://kolibrios.org@6725 a494cfbc-eb01-0410-851d-a64ba20cac60
3089 lines
106 KiB
C
3089 lines
106 KiB
C
/*
|
|
Copyright (c) 1990-2009 Info-ZIP. All rights reserved.
|
|
|
|
See the accompanying file LICENSE, version 2009-Jan-02 or later
|
|
(the contents of which are also included in unzip.h) for terms of use.
|
|
If, for some reason, all these files are missing, the Info-ZIP license
|
|
also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html
|
|
*/
|
|
/*---------------------------------------------------------------------------
|
|
|
|
process.c
|
|
|
|
This file contains the top-level routines for processing multiple zipfiles.
|
|
|
|
Contains: process_zipfiles()
|
|
free_G_buffers()
|
|
do_seekable()
|
|
file_size()
|
|
rec_find()
|
|
find_ecrec64()
|
|
find_ecrec()
|
|
process_zip_cmmnt()
|
|
process_cdir_file_hdr()
|
|
get_cdir_ent()
|
|
process_local_file_hdr()
|
|
getZip64Data()
|
|
ef_scan_for_izux()
|
|
getRISCOSexfield()
|
|
|
|
---------------------------------------------------------------------------*/
|
|
|
|
|
|
#define UNZIP_INTERNAL
|
|
#include "unzip.h"
|
|
#ifdef WINDLL
|
|
# ifdef POCKET_UNZIP
|
|
# include "wince/intrface.h"
|
|
# else
|
|
# include "windll/windll.h"
|
|
# endif
|
|
#endif
|
|
#if defined(DYNALLOC_CRCTAB) || defined(UNICODE_SUPPORT)
|
|
# include "crc32.h"
|
|
#endif
|
|
|
|
static int do_seekable OF((__GPRO__ int lastchance));
|
|
#ifdef DO_SAFECHECK_2GB
|
|
# ifdef USE_STRM_INPUT
|
|
static zoff_t file_size OF((FILE *file));
|
|
# else
|
|
static zoff_t file_size OF((int fh));
|
|
# endif
|
|
#endif /* DO_SAFECHECK_2GB */
|
|
static int rec_find OF((__GPRO__ zoff_t, char *, int));
|
|
static int find_ecrec64 OF((__GPRO__ zoff_t searchlen));
|
|
static int find_ecrec OF((__GPRO__ zoff_t searchlen));
|
|
static int process_zip_cmmnt OF((__GPRO));
|
|
static int get_cdir_ent OF((__GPRO));
|
|
#ifdef IZ_HAVE_UXUIDGID
|
|
static int read_ux3_value OF((ZCONST uch *dbuf, unsigned uidgid_sz,
|
|
ulg *p_uidgid));
|
|
#endif /* IZ_HAVE_UXUIDGID */
|
|
|
|
|
|
static ZCONST char Far CannotAllocateBuffers[] =
|
|
"error: cannot allocate unzip buffers\n";
|
|
|
|
#ifdef SFX
|
|
static ZCONST char Far CannotFindMyself[] =
|
|
"unzipsfx: cannot find myself! [%s]\n";
|
|
# ifdef CHEAP_SFX_AUTORUN
|
|
static ZCONST char Far AutorunPrompt[] =
|
|
"\nAuto-run command: %s\nExecute this command? [y/n] ";
|
|
static ZCONST char Far NotAutoRunning[] =
|
|
"Not executing auto-run command.";
|
|
# endif
|
|
|
|
#else /* !SFX */
|
|
/* process_zipfiles() strings */
|
|
# if (defined(IZ_CHECK_TZ) && defined(USE_EF_UT_TIME))
|
|
static ZCONST char Far WarnInvalidTZ[] =
|
|
"Warning: TZ environment variable not found, cannot use UTC times!!\n";
|
|
# endif
|
|
# if !(defined(UNIX) || defined(AMIGA))
|
|
static ZCONST char Far CannotFindWildcardMatch[] =
|
|
"%s: cannot find any matches for wildcard specification \"%s\".\n";
|
|
# endif /* !(UNIX || AMIGA) */
|
|
static ZCONST char Far FilesProcessOK[] =
|
|
"%d archive%s successfully processed.\n";
|
|
static ZCONST char Far ArchiveWarning[] =
|
|
"%d archive%s had warnings but no fatal errors.\n";
|
|
static ZCONST char Far ArchiveFatalError[] =
|
|
"%d archive%s had fatal errors.\n";
|
|
static ZCONST char Far FileHadNoZipfileDir[] =
|
|
"%d file%s had no zipfile directory.\n";
|
|
static ZCONST char Far ZipfileWasDir[] = "1 \"zipfile\" was a directory.\n";
|
|
static ZCONST char Far ManyZipfilesWereDir[] =
|
|
"%d \"zipfiles\" were directories.\n";
|
|
static ZCONST char Far NoZipfileFound[] = "No zipfiles found.\n";
|
|
|
|
/* do_seekable() strings */
|
|
# ifdef UNIX
|
|
static ZCONST char Far CannotFindZipfileDirMsg[] =
|
|
"%s: cannot find zipfile directory in one of %s or\n\
|
|
%s%s.zip, and cannot find %s, period.\n";
|
|
static ZCONST char Far CannotFindEitherZipfile[] =
|
|
"%s: cannot find or open %s, %s.zip or %s.\n";
|
|
# else /* !UNIX */
|
|
static ZCONST char Far CannotFindZipfileDirMsg[] =
|
|
"%s: cannot find zipfile directory in %s,\n\
|
|
%sand cannot find %s, period.\n";
|
|
# ifdef VMS
|
|
static ZCONST char Far CannotFindEitherZipfile[] =
|
|
"%s: cannot find %s (%s).\n";
|
|
# else /* !VMS */
|
|
static ZCONST char Far CannotFindEitherZipfile[] =
|
|
"%s: cannot find either %s or %s.\n";
|
|
# endif /* ?VMS */
|
|
# endif /* ?UNIX */
|
|
extern ZCONST char Far Zipnfo[]; /* in unzip.c */
|
|
#ifndef WINDLL
|
|
static ZCONST char Far Unzip[] = "unzip";
|
|
#else
|
|
static ZCONST char Far Unzip[] = "UnZip DLL";
|
|
#endif
|
|
#ifdef DO_SAFECHECK_2GB
|
|
static ZCONST char Far ZipfileTooBig[] =
|
|
"Trying to read large file (> 2 GiB) without large file support\n";
|
|
#endif /* DO_SAFECHECK_2GB */
|
|
static ZCONST char Far MaybeExe[] =
|
|
"note: %s may be a plain executable, not an archive\n";
|
|
static ZCONST char Far CentDirNotInZipMsg[] = "\n\
|
|
[%s]:\n\
|
|
Zipfile is disk %lu of a multi-disk archive, and this is not the disk on\n\
|
|
which the central zipfile directory begins (disk %lu).\n";
|
|
static ZCONST char Far EndCentDirBogus[] =
|
|
"\nwarning [%s]: end-of-central-directory record claims this\n\
|
|
is disk %lu but that the central directory starts on disk %lu; this is a\n\
|
|
contradiction. Attempting to process anyway.\n";
|
|
# ifdef NO_MULTIPART
|
|
static ZCONST char Far NoMultiDiskArcSupport[] =
|
|
"\nerror [%s]: zipfile is part of multi-disk archive\n\
|
|
(sorry, not yet supported).\n";
|
|
static ZCONST char Far MaybePakBug[] = "warning [%s]:\
|
|
zipfile claims to be 2nd disk of a 2-part archive;\n\
|
|
attempting to process anyway. If no further errors occur, this archive\n\
|
|
was probably created by PAK v2.51 or earlier. This bug was reported to\n\
|
|
NoGate in March 1991 and was supposed to have been fixed by mid-1991; as\n\
|
|
of mid-1992 it still hadn't been. (If further errors do occur, archive\n\
|
|
was probably created by PKZIP 2.04c or later; UnZip does not yet support\n\
|
|
multi-part archives.)\n";
|
|
# else
|
|
static ZCONST char Far MaybePakBug[] = "warning [%s]:\
|
|
zipfile claims to be last disk of a multi-part archive;\n\
|
|
attempting to process anyway, assuming all parts have been concatenated\n\
|
|
together in order. Expect \"errors\" and warnings...true multi-part support\
|
|
\n doesn't exist yet (coming soon).\n";
|
|
# endif
|
|
static ZCONST char Far ExtraBytesAtStart[] =
|
|
"warning [%s]: %s extra byte%s at beginning or within zipfile\n\
|
|
(attempting to process anyway)\n";
|
|
#endif /* ?SFX */
|
|
|
|
#if ((!defined(WINDLL) && !defined(SFX)) || !defined(NO_ZIPINFO))
|
|
static ZCONST char Far LogInitline[] = "Archive: %s\n";
|
|
#endif
|
|
|
|
static ZCONST char Far MissingBytes[] =
|
|
"error [%s]: missing %s bytes in zipfile\n\
|
|
(attempting to process anyway)\n";
|
|
static ZCONST char Far NullCentDirOffset[] =
|
|
"error [%s]: NULL central directory offset\n\
|
|
(attempting to process anyway)\n";
|
|
static ZCONST char Far ZipfileEmpty[] = "warning [%s]: zipfile is empty\n";
|
|
static ZCONST char Far CentDirStartNotFound[] =
|
|
"error [%s]: start of central directory not found;\n\
|
|
zipfile corrupt.\n%s";
|
|
static ZCONST char Far Cent64EndSigSearchErr[] =
|
|
"fatal error: read failure while seeking for End-of-centdir-64 signature.\n\
|
|
This zipfile is corrupt.\n";
|
|
static ZCONST char Far Cent64EndSigSearchOff[] =
|
|
"error: End-of-centdir-64 signature not where expected (prepended bytes?)\n\
|
|
(attempting to process anyway)\n";
|
|
#ifndef SFX
|
|
static ZCONST char Far CentDirTooLong[] =
|
|
"error [%s]: reported length of central directory is\n\
|
|
%s bytes too long (Atari STZip zipfile? J.H.Holm ZIPSPLIT 1.1\n\
|
|
zipfile?). Compensating...\n";
|
|
static ZCONST char Far CentDirEndSigNotFound[] = "\
|
|
End-of-central-directory signature not found. Either this file is not\n\
|
|
a zipfile, or it constitutes one disk of a multi-part archive. In the\n\
|
|
latter case the central directory and zipfile comment will be found on\n\
|
|
the last disk(s) of this archive.\n";
|
|
#else /* SFX */
|
|
static ZCONST char Far CentDirEndSigNotFound[] =
|
|
" End-of-central-directory signature not found.\n";
|
|
#endif /* ?SFX */
|
|
#ifdef TIMESTAMP
|
|
static ZCONST char Far ZipTimeStampFailed[] =
|
|
"warning: cannot set time for %s\n";
|
|
static ZCONST char Far ZipTimeStampSuccess[] =
|
|
"Updated time stamp for %s.\n";
|
|
#endif
|
|
static ZCONST char Far ZipfileCommTrunc1[] =
|
|
"\ncaution: zipfile comment truncated\n";
|
|
#ifndef NO_ZIPINFO
|
|
static ZCONST char Far NoZipfileComment[] =
|
|
"There is no zipfile comment.\n";
|
|
static ZCONST char Far ZipfileCommentDesc[] =
|
|
"The zipfile comment is %u bytes long and contains the following text:\n";
|
|
static ZCONST char Far ZipfileCommBegin[] =
|
|
"======================== zipfile comment begins\
|
|
==========================\n";
|
|
static ZCONST char Far ZipfileCommEnd[] =
|
|
"========================= zipfile comment ends\
|
|
===========================\n";
|
|
static ZCONST char Far ZipfileCommTrunc2[] =
|
|
"\n The zipfile comment is truncated.\n";
|
|
#endif /* !NO_ZIPINFO */
|
|
#ifdef UNICODE_SUPPORT
|
|
static ZCONST char Far UnicodeVersionError[] =
|
|
"\nwarning: Unicode Path version > 1\n";
|
|
static ZCONST char Far UnicodeMismatchError[] =
|
|
"\nwarning: Unicode Path checksum invalid\n";
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/*******************************/
|
|
/* Function process_zipfiles() */
|
|
/*******************************/
|
|
|
|
int process_zipfiles(__G) /* return PK-type error code */
|
|
__GDEF
|
|
{
|
|
#ifndef SFX
|
|
char *lastzipfn = (char *)NULL;
|
|
int NumWinFiles, NumLoseFiles, NumWarnFiles;
|
|
int NumMissDirs, NumMissFiles;
|
|
#endif
|
|
int error=0, error_in_archive=0;
|
|
|
|
|
|
/*---------------------------------------------------------------------------
|
|
Start by allocating buffers and (re)constructing the various PK signature
|
|
strings.
|
|
---------------------------------------------------------------------------*/
|
|
|
|
G.inbuf = (uch *)malloc(INBUFSIZ + 4); /* 4 extra for hold[] (below) */
|
|
G.outbuf = (uch *)malloc(OUTBUFSIZ + 1); /* 1 extra for string term. */
|
|
|
|
if ((G.inbuf == (uch *)NULL) || (G.outbuf == (uch *)NULL)) {
|
|
Info(slide, 0x401, ((char *)slide,
|
|
LoadFarString(CannotAllocateBuffers)));
|
|
return(PK_MEM);
|
|
}
|
|
G.hold = G.inbuf + INBUFSIZ; /* to check for boundary-spanning sigs */
|
|
#ifndef VMS /* VMS uses its own buffer scheme for textmode flush(). */
|
|
#ifdef SMALL_MEM
|
|
G.outbuf2 = G.outbuf+RAWBUFSIZ; /* never changes */
|
|
#endif
|
|
#endif /* !VMS */
|
|
|
|
#if 0 /* CRC_32_TAB has been NULLified by CONSTRUCTGLOBALS !!!! */
|
|
/* allocate the CRC table later when we know we can read zipfile data */
|
|
CRC_32_TAB = NULL;
|
|
#endif /* 0 */
|
|
|
|
/* finish up initialization of magic signature strings */
|
|
local_hdr_sig[0] /* = extd_local_sig[0] */ = /* ASCII 'P', */
|
|
central_hdr_sig[0] = end_central_sig[0] = /* not EBCDIC */
|
|
end_centloc64_sig[0] = end_central64_sig[0] = 0x50;
|
|
|
|
local_hdr_sig[1] /* = extd_local_sig[1] */ = /* ASCII 'K', */
|
|
central_hdr_sig[1] = end_central_sig[1] = /* not EBCDIC */
|
|
end_centloc64_sig[1] = end_central64_sig[1] = 0x4B;
|
|
|
|
/*---------------------------------------------------------------------------
|
|
Make sure timezone info is set correctly; localtime() returns GMT on some
|
|
OSes (e.g., Solaris 2.x) if this isn't done first. The ifdefs around
|
|
tzset() were initially copied from dos_to_unix_time() in fileio.c. They
|
|
may still be too strict; any listed OS that supplies tzset(), regardless
|
|
of whether the function does anything, should be removed from the ifdefs.
|
|
---------------------------------------------------------------------------*/
|
|
|
|
#if (defined(WIN32) && defined(USE_EF_UT_TIME))
|
|
/* For the Win32 environment, we may have to "prepare" the environment
|
|
prior to the tzset() call, to work around tzset() implementation bugs.
|
|
*/
|
|
iz_w32_prepareTZenv();
|
|
#endif
|
|
|
|
#if (defined(IZ_CHECK_TZ) && defined(USE_EF_UT_TIME))
|
|
# ifndef VALID_TIMEZONE
|
|
# define VALID_TIMEZONE(tmp) \
|
|
(((tmp = getenv("TZ")) != NULL) && (*tmp != '\0'))
|
|
# endif
|
|
{
|
|
char *p;
|
|
G.tz_is_valid = VALID_TIMEZONE(p);
|
|
# ifndef SFX
|
|
if (!G.tz_is_valid) {
|
|
Info(slide, 0x401, ((char *)slide, LoadFarString(WarnInvalidTZ)));
|
|
error_in_archive = error = PK_WARN;
|
|
}
|
|
# endif /* !SFX */
|
|
}
|
|
#endif /* IZ_CHECK_TZ && USE_EF_UT_TIME */
|
|
|
|
/* For systems that do not have tzset() but supply this function using another
|
|
name (_tzset() or something similar), an appropiate "#define tzset ..."
|
|
should be added to the system specifc configuration section. */
|
|
#if (!defined(T20_VMS) && !defined(MACOS) && !defined(RISCOS) && !defined(QDOS))
|
|
#if (!defined(BSD) && !defined(MTS) && !defined(CMS_MVS) && !defined(TANDEM))
|
|
tzset();
|
|
#endif
|
|
#endif
|
|
|
|
/* Initialize UnZip's built-in pseudo hard-coded "ISO <--> OEM" translation,
|
|
depending on the detected codepage setup. */
|
|
#ifdef NEED_ISO_OEM_INIT
|
|
prepare_ISO_OEM_translat(__G);
|
|
#endif
|
|
|
|
/*---------------------------------------------------------------------------
|
|
Initialize the internal flag holding the mode of processing "overwrite
|
|
existing file" cases. We do not use the calling interface flags directly
|
|
because the overwrite mode may be changed by user interaction while
|
|
processing archive files. Such a change should not affect the option
|
|
settings as passed through the DLL calling interface.
|
|
In case of conflicting options, the 'safer' flag uO.overwrite_none takes
|
|
precedence.
|
|
---------------------------------------------------------------------------*/
|
|
G.overwrite_mode = (uO.overwrite_none ? OVERWRT_NEVER :
|
|
(uO.overwrite_all ? OVERWRT_ALWAYS : OVERWRT_QUERY));
|
|
|
|
/*---------------------------------------------------------------------------
|
|
Match (possible) wildcard zipfile specification with existing files and
|
|
attempt to process each. If no hits, try again after appending ".zip"
|
|
suffix. If still no luck, give up.
|
|
---------------------------------------------------------------------------*/
|
|
|
|
#ifdef SFX
|
|
if ((error = do_seekable(__G__ 0)) == PK_NOZIP) {
|
|
#ifdef EXE_EXTENSION
|
|
int len=strlen(G.argv0);
|
|
|
|
/* append .exe if appropriate; also .sfx? */
|
|
if ( (G.zipfn = (char *)malloc(len+sizeof(EXE_EXTENSION))) !=
|
|
(char *)NULL ) {
|
|
strcpy(G.zipfn, G.argv0);
|
|
strcpy(G.zipfn+len, EXE_EXTENSION);
|
|
error = do_seekable(__G__ 0);
|
|
free(G.zipfn);
|
|
G.zipfn = G.argv0; /* for "cannot find myself" message only */
|
|
}
|
|
#endif /* EXE_EXTENSION */
|
|
#ifdef WIN32
|
|
G.zipfn = G.argv0; /* for "cannot find myself" message only */
|
|
#endif
|
|
}
|
|
if (error) {
|
|
if (error == IZ_DIR)
|
|
error_in_archive = PK_NOZIP;
|
|
else
|
|
error_in_archive = error;
|
|
if (error == PK_NOZIP)
|
|
Info(slide, 1, ((char *)slide, LoadFarString(CannotFindMyself),
|
|
G.zipfn));
|
|
}
|
|
#ifdef CHEAP_SFX_AUTORUN
|
|
if (G.autorun_command[0] && !uO.qflag) { /* NO autorun without prompt! */
|
|
Info(slide, 0x81, ((char *)slide, LoadFarString(AutorunPrompt),
|
|
FnFilter1(G.autorun_command)));
|
|
if (fgets(G.answerbuf, 9, stdin) != (char *)NULL
|
|
&& toupper(*G.answerbuf) == 'Y')
|
|
system(G.autorun_command);
|
|
else
|
|
Info(slide, 1, ((char *)slide, LoadFarString(NotAutoRunning)));
|
|
}
|
|
#endif /* CHEAP_SFX_AUTORUN */
|
|
|
|
#else /* !SFX */
|
|
NumWinFiles = NumLoseFiles = NumWarnFiles = 0;
|
|
NumMissDirs = NumMissFiles = 0;
|
|
|
|
while ((G.zipfn = do_wild(__G__ G.wildzipfn)) != (char *)NULL) {
|
|
Trace((stderr, "do_wild( %s ) returns %s\n", G.wildzipfn, G.zipfn));
|
|
|
|
lastzipfn = G.zipfn;
|
|
|
|
/* print a blank line between the output of different zipfiles */
|
|
if (!uO.qflag && error != PK_NOZIP && error != IZ_DIR
|
|
#ifdef TIMESTAMP
|
|
&& (!uO.T_flag || uO.zipinfo_mode)
|
|
#endif
|
|
&& (NumWinFiles+NumLoseFiles+NumWarnFiles+NumMissFiles) > 0)
|
|
(*G.message)((zvoid *)&G, (uch *)"\n", 1L, 0);
|
|
|
|
if ((error = do_seekable(__G__ 0)) == PK_WARN)
|
|
++NumWarnFiles;
|
|
else if (error == IZ_DIR)
|
|
++NumMissDirs;
|
|
else if (error == PK_NOZIP)
|
|
++NumMissFiles;
|
|
else if (error != PK_OK)
|
|
++NumLoseFiles;
|
|
else
|
|
++NumWinFiles;
|
|
|
|
Trace((stderr, "do_seekable(0) returns %d\n", error));
|
|
if (error != IZ_DIR && error > error_in_archive)
|
|
error_in_archive = error;
|
|
#ifdef WINDLL
|
|
if (error == IZ_CTRLC) {
|
|
free_G_buffers(__G);
|
|
return error;
|
|
}
|
|
#endif
|
|
|
|
} /* end while-loop (wildcard zipfiles) */
|
|
|
|
if ((NumWinFiles + NumWarnFiles + NumLoseFiles) == 0 &&
|
|
(NumMissDirs + NumMissFiles) == 1 && lastzipfn != (char *)NULL)
|
|
{
|
|
#if (!defined(UNIX) && !defined(AMIGA)) /* filenames with wildcard characters */
|
|
if (iswild(G.wildzipfn)) {
|
|
if (iswild(lastzipfn)) {
|
|
NumMissDirs = NumMissFiles = 0;
|
|
error_in_archive = PK_COOL;
|
|
if (uO.qflag < 3)
|
|
Info(slide, 0x401, ((char *)slide,
|
|
LoadFarString(CannotFindWildcardMatch),
|
|
LoadFarStringSmall((uO.zipinfo_mode ? Zipnfo : Unzip)),
|
|
G.wildzipfn));
|
|
}
|
|
} else
|
|
#endif
|
|
{
|
|
#ifndef VMS
|
|
/* 2004-11-24 SMS.
|
|
* VMS has already tried a default file type of ".zip" in
|
|
* do_wild(), so adding ZSUFX here only causes confusion by
|
|
* corrupting some valid (though nonexistent) file names.
|
|
* Complaining below about "fred;4.zip" is unlikely to be
|
|
* helpful to the victim.
|
|
*/
|
|
/* 2005-08-14 Chr. Spieler
|
|
* Although we already "know" the failure result, we call
|
|
* do_seekable() again with the same zipfile name (and the
|
|
* lastchance flag set), just to trigger the error report...
|
|
*/
|
|
#if defined(UNIX) || defined(QDOS)
|
|
char *p =
|
|
#endif
|
|
strcpy(lastzipfn + strlen(lastzipfn), ZSUFX);
|
|
#endif /* !VMS */
|
|
|
|
G.zipfn = lastzipfn;
|
|
|
|
NumMissDirs = NumMissFiles = 0;
|
|
error_in_archive = PK_COOL;
|
|
|
|
#if defined(UNIX) || defined(QDOS)
|
|
/* only Unix has case-sensitive filesystems */
|
|
/* Well FlexOS (sometimes) also has them, but support is per media */
|
|
/* and a pig to code for, so treat as case insensitive for now */
|
|
/* we do this under QDOS to check for .zip as well as _zip */
|
|
if ((error = do_seekable(__G__ 0)) == PK_NOZIP || error == IZ_DIR) {
|
|
if (error == IZ_DIR)
|
|
++NumMissDirs;
|
|
strcpy(p, ALT_ZSUFX);
|
|
error = do_seekable(__G__ 1);
|
|
}
|
|
#else
|
|
error = do_seekable(__G__ 1);
|
|
#endif
|
|
Trace((stderr, "do_seekable(1) returns %d\n", error));
|
|
switch (error) {
|
|
case PK_WARN:
|
|
++NumWarnFiles;
|
|
break;
|
|
case IZ_DIR:
|
|
++NumMissDirs;
|
|
error = PK_NOZIP;
|
|
break;
|
|
case PK_NOZIP:
|
|
/* increment again => bug:
|
|
"1 file had no zipfile directory." */
|
|
/* ++NumMissFiles */ ;
|
|
break;
|
|
default:
|
|
if (error)
|
|
++NumLoseFiles;
|
|
else
|
|
++NumWinFiles;
|
|
break;
|
|
}
|
|
|
|
if (error > error_in_archive)
|
|
error_in_archive = error;
|
|
#ifdef WINDLL
|
|
if (error == IZ_CTRLC) {
|
|
free_G_buffers(__G);
|
|
return error;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
#endif /* ?SFX */
|
|
|
|
/*---------------------------------------------------------------------------
|
|
Print summary of all zipfiles, assuming zipfile spec was a wildcard (no
|
|
need for a summary if just one zipfile).
|
|
---------------------------------------------------------------------------*/
|
|
|
|
#ifndef SFX
|
|
if (iswild(G.wildzipfn) && uO.qflag < 3
|
|
#ifdef TIMESTAMP
|
|
&& !(uO.T_flag && !uO.zipinfo_mode && uO.qflag > 1)
|
|
#endif
|
|
)
|
|
{
|
|
if ((NumMissFiles + NumLoseFiles + NumWarnFiles > 0 || NumWinFiles != 1)
|
|
#ifdef TIMESTAMP
|
|
&& !(uO.T_flag && !uO.zipinfo_mode && uO.qflag)
|
|
#endif
|
|
&& !(uO.tflag && uO.qflag > 1))
|
|
(*G.message)((zvoid *)&G, (uch *)"\n", 1L, 0x401);
|
|
if ((NumWinFiles > 1) ||
|
|
(NumWinFiles == 1 &&
|
|
NumMissDirs + NumMissFiles + NumLoseFiles + NumWarnFiles > 0))
|
|
Info(slide, 0x401, ((char *)slide, LoadFarString(FilesProcessOK),
|
|
NumWinFiles, (NumWinFiles == 1)? " was" : "s were"));
|
|
if (NumWarnFiles > 0)
|
|
Info(slide, 0x401, ((char *)slide, LoadFarString(ArchiveWarning),
|
|
NumWarnFiles, (NumWarnFiles == 1)? "" : "s"));
|
|
if (NumLoseFiles > 0)
|
|
Info(slide, 0x401, ((char *)slide, LoadFarString(ArchiveFatalError),
|
|
NumLoseFiles, (NumLoseFiles == 1)? "" : "s"));
|
|
if (NumMissFiles > 0)
|
|
Info(slide, 0x401, ((char *)slide,
|
|
LoadFarString(FileHadNoZipfileDir), NumMissFiles,
|
|
(NumMissFiles == 1)? "" : "s"));
|
|
if (NumMissDirs == 1)
|
|
Info(slide, 0x401, ((char *)slide, LoadFarString(ZipfileWasDir)));
|
|
else if (NumMissDirs > 0)
|
|
Info(slide, 0x401, ((char *)slide,
|
|
LoadFarString(ManyZipfilesWereDir), NumMissDirs));
|
|
if (NumWinFiles + NumLoseFiles + NumWarnFiles == 0)
|
|
Info(slide, 0x401, ((char *)slide, LoadFarString(NoZipfileFound)));
|
|
}
|
|
#endif /* !SFX */
|
|
|
|
/* free allocated memory */
|
|
free_G_buffers(__G);
|
|
|
|
return error_in_archive;
|
|
|
|
} /* end function process_zipfiles() */
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************/
|
|
/* Function free_G_buffers() */
|
|
/*****************************/
|
|
|
|
void free_G_buffers(__G) /* releases all memory allocated in global vars */
|
|
__GDEF
|
|
{
|
|
#ifndef SFX
|
|
unsigned i;
|
|
#endif
|
|
|
|
#ifdef SYSTEM_SPECIFIC_DTOR
|
|
SYSTEM_SPECIFIC_DTOR(__G);
|
|
#endif
|
|
|
|
inflate_free(__G);
|
|
checkdir(__G__ (char *)NULL, END);
|
|
|
|
#ifdef DYNALLOC_CRCTAB
|
|
if (CRC_32_TAB) {
|
|
free_crc_table();
|
|
CRC_32_TAB = NULL;
|
|
}
|
|
#endif
|
|
|
|
if (G.key != (char *)NULL) {
|
|
free(G.key);
|
|
G.key = (char *)NULL;
|
|
}
|
|
|
|
if (G.extra_field != (uch *)NULL) {
|
|
free(G.extra_field);
|
|
G.extra_field = (uch *)NULL;
|
|
}
|
|
|
|
#if (!defined(VMS) && !defined(SMALL_MEM))
|
|
/* VMS uses its own buffer scheme for textmode flush() */
|
|
if (G.outbuf2) {
|
|
free(G.outbuf2); /* malloc'd ONLY if unshrink and -a */
|
|
G.outbuf2 = (uch *)NULL;
|
|
}
|
|
#endif
|
|
|
|
if (G.outbuf)
|
|
free(G.outbuf);
|
|
if (G.inbuf)
|
|
free(G.inbuf);
|
|
G.inbuf = G.outbuf = (uch *)NULL;
|
|
|
|
#ifdef UNICODE_SUPPORT
|
|
if (G.filename_full) {
|
|
free(G.filename_full);
|
|
G.filename_full = (char *)NULL;
|
|
G.fnfull_bufsize = 0;
|
|
}
|
|
#endif /* UNICODE_SUPPORT */
|
|
|
|
#ifndef SFX
|
|
for (i = 0; i < DIR_BLKSIZ; i++) {
|
|
if (G.info[i].cfilname != (char Far *)NULL) {
|
|
zffree(G.info[i].cfilname);
|
|
G.info[i].cfilname = (char Far *)NULL;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef MALLOC_WORK
|
|
if (G.area.Slide) {
|
|
free(G.area.Slide);
|
|
G.area.Slide = (uch *)NULL;
|
|
}
|
|
#endif
|
|
|
|
} /* end function free_G_buffers() */
|
|
|
|
|
|
|
|
|
|
|
|
/**************************/
|
|
/* Function do_seekable() */
|
|
/**************************/
|
|
|
|
static int do_seekable(__G__ lastchance) /* return PK-type error code */
|
|
__GDEF
|
|
int lastchance;
|
|
{
|
|
#ifndef SFX
|
|
/* static int no_ecrec = FALSE; SKM: moved to globals.h */
|
|
int maybe_exe=FALSE;
|
|
int too_weird_to_continue=FALSE;
|
|
#ifdef TIMESTAMP
|
|
time_t uxstamp;
|
|
ulg nmember = 0L;
|
|
#endif
|
|
#endif
|
|
int error=0, error_in_archive;
|
|
|
|
|
|
/*---------------------------------------------------------------------------
|
|
Open the zipfile for reading in BINARY mode to prevent CR/LF translation,
|
|
which would corrupt the bit streams.
|
|
---------------------------------------------------------------------------*/
|
|
|
|
if (SSTAT(G.zipfn, &G.statbuf) ||
|
|
#ifdef THEOS
|
|
(error = S_ISLIB(G.statbuf.st_mode)) != 0 ||
|
|
#endif
|
|
(error = S_ISDIR(G.statbuf.st_mode)) != 0)
|
|
{
|
|
#ifndef SFX
|
|
if (lastchance && (uO.qflag < 3)) {
|
|
#if defined(UNIX) || defined(QDOS)
|
|
if (G.no_ecrec)
|
|
Info(slide, 1, ((char *)slide,
|
|
LoadFarString(CannotFindZipfileDirMsg),
|
|
LoadFarStringSmall((uO.zipinfo_mode ? Zipnfo : Unzip)),
|
|
G.wildzipfn, uO.zipinfo_mode? " " : "", G.wildzipfn,
|
|
G.zipfn));
|
|
else
|
|
Info(slide, 1, ((char *)slide,
|
|
LoadFarString(CannotFindEitherZipfile),
|
|
LoadFarStringSmall((uO.zipinfo_mode ? Zipnfo : Unzip)),
|
|
G.wildzipfn, G.wildzipfn, G.zipfn));
|
|
#else /* !(UNIX || QDOS) */
|
|
if (G.no_ecrec)
|
|
Info(slide, 0x401, ((char *)slide,
|
|
LoadFarString(CannotFindZipfileDirMsg),
|
|
LoadFarStringSmall((uO.zipinfo_mode ? Zipnfo : Unzip)),
|
|
G.wildzipfn, uO.zipinfo_mode? " " : "", G.zipfn));
|
|
else
|
|
#ifdef VMS
|
|
Info(slide, 0x401, ((char *)slide,
|
|
LoadFarString(CannotFindEitherZipfile),
|
|
LoadFarStringSmall((uO.zipinfo_mode ? Zipnfo : Unzip)),
|
|
G.wildzipfn,
|
|
(*G.zipfn ? G.zipfn : vms_msg_text())));
|
|
#else /* !VMS */
|
|
Info(slide, 0x401, ((char *)slide,
|
|
LoadFarString(CannotFindEitherZipfile),
|
|
LoadFarStringSmall((uO.zipinfo_mode ? Zipnfo : Unzip)),
|
|
G.wildzipfn, G.zipfn));
|
|
#endif /* ?VMS */
|
|
#endif /* ?(UNIX || QDOS) */
|
|
}
|
|
#endif /* !SFX */
|
|
return error? IZ_DIR : PK_NOZIP;
|
|
}
|
|
G.ziplen = G.statbuf.st_size;
|
|
|
|
#ifndef SFX
|
|
#if defined(UNIX) || defined(DOS_OS2_W32) || defined(THEOS)
|
|
if (G.statbuf.st_mode & S_IEXEC) /* no extension on Unix exes: might */
|
|
maybe_exe = TRUE; /* find unzip, not unzip.zip; etc. */
|
|
#endif
|
|
#endif /* !SFX */
|
|
|
|
#ifdef VMS
|
|
if (check_format(__G)) /* check for variable-length format */
|
|
return PK_ERR;
|
|
#endif
|
|
|
|
if (open_input_file(__G)) /* this should never happen, given */
|
|
return PK_NOZIP; /* the stat() test above, but... */
|
|
|
|
#ifdef DO_SAFECHECK_2GB
|
|
/* Need more care: Do not trust the size returned by stat() but
|
|
determine it by reading beyond the end of the file. */
|
|
G.ziplen = file_size(G.zipfd);
|
|
|
|
if (G.ziplen == EOF) {
|
|
Info(slide, 0x401, ((char *)slide, LoadFarString(ZipfileTooBig)));
|
|
/*
|
|
printf(
|
|
" We need a better error message for: 64-bit file, 32-bit program.\n");
|
|
*/
|
|
CLOSE_INFILE();
|
|
return IZ_ERRBF;
|
|
}
|
|
#endif /* DO_SAFECHECK_2GB */
|
|
|
|
/*---------------------------------------------------------------------------
|
|
Find and process the end-of-central-directory header. UnZip need only
|
|
check last 65557 bytes of zipfile: comment may be up to 65535, end-of-
|
|
central-directory record is 18 bytes, and signature itself is 4 bytes;
|
|
add some to allow for appended garbage. Since ZipInfo is often used as
|
|
a debugging tool, search the whole zipfile if zipinfo_mode is true.
|
|
---------------------------------------------------------------------------*/
|
|
|
|
G.cur_zipfile_bufstart = 0;
|
|
G.inptr = G.inbuf;
|
|
|
|
#if ((!defined(WINDLL) && !defined(SFX)) || !defined(NO_ZIPINFO))
|
|
# if (!defined(WINDLL) && !defined(SFX))
|
|
if ( (!uO.zipinfo_mode && !uO.qflag
|
|
# ifdef TIMESTAMP
|
|
&& !uO.T_flag
|
|
# endif
|
|
)
|
|
# ifndef NO_ZIPINFO
|
|
|| (uO.zipinfo_mode && uO.hflag)
|
|
# endif
|
|
)
|
|
# else /* not (!WINDLL && !SFX) ==> !NO_ZIPINFO !! */
|
|
if (uO.zipinfo_mode && uO.hflag)
|
|
# endif /* if..else..: (!WINDLL && !SFX) */
|
|
# ifdef WIN32 /* Win32 console may require codepage conversion for G.zipfn */
|
|
Info(slide, 0, ((char *)slide, LoadFarString(LogInitline),
|
|
FnFilter1(G.zipfn)));
|
|
# else
|
|
Info(slide, 0, ((char *)slide, LoadFarString(LogInitline), G.zipfn));
|
|
# endif
|
|
#endif /* (!WINDLL && !SFX) || !NO_ZIPINFO */
|
|
|
|
if ( (error_in_archive = find_ecrec(__G__
|
|
#ifndef NO_ZIPINFO
|
|
uO.zipinfo_mode ? G.ziplen :
|
|
#endif
|
|
MIN(G.ziplen, 66000L)))
|
|
> PK_WARN )
|
|
{
|
|
CLOSE_INFILE();
|
|
|
|
#ifdef SFX
|
|
++lastchance; /* avoid picky compiler warnings */
|
|
return error_in_archive;
|
|
#else
|
|
if (maybe_exe)
|
|
Info(slide, 0x401, ((char *)slide, LoadFarString(MaybeExe),
|
|
G.zipfn));
|
|
if (lastchance)
|
|
return error_in_archive;
|
|
else {
|
|
G.no_ecrec = TRUE; /* assume we found wrong file: e.g., */
|
|
return PK_NOZIP; /* unzip instead of unzip.zip */
|
|
}
|
|
#endif /* ?SFX */
|
|
}
|
|
|
|
if ((uO.zflag > 0) && !uO.zipinfo_mode) { /* unzip: zflag = comment ONLY */
|
|
CLOSE_INFILE();
|
|
return error_in_archive;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------
|
|
Test the end-of-central-directory info for incompatibilities (multi-disk
|
|
archives) or inconsistencies (missing or extra bytes in zipfile).
|
|
---------------------------------------------------------------------------*/
|
|
|
|
#ifdef NO_MULTIPART
|
|
error = !uO.zipinfo_mode && (G.ecrec.number_this_disk == 1) &&
|
|
(G.ecrec.num_disk_start_cdir == 1);
|
|
#else
|
|
error = !uO.zipinfo_mode && (G.ecrec.number_this_disk != 0);
|
|
#endif
|
|
|
|
#ifndef SFX
|
|
if (uO.zipinfo_mode &&
|
|
G.ecrec.number_this_disk != G.ecrec.num_disk_start_cdir)
|
|
{
|
|
if (G.ecrec.number_this_disk > G.ecrec.num_disk_start_cdir) {
|
|
Info(slide, 0x401, ((char *)slide,
|
|
LoadFarString(CentDirNotInZipMsg), G.zipfn,
|
|
(ulg)G.ecrec.number_this_disk,
|
|
(ulg)G.ecrec.num_disk_start_cdir));
|
|
error_in_archive = PK_FIND;
|
|
too_weird_to_continue = TRUE;
|
|
} else {
|
|
Info(slide, 0x401, ((char *)slide,
|
|
LoadFarString(EndCentDirBogus), G.zipfn,
|
|
(ulg)G.ecrec.number_this_disk,
|
|
(ulg)G.ecrec.num_disk_start_cdir));
|
|
error_in_archive = PK_WARN;
|
|
}
|
|
#ifdef NO_MULTIPART /* concatenation of multiple parts works in some cases */
|
|
} else if (!uO.zipinfo_mode && !error && G.ecrec.number_this_disk != 0) {
|
|
Info(slide, 0x401, ((char *)slide, LoadFarString(NoMultiDiskArcSupport),
|
|
G.zipfn));
|
|
error_in_archive = PK_FIND;
|
|
too_weird_to_continue = TRUE;
|
|
#endif
|
|
}
|
|
|
|
if (!too_weird_to_continue) { /* (relatively) normal zipfile: go for it */
|
|
if (error) {
|
|
Info(slide, 0x401, ((char *)slide, LoadFarString(MaybePakBug),
|
|
G.zipfn));
|
|
error_in_archive = PK_WARN;
|
|
}
|
|
#endif /* !SFX */
|
|
if ((G.extra_bytes = G.real_ecrec_offset-G.expect_ecrec_offset) <
|
|
(zoff_t)0)
|
|
{
|
|
Info(slide, 0x401, ((char *)slide, LoadFarString(MissingBytes),
|
|
G.zipfn, FmZofft((-G.extra_bytes), NULL, NULL)));
|
|
error_in_archive = PK_ERR;
|
|
} else if (G.extra_bytes > 0) {
|
|
if ((G.ecrec.offset_start_central_directory == 0) &&
|
|
(G.ecrec.size_central_directory != 0)) /* zip 1.5 -go bug */
|
|
{
|
|
Info(slide, 0x401, ((char *)slide,
|
|
LoadFarString(NullCentDirOffset), G.zipfn));
|
|
G.ecrec.offset_start_central_directory = G.extra_bytes;
|
|
G.extra_bytes = 0;
|
|
error_in_archive = PK_ERR;
|
|
}
|
|
#ifndef SFX
|
|
else {
|
|
Info(slide, 0x401, ((char *)slide,
|
|
LoadFarString(ExtraBytesAtStart), G.zipfn,
|
|
FmZofft(G.extra_bytes, NULL, NULL),
|
|
(G.extra_bytes == 1)? "":"s"));
|
|
error_in_archive = PK_WARN;
|
|
}
|
|
#endif /* !SFX */
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------
|
|
Check for empty zipfile and exit now if so.
|
|
-----------------------------------------------------------------------*/
|
|
|
|
if (G.expect_ecrec_offset==0L && G.ecrec.size_central_directory==0) {
|
|
if (uO.zipinfo_mode)
|
|
Info(slide, 0, ((char *)slide, "%sEmpty zipfile.\n",
|
|
uO.lflag>9? "\n " : ""));
|
|
else
|
|
Info(slide, 0x401, ((char *)slide, LoadFarString(ZipfileEmpty),
|
|
G.zipfn));
|
|
CLOSE_INFILE();
|
|
return (error_in_archive > PK_WARN)? error_in_archive : PK_WARN;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------
|
|
Compensate for missing or extra bytes, and seek to where the start
|
|
of central directory should be. If header not found, uncompensate
|
|
and try again (necessary for at least some Atari archives created
|
|
with STZip, as well as archives created by J.H. Holm's ZIPSPLIT 1.1).
|
|
-----------------------------------------------------------------------*/
|
|
|
|
error = seek_zipf(__G__ G.ecrec.offset_start_central_directory);
|
|
if (error == PK_BADERR) {
|
|
CLOSE_INFILE();
|
|
return PK_BADERR;
|
|
}
|
|
#ifdef OLD_SEEK_TEST
|
|
if (error != PK_OK || readbuf(__G__ G.sig, 4) == 0) {
|
|
CLOSE_INFILE();
|
|
return PK_ERR; /* file may be locked, or possibly disk error(?) */
|
|
}
|
|
if (memcmp(G.sig, central_hdr_sig, 4))
|
|
#else
|
|
if ((error != PK_OK) || (readbuf(__G__ G.sig, 4) == 0) ||
|
|
memcmp(G.sig, central_hdr_sig, 4))
|
|
#endif
|
|
{
|
|
#ifndef SFX
|
|
zoff_t tmp = G.extra_bytes;
|
|
#endif
|
|
|
|
G.extra_bytes = 0;
|
|
error = seek_zipf(__G__ G.ecrec.offset_start_central_directory);
|
|
if ((error != PK_OK) || (readbuf(__G__ G.sig, 4) == 0) ||
|
|
memcmp(G.sig, central_hdr_sig, 4))
|
|
{
|
|
if (error != PK_BADERR)
|
|
Info(slide, 0x401, ((char *)slide,
|
|
LoadFarString(CentDirStartNotFound), G.zipfn,
|
|
LoadFarStringSmall(ReportMsg)));
|
|
CLOSE_INFILE();
|
|
return (error != PK_OK ? error : PK_BADERR);
|
|
}
|
|
#ifndef SFX
|
|
Info(slide, 0x401, ((char *)slide, LoadFarString(CentDirTooLong),
|
|
G.zipfn, FmZofft((-tmp), NULL, NULL)));
|
|
#endif
|
|
error_in_archive = PK_ERR;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------
|
|
Seek to the start of the central directory one last time, since we
|
|
have just read the first entry's signature bytes; then list, extract
|
|
or test member files as instructed, and close the zipfile.
|
|
-----------------------------------------------------------------------*/
|
|
|
|
error = seek_zipf(__G__ G.ecrec.offset_start_central_directory);
|
|
if (error != PK_OK) {
|
|
CLOSE_INFILE();
|
|
return error;
|
|
}
|
|
|
|
Trace((stderr, "about to extract/list files (error = %d)\n",
|
|
error_in_archive));
|
|
|
|
#ifdef DLL
|
|
/* G.fValidate is used only to look at an archive to see if
|
|
it appears to be a valid archive. There is no interest
|
|
in what the archive contains, nor in validating that the
|
|
entries in the archive are in good condition. This is
|
|
currently used only in the Windows DLLs for purposes of
|
|
checking archives within an archive to determine whether
|
|
or not to display the inner archives.
|
|
*/
|
|
if (!G.fValidate)
|
|
#endif
|
|
{
|
|
#ifndef NO_ZIPINFO
|
|
if (uO.zipinfo_mode)
|
|
error = zipinfo(__G); /* ZIPINFO 'EM */
|
|
else
|
|
#endif
|
|
#ifndef SFX
|
|
#ifdef TIMESTAMP
|
|
if (uO.T_flag)
|
|
error = get_time_stamp(__G__ &uxstamp, &nmember);
|
|
else
|
|
#endif
|
|
if (uO.vflag && !uO.tflag && !uO.cflag)
|
|
error = list_files(__G); /* LIST 'EM */
|
|
else
|
|
#endif /* !SFX */
|
|
error = extract_or_test_files(__G); /* EXTRACT OR TEST 'EM */
|
|
|
|
Trace((stderr, "done with extract/list files (error = %d)\n",
|
|
error));
|
|
}
|
|
|
|
if (error > error_in_archive) /* don't overwrite stronger error */
|
|
error_in_archive = error; /* with (for example) a warning */
|
|
#ifndef SFX
|
|
} /* end if (!too_weird_to_continue) */
|
|
#endif
|
|
|
|
CLOSE_INFILE();
|
|
|
|
#ifdef TIMESTAMP
|
|
if (uO.T_flag && !uO.zipinfo_mode && (nmember > 0L)) {
|
|
# ifdef WIN32
|
|
if (stamp_file(__G__ G.zipfn, uxstamp)) { /* TIME-STAMP 'EM */
|
|
# else
|
|
if (stamp_file(G.zipfn, uxstamp)) { /* TIME-STAMP 'EM */
|
|
# endif
|
|
if (uO.qflag < 3)
|
|
Info(slide, 0x201, ((char *)slide,
|
|
LoadFarString(ZipTimeStampFailed), G.zipfn));
|
|
if (error_in_archive < PK_WARN)
|
|
error_in_archive = PK_WARN;
|
|
} else {
|
|
if (!uO.qflag)
|
|
Info(slide, 0, ((char *)slide,
|
|
LoadFarString(ZipTimeStampSuccess), G.zipfn));
|
|
}
|
|
}
|
|
#endif
|
|
return error_in_archive;
|
|
|
|
} /* end function do_seekable() */
|
|
|
|
|
|
|
|
|
|
#ifdef DO_SAFECHECK_2GB
|
|
/************************/
|
|
/* Function file_size() */
|
|
/************************/
|
|
/* File size determination which does not mislead for large files in a
|
|
small-file program. Probably should be somewhere else.
|
|
The file has to be opened previously
|
|
*/
|
|
#ifdef USE_STRM_INPUT
|
|
static zoff_t file_size(file)
|
|
FILE *file;
|
|
{
|
|
int sts;
|
|
size_t siz;
|
|
#else /* !USE_STRM_INPUT */
|
|
static zoff_t file_size(fh)
|
|
int fh;
|
|
{
|
|
int siz;
|
|
#endif /* ?USE_STRM_INPUT */
|
|
zoff_t ofs;
|
|
char waste[4];
|
|
|
|
#ifdef USE_STRM_INPUT
|
|
/* Seek to actual EOF. */
|
|
sts = zfseeko(file, 0, SEEK_END);
|
|
if (sts != 0) {
|
|
/* fseeko() failed. (Unlikely.) */
|
|
ofs = EOF;
|
|
} else {
|
|
/* Get apparent offset at EOF. */
|
|
ofs = zftello(file);
|
|
if (ofs < 0) {
|
|
/* Offset negative (overflow). File too big. */
|
|
ofs = EOF;
|
|
} else {
|
|
/* Seek to apparent EOF offset.
|
|
Won't be at actual EOF if offset was truncated.
|
|
*/
|
|
sts = zfseeko(file, ofs, SEEK_SET);
|
|
if (sts != 0) {
|
|
/* fseeko() failed. (Unlikely.) */
|
|
ofs = EOF;
|
|
} else {
|
|
/* Read a byte at apparent EOF. Should set EOF flag. */
|
|
siz = fread(waste, 1, 1, file);
|
|
if (feof(file) == 0) {
|
|
/* Not at EOF, but should be. File too big. */
|
|
ofs = EOF;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#else /* !USE_STRM_INPUT */
|
|
/* Seek to actual EOF. */
|
|
ofs = zlseek(fh, 0, SEEK_END);
|
|
if (ofs == (zoff_t) -1) {
|
|
/* zlseek() failed. (Unlikely.) */
|
|
ofs = EOF;
|
|
} else if (ofs < 0) {
|
|
/* Offset negative (overflow). File too big. */
|
|
ofs = EOF;
|
|
} else {
|
|
/* Seek to apparent EOF offset.
|
|
Won't be at actual EOF if offset was truncated.
|
|
*/
|
|
ofs = zlseek(fh, ofs, SEEK_SET);
|
|
if (ofs == (zoff_t) -1) {
|
|
/* zlseek() failed. (Unlikely.) */
|
|
ofs = EOF;
|
|
} else {
|
|
/* Read a byte at apparent EOF. Should set EOF flag. */
|
|
siz = read(fh, waste, 1);
|
|
if (siz != 0) {
|
|
/* Not at EOF, but should be. File too big. */
|
|
ofs = EOF;
|
|
}
|
|
}
|
|
}
|
|
#endif /* ?USE_STRM_INPUT */
|
|
return ofs;
|
|
} /* end function file_size() */
|
|
#endif /* DO_SAFECHECK_2GB */
|
|
|
|
|
|
|
|
|
|
/***********************/
|
|
/* Function rec_find() */
|
|
/***********************/
|
|
|
|
static int rec_find(__G__ searchlen, signature, rec_size)
|
|
/* return 0 when rec found, 1 when not found, 2 in case of read error */
|
|
__GDEF
|
|
zoff_t searchlen;
|
|
char* signature;
|
|
int rec_size;
|
|
{
|
|
int i, numblks, found=FALSE;
|
|
zoff_t tail_len;
|
|
|
|
/*---------------------------------------------------------------------------
|
|
Zipfile is longer than INBUFSIZ: may need to loop. Start with short
|
|
block at end of zipfile (if not TOO short).
|
|
---------------------------------------------------------------------------*/
|
|
|
|
if ((tail_len = G.ziplen % INBUFSIZ) > rec_size) {
|
|
#ifdef USE_STRM_INPUT
|
|
zfseeko(G.zipfd, G.ziplen-tail_len, SEEK_SET);
|
|
G.cur_zipfile_bufstart = zftello(G.zipfd);
|
|
#else /* !USE_STRM_INPUT */
|
|
G.cur_zipfile_bufstart = zlseek(G.zipfd, G.ziplen-tail_len, SEEK_SET);
|
|
#endif /* ?USE_STRM_INPUT */
|
|
if ((G.incnt = read(G.zipfd, (char *)G.inbuf,
|
|
(unsigned int)tail_len)) != (int)tail_len)
|
|
return 2; /* it's expedient... */
|
|
|
|
/* 'P' must be at least (rec_size+4) bytes from end of zipfile */
|
|
for (G.inptr = G.inbuf+(int)tail_len-(rec_size+4);
|
|
G.inptr >= G.inbuf;
|
|
--G.inptr) {
|
|
if ( (*G.inptr == (uch)0x50) && /* ASCII 'P' */
|
|
!memcmp((char *)G.inptr, signature, 4) ) {
|
|
G.incnt -= (int)(G.inptr - G.inbuf);
|
|
found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
/* sig may span block boundary: */
|
|
memcpy((char *)G.hold, (char *)G.inbuf, 3);
|
|
} else
|
|
G.cur_zipfile_bufstart = G.ziplen - tail_len;
|
|
|
|
/*-----------------------------------------------------------------------
|
|
Loop through blocks of zipfile data, starting at the end and going
|
|
toward the beginning. In general, need not check whole zipfile for
|
|
signature, but may want to do so if testing.
|
|
-----------------------------------------------------------------------*/
|
|
|
|
numblks = (int)((searchlen - tail_len + (INBUFSIZ-1)) / INBUFSIZ);
|
|
/* ==amount= ==done== ==rounding== =blksiz= */
|
|
|
|
for (i = 1; !found && (i <= numblks); ++i) {
|
|
G.cur_zipfile_bufstart -= INBUFSIZ;
|
|
#ifdef USE_STRM_INPUT
|
|
zfseeko(G.zipfd, G.cur_zipfile_bufstart, SEEK_SET);
|
|
#else /* !USE_STRM_INPUT */
|
|
zlseek(G.zipfd, G.cur_zipfile_bufstart, SEEK_SET);
|
|
#endif /* ?USE_STRM_INPUT */
|
|
if ((G.incnt = read(G.zipfd,(char *)G.inbuf,INBUFSIZ))
|
|
!= INBUFSIZ)
|
|
return 2; /* read error is fatal failure */
|
|
|
|
for (G.inptr = G.inbuf+INBUFSIZ-1; G.inptr >= G.inbuf; --G.inptr)
|
|
if ( (*G.inptr == (uch)0x50) && /* ASCII 'P' */
|
|
!memcmp((char *)G.inptr, signature, 4) ) {
|
|
G.incnt -= (int)(G.inptr - G.inbuf);
|
|
found = TRUE;
|
|
break;
|
|
}
|
|
/* sig may span block boundary: */
|
|
memcpy((char *)G.hold, (char *)G.inbuf, 3);
|
|
}
|
|
return (found ? 0 : 1);
|
|
} /* end function rec_find() */
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
/********************************/
|
|
/* Function check_ecrec_zip64() */
|
|
/********************************/
|
|
|
|
static int check_ecrec_zip64(__G)
|
|
__GDEF
|
|
{
|
|
return G.ecrec.offset_start_central_directory == 0xFFFFFFFFL
|
|
|| G.ecrec.size_central_directory == 0xFFFFFFFFL
|
|
|| G.ecrec.total_entries_central_dir == 0xFFFF
|
|
|| G.ecrec.num_entries_centrl_dir_ths_disk == 0xFFFF
|
|
|| G.ecrec.num_disk_start_cdir == 0xFFFF
|
|
|| G.ecrec.number_this_disk == 0xFFFF;
|
|
} /* end function check_ecrec_zip64() */
|
|
#endif /* never */
|
|
|
|
|
|
|
|
/***************************/
|
|
/* Function find_ecrec64() */
|
|
/***************************/
|
|
|
|
static int find_ecrec64(__G__ searchlen) /* return PK-class error */
|
|
__GDEF
|
|
zoff_t searchlen;
|
|
{
|
|
ec_byte_rec64 byterec; /* buf for ecrec64 */
|
|
ec_byte_loc64 byterecL; /* buf for ecrec64 locator */
|
|
zoff_t ecloc64_start_offset; /* start offset of ecrec64 locator */
|
|
zusz_t ecrec64_start_offset; /* start offset of ecrec64 */
|
|
zuvl_t ecrec64_start_disk; /* start disk of ecrec64 */
|
|
zuvl_t ecloc64_total_disks; /* total disks */
|
|
zuvl_t ecrec64_disk_cdstart; /* disk number of central dir start */
|
|
zucn_t ecrec64_this_entries; /* entries on disk with ecrec64 */
|
|
zucn_t ecrec64_tot_entries; /* total number of entries */
|
|
zusz_t ecrec64_cdirsize; /* length of central dir */
|
|
zusz_t ecrec64_offs_cdstart; /* offset of central dir start */
|
|
|
|
/* First, find the ecrec64 locator. By definition, this must be before
|
|
ecrec with nothing in between. We back up the size of the ecrec64
|
|
locator and check. */
|
|
|
|
ecloc64_start_offset = G.real_ecrec_offset - (ECLOC64_SIZE+4);
|
|
if (ecloc64_start_offset < 0)
|
|
/* Seeking would go past beginning, so probably empty archive */
|
|
return PK_COOL;
|
|
|
|
#ifdef USE_STRM_INPUT
|
|
zfseeko(G.zipfd, ecloc64_start_offset, SEEK_SET);
|
|
G.cur_zipfile_bufstart = zftello(G.zipfd);
|
|
#else /* !USE_STRM_INPUT */
|
|
G.cur_zipfile_bufstart = zlseek(G.zipfd, ecloc64_start_offset, SEEK_SET);
|
|
#endif /* ?USE_STRM_INPUT */
|
|
|
|
if ((G.incnt = read(G.zipfd, (char *)byterecL, ECLOC64_SIZE+4))
|
|
!= (ECLOC64_SIZE+4)) {
|
|
if (uO.qflag || uO.zipinfo_mode)
|
|
Info(slide, 0x401, ((char *)slide, "[%s]\n", G.zipfn));
|
|
Info(slide, 0x401, ((char *)slide,
|
|
LoadFarString(Cent64EndSigSearchErr)));
|
|
return PK_ERR;
|
|
}
|
|
|
|
if (memcmp((char *)byterecL, end_centloc64_sig, 4) ) {
|
|
/* not found */
|
|
return PK_COOL;
|
|
}
|
|
|
|
/* Read the locator. */
|
|
ecrec64_start_disk = (zuvl_t)makelong(&byterecL[NUM_DISK_START_EOCDR64]);
|
|
ecrec64_start_offset = (zusz_t)makeint64(&byterecL[OFFSET_START_EOCDR64]);
|
|
ecloc64_total_disks = (zuvl_t)makelong(&byterecL[NUM_THIS_DISK_LOC64]);
|
|
|
|
/* Check for consistency */
|
|
#ifdef TEST
|
|
fprintf(stdout,"\nnumber of disks (ECR) %u, (ECLOC64) %lu\n",
|
|
G.ecrec.number_this_disk, ecloc64_total_disks); fflush(stdout);
|
|
#endif
|
|
if ((G.ecrec.number_this_disk != 0xFFFF) &&
|
|
(G.ecrec.number_this_disk != ecloc64_total_disks - 1)) {
|
|
/* Note: For some unknown reason, the developers at PKWARE decided to
|
|
store the "zip64 total disks" value as a counter starting from 1,
|
|
whereas all other "split/span volume" related fields use 0-based
|
|
volume numbers. Sigh... */
|
|
/* When the total number of disks as found in the traditional ecrec
|
|
is not 0xFFFF, the disk numbers in ecrec and ecloc64 must match.
|
|
When this is not the case, the found ecrec64 locator cannot be valid.
|
|
-> This is not a Zip64 archive.
|
|
*/
|
|
Trace((stderr,
|
|
"\ninvalid ECLOC64, differing disk# (ECR %u, ECL64 %lu)\n",
|
|
G.ecrec.number_this_disk, ecloc64_total_disks - 1));
|
|
return PK_COOL;
|
|
}
|
|
|
|
/* If found locator, look for ecrec64 where the locator says it is. */
|
|
|
|
/* For now assume that ecrec64 is on the same disk as ecloc64 and ecrec,
|
|
which is usually the case and is how Zip writes it. To do this right,
|
|
however, we should allow the ecrec64 to be on another disk since
|
|
the AppNote allows it and the ecrec64 can be large, especially if
|
|
Version 2 is used (AppNote uses 8 bytes for the size of this record). */
|
|
|
|
/* FIX BELOW IF ADD SUPPORT FOR MULTIPLE DISKS */
|
|
|
|
if (ecrec64_start_offset > (zusz_t)ecloc64_start_offset) {
|
|
/* ecrec64 has to be before ecrec64 locator */
|
|
if (uO.qflag || uO.zipinfo_mode)
|
|
Info(slide, 0x401, ((char *)slide, "[%s]\n", G.zipfn));
|
|
Info(slide, 0x401, ((char *)slide,
|
|
LoadFarString(Cent64EndSigSearchErr)));
|
|
return PK_ERR;
|
|
}
|
|
|
|
#ifdef USE_STRM_INPUT
|
|
zfseeko(G.zipfd, ecrec64_start_offset, SEEK_SET);
|
|
G.cur_zipfile_bufstart = zftello(G.zipfd);
|
|
#else /* !USE_STRM_INPUT */
|
|
G.cur_zipfile_bufstart = zlseek(G.zipfd, ecrec64_start_offset, SEEK_SET);
|
|
#endif /* ?USE_STRM_INPUT */
|
|
|
|
if ((G.incnt = read(G.zipfd, (char *)byterec, ECREC64_SIZE+4))
|
|
!= (ECREC64_SIZE+4)) {
|
|
if (uO.qflag || uO.zipinfo_mode)
|
|
Info(slide, 0x401, ((char *)slide, "[%s]\n", G.zipfn));
|
|
Info(slide, 0x401, ((char *)slide,
|
|
LoadFarString(Cent64EndSigSearchErr)));
|
|
return PK_ERR;
|
|
}
|
|
|
|
if (memcmp((char *)byterec, end_central64_sig, 4) ) {
|
|
/* Zip64 EOCD Record not found */
|
|
/* Since we already have seen the Zip64 EOCD Locator, it's
|
|
possible we got here because there are bytes prepended
|
|
to the archive, like the sfx prefix. */
|
|
|
|
/* Make a guess as to where the Zip64 EOCD Record might be */
|
|
ecrec64_start_offset = ecloc64_start_offset - ECREC64_SIZE - 4;
|
|
|
|
#ifdef USE_STRM_INPUT
|
|
zfseeko(G.zipfd, ecrec64_start_offset, SEEK_SET);
|
|
G.cur_zipfile_bufstart = zftello(G.zipfd);
|
|
#else /* !USE_STRM_INPUT */
|
|
G.cur_zipfile_bufstart = zlseek(G.zipfd, ecrec64_start_offset, SEEK_SET);
|
|
#endif /* ?USE_STRM_INPUT */
|
|
|
|
if ((G.incnt = read(G.zipfd, (char *)byterec, ECREC64_SIZE+4))
|
|
!= (ECREC64_SIZE+4)) {
|
|
if (uO.qflag || uO.zipinfo_mode)
|
|
Info(slide, 0x401, ((char *)slide, "[%s]\n", G.zipfn));
|
|
Info(slide, 0x401, ((char *)slide,
|
|
LoadFarString(Cent64EndSigSearchErr)));
|
|
return PK_ERR;
|
|
}
|
|
|
|
if (memcmp((char *)byterec, end_central64_sig, 4) ) {
|
|
/* Zip64 EOCD Record not found */
|
|
/* Probably something not so easy to handle so exit */
|
|
if (uO.qflag || uO.zipinfo_mode)
|
|
Info(slide, 0x401, ((char *)slide, "[%s]\n", G.zipfn));
|
|
Info(slide, 0x401, ((char *)slide,
|
|
LoadFarString(Cent64EndSigSearchErr)));
|
|
return PK_ERR;
|
|
}
|
|
|
|
if (uO.qflag || uO.zipinfo_mode)
|
|
Info(slide, 0x401, ((char *)slide, "[%s]\n", G.zipfn));
|
|
Info(slide, 0x401, ((char *)slide,
|
|
LoadFarString(Cent64EndSigSearchOff)));
|
|
}
|
|
|
|
/* Check consistency of found ecrec64 with ecloc64 (and ecrec): */
|
|
if ( (zuvl_t)makelong(&byterec[NUMBER_THIS_DSK_REC64])
|
|
!= ecrec64_start_disk )
|
|
/* found ecrec64 does not match ecloc64 info -> no Zip64 archive */
|
|
return PK_COOL;
|
|
/* Read all relevant ecrec64 fields and compare them to the corresponding
|
|
ecrec fields unless those are set to "all-ones".
|
|
*/
|
|
ecrec64_disk_cdstart =
|
|
(zuvl_t)makelong(&byterec[NUM_DISK_START_CEN_DIR64]);
|
|
if ( (G.ecrec.num_disk_start_cdir != 0xFFFF) &&
|
|
(G.ecrec.num_disk_start_cdir != ecrec64_disk_cdstart) )
|
|
return PK_COOL;
|
|
ecrec64_this_entries
|
|
= makeint64(&byterec[NUM_ENTRIES_CEN_DIR_THS_DISK64]);
|
|
if ( (G.ecrec.num_entries_centrl_dir_ths_disk != 0xFFFF) &&
|
|
(G.ecrec.num_entries_centrl_dir_ths_disk != ecrec64_this_entries) )
|
|
return PK_COOL;
|
|
ecrec64_tot_entries
|
|
= makeint64(&byterec[TOTAL_ENTRIES_CENTRAL_DIR64]);
|
|
if ( (G.ecrec.total_entries_central_dir != 0xFFFF) &&
|
|
(G.ecrec.total_entries_central_dir != ecrec64_tot_entries) )
|
|
return PK_COOL;
|
|
ecrec64_cdirsize
|
|
= makeint64(&byterec[SIZE_CENTRAL_DIRECTORY64]);
|
|
if ( (G.ecrec.size_central_directory != 0xFFFFFFFFL) &&
|
|
(G.ecrec.size_central_directory != ecrec64_cdirsize) )
|
|
return PK_COOL;
|
|
ecrec64_offs_cdstart
|
|
= makeint64(&byterec[OFFSET_START_CENTRAL_DIRECT64]);
|
|
if ( (G.ecrec.offset_start_central_directory != 0xFFFFFFFFL) &&
|
|
(G.ecrec.offset_start_central_directory != ecrec64_offs_cdstart) )
|
|
return PK_COOL;
|
|
|
|
/* Now, we are (almost) sure that we have a Zip64 archive. */
|
|
G.ecrec.have_ecr64 = 1;
|
|
|
|
/* Update the "end-of-central-dir offset" for later checks. */
|
|
G.real_ecrec_offset = ecrec64_start_offset;
|
|
|
|
/* Update all ecdir_rec data that are flagged to be invalid
|
|
in Zip64 mode. Set the ecrec64-mandatory flag when such a
|
|
case is found. */
|
|
if (G.ecrec.number_this_disk == 0xFFFF) {
|
|
G.ecrec.number_this_disk = ecrec64_start_disk;
|
|
if (ecrec64_start_disk != 0xFFFF) G.ecrec.is_zip64_archive = TRUE;
|
|
}
|
|
if (G.ecrec.num_disk_start_cdir == 0xFFFF) {
|
|
G.ecrec.num_disk_start_cdir = ecrec64_disk_cdstart;
|
|
if (ecrec64_disk_cdstart != 0xFFFF) G.ecrec.is_zip64_archive = TRUE;
|
|
}
|
|
if (G.ecrec.num_entries_centrl_dir_ths_disk == 0xFFFF) {
|
|
G.ecrec.num_entries_centrl_dir_ths_disk = ecrec64_this_entries;
|
|
if (ecrec64_this_entries != 0xFFFF) G.ecrec.is_zip64_archive = TRUE;
|
|
}
|
|
if (G.ecrec.total_entries_central_dir == 0xFFFF) {
|
|
G.ecrec.total_entries_central_dir = ecrec64_tot_entries;
|
|
if (ecrec64_tot_entries != 0xFFFF) G.ecrec.is_zip64_archive = TRUE;
|
|
}
|
|
if (G.ecrec.size_central_directory == 0xFFFFFFFFL) {
|
|
G.ecrec.size_central_directory = ecrec64_cdirsize;
|
|
if (ecrec64_cdirsize != 0xFFFFFFFF) G.ecrec.is_zip64_archive = TRUE;
|
|
}
|
|
if (G.ecrec.offset_start_central_directory == 0xFFFFFFFFL) {
|
|
G.ecrec.offset_start_central_directory = ecrec64_offs_cdstart;
|
|
if (ecrec64_offs_cdstart != 0xFFFFFFFF) G.ecrec.is_zip64_archive = TRUE;
|
|
}
|
|
|
|
return PK_COOL;
|
|
} /* end function find_ecrec64() */
|
|
|
|
|
|
|
|
/*************************/
|
|
/* Function find_ecrec() */
|
|
/*************************/
|
|
|
|
static int find_ecrec(__G__ searchlen) /* return PK-class error */
|
|
__GDEF
|
|
zoff_t searchlen;
|
|
{
|
|
int found = FALSE;
|
|
int error_in_archive;
|
|
int result;
|
|
ec_byte_rec byterec;
|
|
|
|
/*---------------------------------------------------------------------------
|
|
Treat case of short zipfile separately.
|
|
---------------------------------------------------------------------------*/
|
|
|
|
if (G.ziplen <= INBUFSIZ) {
|
|
#ifdef USE_STRM_INPUT
|
|
zfseeko(G.zipfd, 0L, SEEK_SET);
|
|
#else /* !USE_STRM_INPUT */
|
|
zlseek(G.zipfd, 0L, SEEK_SET);
|
|
#endif /* ?USE_STRM_INPUT */
|
|
if ((G.incnt = read(G.zipfd,(char *)G.inbuf,(unsigned int)G.ziplen))
|
|
== (int)G.ziplen)
|
|
|
|
/* 'P' must be at least (ECREC_SIZE+4) bytes from end of zipfile */
|
|
for (G.inptr = G.inbuf+(int)G.ziplen-(ECREC_SIZE+4);
|
|
G.inptr >= G.inbuf;
|
|
--G.inptr) {
|
|
if ( (*G.inptr == (uch)0x50) && /* ASCII 'P' */
|
|
!memcmp((char *)G.inptr, end_central_sig, 4)) {
|
|
G.incnt -= (int)(G.inptr - G.inbuf);
|
|
found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------
|
|
Zipfile is longer than INBUFSIZ:
|
|
|
|
MB - this next block of code moved to rec_find so that same code can be
|
|
used to look for zip64 ec record. No need to include code above since
|
|
a zip64 ec record will only be looked for if it is a BIG file.
|
|
---------------------------------------------------------------------------*/
|
|
|
|
} else {
|
|
found =
|
|
(rec_find(__G__ searchlen, end_central_sig, ECREC_SIZE) == 0
|
|
? TRUE : FALSE);
|
|
} /* end if (ziplen > INBUFSIZ) */
|
|
|
|
/*---------------------------------------------------------------------------
|
|
Searched through whole region where signature should be without finding
|
|
it. Print informational message and die a horrible death.
|
|
---------------------------------------------------------------------------*/
|
|
|
|
if (!found) {
|
|
if (uO.qflag || uO.zipinfo_mode)
|
|
Info(slide, 0x401, ((char *)slide, "[%s]\n", G.zipfn));
|
|
Info(slide, 0x401, ((char *)slide,
|
|
LoadFarString(CentDirEndSigNotFound)));
|
|
return PK_ERR; /* failed */
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------
|
|
Found the signature, so get the end-central data before returning. Do
|
|
any necessary machine-type conversions (byte ordering, structure padding
|
|
compensation) by reading data into character array and copying to struct.
|
|
---------------------------------------------------------------------------*/
|
|
|
|
G.real_ecrec_offset = G.cur_zipfile_bufstart + (G.inptr-G.inbuf);
|
|
#ifdef TEST
|
|
printf("\n found end-of-central-dir signature at offset %s (%sh)\n",
|
|
FmZofft(G.real_ecrec_offset, NULL, NULL),
|
|
FmZofft(G.real_ecrec_offset, FZOFFT_HEX_DOT_WID, "X"));
|
|
printf(" from beginning of file; offset %d (%.4Xh) within block\n",
|
|
G.inptr-G.inbuf, G.inptr-G.inbuf);
|
|
#endif
|
|
|
|
if (readbuf(__G__ (char *)byterec, ECREC_SIZE+4) == 0)
|
|
return PK_EOF;
|
|
|
|
G.ecrec.number_this_disk =
|
|
makeword(&byterec[NUMBER_THIS_DISK]);
|
|
G.ecrec.num_disk_start_cdir =
|
|
makeword(&byterec[NUM_DISK_WITH_START_CEN_DIR]);
|
|
G.ecrec.num_entries_centrl_dir_ths_disk =
|
|
makeword(&byterec[NUM_ENTRIES_CEN_DIR_THS_DISK]);
|
|
G.ecrec.total_entries_central_dir =
|
|
makeword(&byterec[TOTAL_ENTRIES_CENTRAL_DIR]);
|
|
G.ecrec.size_central_directory =
|
|
makelong(&byterec[SIZE_CENTRAL_DIRECTORY]);
|
|
G.ecrec.offset_start_central_directory =
|
|
makelong(&byterec[OFFSET_START_CENTRAL_DIRECTORY]);
|
|
G.ecrec.zipfile_comment_length =
|
|
makeword(&byterec[ZIPFILE_COMMENT_LENGTH]);
|
|
|
|
/* Now, we have to read the archive comment, BEFORE the file pointer
|
|
is moved away backwards to seek for a Zip64 ECLOC64 structure.
|
|
*/
|
|
if ( (error_in_archive = process_zip_cmmnt(__G)) > PK_WARN )
|
|
return error_in_archive;
|
|
|
|
/* Next: Check for existence of Zip64 end-of-cent-dir locator
|
|
ECLOC64. This structure must reside on the same volume as the
|
|
classic ECREC, at exactly (ECLOC64_SIZE+4) bytes in front
|
|
of the ECREC.
|
|
The ECLOC64 structure directs to the longer ECREC64 structure
|
|
A ECREC64 will ALWAYS exist for a proper Zip64 archive, as
|
|
the "Version Needed To Extract" field is required to be set
|
|
to 4.5 or higher whenever any Zip64 features are used anywhere
|
|
in the archive, so just check for that to see if this is a
|
|
Zip64 archive.
|
|
*/
|
|
result = find_ecrec64(__G__ searchlen+76);
|
|
/* 76 bytes for zip64ec & zip64 locator */
|
|
if (result != PK_COOL) {
|
|
if (error_in_archive < result)
|
|
error_in_archive = result;
|
|
return error_in_archive;
|
|
}
|
|
|
|
G.expect_ecrec_offset = G.ecrec.offset_start_central_directory +
|
|
G.ecrec.size_central_directory;
|
|
|
|
#ifndef NO_ZIPINFO
|
|
if (uO.zipinfo_mode) {
|
|
/* In ZipInfo mode, additional info about the data found in the
|
|
end-of-central-directory areas is printed out.
|
|
*/
|
|
zi_end_central(__G);
|
|
}
|
|
#endif
|
|
|
|
return error_in_archive;
|
|
|
|
} /* end function find_ecrec() */
|
|
|
|
|
|
|
|
|
|
|
|
/********************************/
|
|
/* Function process_zip_cmmnt() */
|
|
/********************************/
|
|
|
|
static int process_zip_cmmnt(__G) /* return PK-type error code */
|
|
__GDEF
|
|
{
|
|
int error = PK_COOL;
|
|
|
|
|
|
/*---------------------------------------------------------------------------
|
|
Get the zipfile comment (up to 64KB long), if any, and print it out.
|
|
---------------------------------------------------------------------------*/
|
|
|
|
#ifdef WINDLL
|
|
/* for comment button: */
|
|
if ((!G.fValidate) && (G.lpUserFunctions != NULL))
|
|
G.lpUserFunctions->cchComment = G.ecrec.zipfile_comment_length;
|
|
#endif /* WINDLL */
|
|
|
|
#ifndef NO_ZIPINFO
|
|
/* ZipInfo, verbose format */
|
|
if (uO.zipinfo_mode && uO.lflag > 9) {
|
|
/*-------------------------------------------------------------------
|
|
Get the zipfile comment, if any, and print it out.
|
|
(Comment may be up to 64KB long. May the fleas of a thousand
|
|
camels infest the arm-pits of anyone who actually takes advantage
|
|
of this fact.)
|
|
-------------------------------------------------------------------*/
|
|
|
|
if (!G.ecrec.zipfile_comment_length)
|
|
Info(slide, 0, ((char *)slide, LoadFarString(NoZipfileComment)));
|
|
else {
|
|
Info(slide, 0, ((char *)slide, LoadFarString(ZipfileCommentDesc),
|
|
G.ecrec.zipfile_comment_length));
|
|
Info(slide, 0, ((char *)slide, LoadFarString(ZipfileCommBegin)));
|
|
if (do_string(__G__ G.ecrec.zipfile_comment_length, DISPLAY))
|
|
error = PK_WARN;
|
|
Info(slide, 0, ((char *)slide, LoadFarString(ZipfileCommEnd)));
|
|
if (error)
|
|
Info(slide, 0, ((char *)slide,
|
|
LoadFarString(ZipfileCommTrunc2)));
|
|
} /* endif (comment exists) */
|
|
|
|
/* ZipInfo, non-verbose mode: print zipfile comment only if requested */
|
|
} else if (G.ecrec.zipfile_comment_length &&
|
|
(uO.zflag > 0) && uO.zipinfo_mode) {
|
|
if (do_string(__G__ G.ecrec.zipfile_comment_length, DISPLAY)) {
|
|
Info(slide, 0x401, ((char *)slide,
|
|
LoadFarString(ZipfileCommTrunc1)));
|
|
error = PK_WARN;
|
|
}
|
|
} else
|
|
#endif /* !NO_ZIPINFO */
|
|
if ( G.ecrec.zipfile_comment_length &&
|
|
(uO.zflag > 0
|
|
#ifndef WINDLL
|
|
|| (uO.zflag == 0
|
|
# ifndef NO_ZIPINFO
|
|
&& !uO.zipinfo_mode
|
|
# endif
|
|
# ifdef TIMESTAMP
|
|
&& !uO.T_flag
|
|
# endif
|
|
&& !uO.qflag)
|
|
#endif /* !WINDLL */
|
|
) )
|
|
{
|
|
if (do_string(__G__ G.ecrec.zipfile_comment_length,
|
|
#if (defined(SFX) && defined(CHEAP_SFX_AUTORUN))
|
|
# ifndef NO_ZIPINFO
|
|
(oU.zipinfo_mode ? DISPLAY : CHECK_AUTORUN)
|
|
# else
|
|
CHECK_AUTORUN
|
|
# endif
|
|
#else
|
|
DISPLAY
|
|
#endif
|
|
))
|
|
{
|
|
Info(slide, 0x401, ((char *)slide,
|
|
LoadFarString(ZipfileCommTrunc1)));
|
|
error = PK_WARN;
|
|
}
|
|
}
|
|
#if (defined(SFX) && defined(CHEAP_SFX_AUTORUN))
|
|
else if (G.ecrec.zipfile_comment_length) {
|
|
if (do_string(__G__ G.ecrec.zipfile_comment_length, CHECK_AUTORUN_Q))
|
|
{
|
|
Info(slide, 0x401, ((char *)slide,
|
|
LoadFarString(ZipfileCommTrunc1)));
|
|
error = PK_WARN;
|
|
}
|
|
}
|
|
#endif
|
|
return error;
|
|
|
|
} /* end function process_zip_cmmnt() */
|
|
|
|
|
|
|
|
|
|
|
|
/************************************/
|
|
/* Function process_cdir_file_hdr() */
|
|
/************************************/
|
|
|
|
int process_cdir_file_hdr(__G) /* return PK-type error code */
|
|
__GDEF
|
|
{
|
|
int error;
|
|
|
|
|
|
/*---------------------------------------------------------------------------
|
|
Get central directory info, save host and method numbers, and set flag
|
|
for lowercase conversion of filename, depending on the OS from which the
|
|
file is coming.
|
|
---------------------------------------------------------------------------*/
|
|
|
|
if ((error = get_cdir_ent(__G)) != 0)
|
|
return error;
|
|
|
|
G.pInfo->hostver = G.crec.version_made_by[0];
|
|
G.pInfo->hostnum = MIN(G.crec.version_made_by[1], NUM_HOSTS);
|
|
/* extnum = MIN(crec.version_needed_to_extract[1], NUM_HOSTS); */
|
|
|
|
G.pInfo->lcflag = 0;
|
|
if (uO.L_flag == 1) /* name conversion for monocase systems */
|
|
switch (G.pInfo->hostnum) {
|
|
case FS_FAT_: /* PKZIP and zip -k store in uppercase */
|
|
case CPM_: /* like MS-DOS, right? */
|
|
case VM_CMS_: /* all caps? */
|
|
case MVS_: /* all caps? */
|
|
case TANDEM_:
|
|
case TOPS20_:
|
|
case VMS_: /* our Zip uses lowercase, but ASi's doesn't */
|
|
/* case Z_SYSTEM_: ? */
|
|
/* case QDOS_: ? */
|
|
G.pInfo->lcflag = 1; /* convert filename to lowercase */
|
|
break;
|
|
|
|
default: /* AMIGA_, FS_HPFS_, FS_NTFS_, MAC_, UNIX_, ATARI_, */
|
|
break; /* FS_VFAT_, ATHEOS_, BEOS_ (Z_SYSTEM_), THEOS_: */
|
|
/* no conversion */
|
|
}
|
|
else if (uO.L_flag > 1) /* let -LL force lower case for all names */
|
|
G.pInfo->lcflag = 1;
|
|
|
|
/* do Amigas (AMIGA_) also have volume labels? */
|
|
if (IS_VOLID(G.crec.external_file_attributes) &&
|
|
(G.pInfo->hostnum == FS_FAT_ || G.pInfo->hostnum == FS_HPFS_ ||
|
|
G.pInfo->hostnum == FS_NTFS_ || G.pInfo->hostnum == ATARI_))
|
|
{
|
|
G.pInfo->vollabel = TRUE;
|
|
G.pInfo->lcflag = 0; /* preserve case of volume labels */
|
|
} else
|
|
G.pInfo->vollabel = FALSE;
|
|
|
|
/* this flag is needed to detect archives made by "PKZIP for Unix" when
|
|
deciding which kind of codepage conversion has to be applied to
|
|
strings (see do_string() function in fileio.c) */
|
|
G.pInfo->HasUxAtt = (G.crec.external_file_attributes & 0xffff0000L) != 0L;
|
|
|
|
#ifdef UNICODE_SUPPORT
|
|
/* remember the state of GPB11 (General Purpuse Bit 11) which indicates
|
|
that the standard path and comment are UTF-8. */
|
|
G.pInfo->GPFIsUTF8
|
|
= (G.crec.general_purpose_bit_flag & (1 << 11)) == (1 << 11);
|
|
#endif
|
|
|
|
return PK_COOL;
|
|
|
|
} /* end function process_cdir_file_hdr() */
|
|
|
|
|
|
|
|
|
|
|
|
/***************************/
|
|
/* Function get_cdir_ent() */
|
|
/***************************/
|
|
|
|
static int get_cdir_ent(__G) /* return PK-type error code */
|
|
__GDEF
|
|
{
|
|
cdir_byte_hdr byterec;
|
|
|
|
|
|
/*---------------------------------------------------------------------------
|
|
Read the next central directory entry and do any necessary machine-type
|
|
conversions (byte ordering, structure padding compensation--do so by
|
|
copying the data from the array into which it was read (byterec) to the
|
|
usable struct (crec)).
|
|
---------------------------------------------------------------------------*/
|
|
|
|
if (readbuf(__G__ (char *)byterec, CREC_SIZE) == 0)
|
|
return PK_EOF;
|
|
|
|
G.crec.version_made_by[0] = byterec[C_VERSION_MADE_BY_0];
|
|
G.crec.version_made_by[1] = byterec[C_VERSION_MADE_BY_1];
|
|
G.crec.version_needed_to_extract[0] =
|
|
byterec[C_VERSION_NEEDED_TO_EXTRACT_0];
|
|
G.crec.version_needed_to_extract[1] =
|
|
byterec[C_VERSION_NEEDED_TO_EXTRACT_1];
|
|
|
|
G.crec.general_purpose_bit_flag =
|
|
makeword(&byterec[C_GENERAL_PURPOSE_BIT_FLAG]);
|
|
G.crec.compression_method =
|
|
makeword(&byterec[C_COMPRESSION_METHOD]);
|
|
G.crec.last_mod_dos_datetime =
|
|
makelong(&byterec[C_LAST_MOD_DOS_DATETIME]);
|
|
G.crec.crc32 =
|
|
makelong(&byterec[C_CRC32]);
|
|
G.crec.csize =
|
|
makelong(&byterec[C_COMPRESSED_SIZE]);
|
|
G.crec.ucsize =
|
|
makelong(&byterec[C_UNCOMPRESSED_SIZE]);
|
|
G.crec.filename_length =
|
|
makeword(&byterec[C_FILENAME_LENGTH]);
|
|
G.crec.extra_field_length =
|
|
makeword(&byterec[C_EXTRA_FIELD_LENGTH]);
|
|
G.crec.file_comment_length =
|
|
makeword(&byterec[C_FILE_COMMENT_LENGTH]);
|
|
G.crec.disk_number_start =
|
|
makeword(&byterec[C_DISK_NUMBER_START]);
|
|
G.crec.internal_file_attributes =
|
|
makeword(&byterec[C_INTERNAL_FILE_ATTRIBUTES]);
|
|
G.crec.external_file_attributes =
|
|
makelong(&byterec[C_EXTERNAL_FILE_ATTRIBUTES]); /* LONG, not word! */
|
|
G.crec.relative_offset_local_header =
|
|
makelong(&byterec[C_RELATIVE_OFFSET_LOCAL_HEADER]);
|
|
|
|
return PK_COOL;
|
|
|
|
} /* end function get_cdir_ent() */
|
|
|
|
|
|
|
|
|
|
|
|
/*************************************/
|
|
/* Function process_local_file_hdr() */
|
|
/*************************************/
|
|
|
|
int process_local_file_hdr(__G) /* return PK-type error code */
|
|
__GDEF
|
|
{
|
|
local_byte_hdr byterec;
|
|
|
|
|
|
/*---------------------------------------------------------------------------
|
|
Read the next local file header and do any necessary machine-type con-
|
|
versions (byte ordering, structure padding compensation--do so by copy-
|
|
ing the data from the array into which it was read (byterec) to the
|
|
usable struct (lrec)).
|
|
---------------------------------------------------------------------------*/
|
|
|
|
if (readbuf(__G__ (char *)byterec, LREC_SIZE) == 0)
|
|
return PK_EOF;
|
|
|
|
G.lrec.version_needed_to_extract[0] =
|
|
byterec[L_VERSION_NEEDED_TO_EXTRACT_0];
|
|
G.lrec.version_needed_to_extract[1] =
|
|
byterec[L_VERSION_NEEDED_TO_EXTRACT_1];
|
|
|
|
G.lrec.general_purpose_bit_flag =
|
|
makeword(&byterec[L_GENERAL_PURPOSE_BIT_FLAG]);
|
|
G.lrec.compression_method = makeword(&byterec[L_COMPRESSION_METHOD]);
|
|
G.lrec.last_mod_dos_datetime = makelong(&byterec[L_LAST_MOD_DOS_DATETIME]);
|
|
G.lrec.crc32 = makelong(&byterec[L_CRC32]);
|
|
G.lrec.csize = makelong(&byterec[L_COMPRESSED_SIZE]);
|
|
G.lrec.ucsize = makelong(&byterec[L_UNCOMPRESSED_SIZE]);
|
|
G.lrec.filename_length = makeword(&byterec[L_FILENAME_LENGTH]);
|
|
G.lrec.extra_field_length = makeword(&byterec[L_EXTRA_FIELD_LENGTH]);
|
|
|
|
if ((G.lrec.general_purpose_bit_flag & 8) != 0) {
|
|
/* can't trust local header, use central directory: */
|
|
G.lrec.crc32 = G.pInfo->crc;
|
|
G.lrec.csize = G.pInfo->compr_size;
|
|
G.lrec.ucsize = G.pInfo->uncompr_size;
|
|
}
|
|
|
|
G.csize = G.lrec.csize;
|
|
|
|
return PK_COOL;
|
|
|
|
} /* end function process_local_file_hdr() */
|
|
|
|
|
|
/*******************************/
|
|
/* Function getZip64Data() */
|
|
/*******************************/
|
|
|
|
int getZip64Data(__G__ ef_buf, ef_len)
|
|
__GDEF
|
|
ZCONST uch *ef_buf; /* buffer containing extra field */
|
|
unsigned ef_len; /* total length of extra field */
|
|
{
|
|
unsigned eb_id;
|
|
unsigned eb_len;
|
|
|
|
/*---------------------------------------------------------------------------
|
|
This function scans the extra field for zip64 information, ie 8-byte
|
|
versions of compressed file size, uncompressed file size, relative offset
|
|
and a 4-byte version of disk start number.
|
|
Sets both local header and central header fields. Not terribly clever,
|
|
but it means that this procedure is only called in one place.
|
|
---------------------------------------------------------------------------*/
|
|
|
|
if (ef_len == 0 || ef_buf == NULL)
|
|
return PK_COOL;
|
|
|
|
Trace((stderr,"\ngetZip64Data: scanning extra field of length %u\n",
|
|
ef_len));
|
|
|
|
while (ef_len >= EB_HEADSIZE) {
|
|
eb_id = makeword(EB_ID + ef_buf);
|
|
eb_len = makeword(EB_LEN + ef_buf);
|
|
|
|
if (eb_len > (ef_len - EB_HEADSIZE)) {
|
|
/* discovered some extra field inconsistency! */
|
|
Trace((stderr,
|
|
"getZip64Data: block length %u > rest ef_size %u\n", eb_len,
|
|
ef_len - EB_HEADSIZE));
|
|
break;
|
|
}
|
|
if (eb_id == EF_PKSZ64) {
|
|
|
|
int offset = EB_HEADSIZE;
|
|
|
|
if (G.crec.ucsize == 0xffffffff || G.lrec.ucsize == 0xffffffff){
|
|
G.lrec.ucsize = G.crec.ucsize = makeint64(offset + ef_buf);
|
|
offset += sizeof(G.crec.ucsize);
|
|
}
|
|
if (G.crec.csize == 0xffffffff || G.lrec.csize == 0xffffffff){
|
|
G.csize = G.lrec.csize = G.crec.csize = makeint64(offset + ef_buf);
|
|
offset += sizeof(G.crec.csize);
|
|
}
|
|
if (G.crec.relative_offset_local_header == 0xffffffff){
|
|
G.crec.relative_offset_local_header = makeint64(offset + ef_buf);
|
|
offset += sizeof(G.crec.relative_offset_local_header);
|
|
}
|
|
if (G.crec.disk_number_start == 0xffff){
|
|
G.crec.disk_number_start = (zuvl_t)makelong(offset + ef_buf);
|
|
offset += sizeof(G.crec.disk_number_start);
|
|
}
|
|
}
|
|
|
|
/* Skip this extra field block */
|
|
ef_buf += (eb_len + EB_HEADSIZE);
|
|
ef_len -= (eb_len + EB_HEADSIZE);
|
|
}
|
|
|
|
return PK_COOL;
|
|
} /* end function getZip64Data() */
|
|
|
|
|
|
#ifdef UNICODE_SUPPORT
|
|
|
|
/*******************************/
|
|
/* Function getUnicodeData() */
|
|
/*******************************/
|
|
|
|
int getUnicodeData(__G__ ef_buf, ef_len)
|
|
__GDEF
|
|
ZCONST uch *ef_buf; /* buffer containing extra field */
|
|
unsigned ef_len; /* total length of extra field */
|
|
{
|
|
unsigned eb_id;
|
|
unsigned eb_len;
|
|
|
|
/*---------------------------------------------------------------------------
|
|
This function scans the extra field for Unicode information, ie UTF-8
|
|
path extra fields.
|
|
|
|
On return, G.unipath_filename =
|
|
NULL, if no Unicode path extra field or error
|
|
"", if the standard path is UTF-8 (free when done)
|
|
null-terminated UTF-8 path (free when done)
|
|
Return PK_COOL if no error.
|
|
---------------------------------------------------------------------------*/
|
|
|
|
G.unipath_filename = NULL;
|
|
|
|
if (ef_len == 0 || ef_buf == NULL)
|
|
return PK_COOL;
|
|
|
|
Trace((stderr,"\ngetUnicodeData: scanning extra field of length %u\n",
|
|
ef_len));
|
|
|
|
while (ef_len >= EB_HEADSIZE) {
|
|
eb_id = makeword(EB_ID + ef_buf);
|
|
eb_len = makeword(EB_LEN + ef_buf);
|
|
|
|
if (eb_len > (ef_len - EB_HEADSIZE)) {
|
|
/* discovered some extra field inconsistency! */
|
|
Trace((stderr,
|
|
"getUnicodeData: block length %u > rest ef_size %u\n", eb_len,
|
|
ef_len - EB_HEADSIZE));
|
|
break;
|
|
}
|
|
if (eb_id == EF_UNIPATH) {
|
|
|
|
int offset = EB_HEADSIZE;
|
|
ush ULen = eb_len - 5;
|
|
ulg chksum = CRCVAL_INITIAL;
|
|
|
|
/* version */
|
|
G.unipath_version = (uch) *(offset + ef_buf);
|
|
offset += 1;
|
|
if (G.unipath_version > 1) {
|
|
/* can do only version 1 */
|
|
Info(slide, 0x401, ((char *)slide,
|
|
LoadFarString(UnicodeVersionError)));
|
|
return PK_ERR;
|
|
}
|
|
|
|
/* filename CRC */
|
|
G.unipath_checksum = makelong(offset + ef_buf);
|
|
offset += 4;
|
|
|
|
/*
|
|
* Compute 32-bit crc
|
|
*/
|
|
|
|
chksum = crc32(chksum, (uch *)(G.filename_full),
|
|
strlen(G.filename_full));
|
|
|
|
/* If the checksums's don't match then likely filename has been
|
|
* modified and the Unicode Path is no longer valid.
|
|
*/
|
|
if (chksum != G.unipath_checksum) {
|
|
Info(slide, 0x401, ((char *)slide,
|
|
LoadFarString(UnicodeMismatchError)));
|
|
if (G.unicode_mismatch == 1) {
|
|
/* warn and continue */
|
|
} else if (G.unicode_mismatch == 2) {
|
|
/* ignore and continue */
|
|
} else if (G.unicode_mismatch == 0) {
|
|
}
|
|
return PK_ERR;
|
|
}
|
|
|
|
/* UTF-8 Path */
|
|
if ((G.unipath_filename = malloc(ULen + 1)) == NULL) {
|
|
return PK_ERR;
|
|
}
|
|
if (ULen == 0) {
|
|
/* standard path is UTF-8 so use that */
|
|
G.unipath_filename[0] = '\0';
|
|
} else {
|
|
/* UTF-8 path */
|
|
strncpy(G.unipath_filename,
|
|
(ZCONST char *)(offset + ef_buf), ULen);
|
|
G.unipath_filename[ULen] = '\0';
|
|
}
|
|
}
|
|
|
|
/* Skip this extra field block */
|
|
ef_buf += (eb_len + EB_HEADSIZE);
|
|
ef_len -= (eb_len + EB_HEADSIZE);
|
|
}
|
|
|
|
return PK_COOL;
|
|
} /* end function getUnicodeData() */
|
|
|
|
|
|
|
|
|
|
#ifdef UNICODE_WCHAR
|
|
/*---------------------------------------------
|
|
* Unicode conversion functions
|
|
*
|
|
* Based on functions provided by Paul Kienitz
|
|
*
|
|
*---------------------------------------------
|
|
*/
|
|
|
|
/*
|
|
NOTES APPLICABLE TO ALL STRING FUNCTIONS:
|
|
|
|
All of the x_to_y functions take parameters for an output buffer and
|
|
its available length, and return an int. The value returned is the
|
|
length of the string that the input produces, which may be larger than
|
|
the provided buffer length. If the returned value is less than the
|
|
buffer length, then the contents of the buffer will be null-terminated;
|
|
otherwise, it will not be terminated and may be invalid, possibly
|
|
stopping in the middle of a multibyte sequence.
|
|
|
|
In all cases you may pass NULL as the buffer and/or 0 as the length, if
|
|
you just want to learn how much space the string is going to require.
|
|
|
|
The functions will return -1 if the input is invalid UTF-8 or cannot be
|
|
encoded as UTF-8.
|
|
*/
|
|
|
|
static int utf8_char_bytes OF((ZCONST char *utf8));
|
|
static ulg ucs4_char_from_utf8 OF((ZCONST char **utf8));
|
|
static int utf8_to_ucs4_string OF((ZCONST char *utf8, ulg *ucs4buf,
|
|
int buflen));
|
|
|
|
/* utility functions for managing UTF-8 and UCS-4 strings */
|
|
|
|
|
|
/* utf8_char_bytes
|
|
*
|
|
* Returns the number of bytes used by the first character in a UTF-8
|
|
* string, or -1 if the UTF-8 is invalid or null.
|
|
*/
|
|
static int utf8_char_bytes(utf8)
|
|
ZCONST char *utf8;
|
|
{
|
|
int t, r;
|
|
unsigned lead;
|
|
|
|
if (!utf8)
|
|
return -1; /* no input */
|
|
lead = (unsigned char) *utf8;
|
|
if (lead < 0x80)
|
|
r = 1; /* an ascii-7 character */
|
|
else if (lead < 0xC0)
|
|
return -1; /* error: trailing byte without lead byte */
|
|
else if (lead < 0xE0)
|
|
r = 2; /* an 11 bit character */
|
|
else if (lead < 0xF0)
|
|
r = 3; /* a 16 bit character */
|
|
else if (lead < 0xF8)
|
|
r = 4; /* a 21 bit character (the most currently used) */
|
|
else if (lead < 0xFC)
|
|
r = 5; /* a 26 bit character (shouldn't happen) */
|
|
else if (lead < 0xFE)
|
|
r = 6; /* a 31 bit character (shouldn't happen) */
|
|
else
|
|
return -1; /* error: invalid lead byte */
|
|
for (t = 1; t < r; t++)
|
|
if ((unsigned char) utf8[t] < 0x80 || (unsigned char) utf8[t] >= 0xC0)
|
|
return -1; /* error: not enough valid trailing bytes */
|
|
return r;
|
|
}
|
|
|
|
|
|
/* ucs4_char_from_utf8
|
|
*
|
|
* Given a reference to a pointer into a UTF-8 string, returns the next
|
|
* UCS-4 character and advances the pointer to the next character sequence.
|
|
* Returns ~0 (= -1 in twos-complement notation) and does not advance the
|
|
* pointer when input is ill-formed.
|
|
*/
|
|
static ulg ucs4_char_from_utf8(utf8)
|
|
ZCONST char **utf8;
|
|
{
|
|
ulg ret;
|
|
int t, bytes;
|
|
|
|
if (!utf8)
|
|
return ~0L; /* no input */
|
|
bytes = utf8_char_bytes(*utf8);
|
|
if (bytes <= 0)
|
|
return ~0L; /* invalid input */
|
|
if (bytes == 1)
|
|
ret = **utf8; /* ascii-7 */
|
|
else
|
|
ret = **utf8 & (0x7F >> bytes); /* lead byte of a multibyte sequence */
|
|
(*utf8)++;
|
|
for (t = 1; t < bytes; t++) /* consume trailing bytes */
|
|
ret = (ret << 6) | (*((*utf8)++) & 0x3F);
|
|
return (zwchar) ret;
|
|
}
|
|
|
|
|
|
#if 0 /* currently unused */
|
|
/* utf8_from_ucs4_char - Convert UCS char to UTF-8
|
|
*
|
|
* Returns the number of bytes put into utf8buf to represent ch, from 1 to 6,
|
|
* or -1 if ch is too large to represent. utf8buf must have room for 6 bytes.
|
|
*/
|
|
static int utf8_from_ucs4_char(utf8buf, ch)
|
|
char *utf8buf;
|
|
ulg ch;
|
|
{
|
|
int trailing = 0;
|
|
int leadmask = 0x80;
|
|
int leadbits = 0x3F;
|
|
int tch = ch;
|
|
int ret;
|
|
|
|
if (ch > 0x7FFFFFFFL)
|
|
return -1; /* UTF-8 can represent 31 bits */
|
|
if (ch < 0x7F)
|
|
{
|
|
*utf8buf++ = (char) ch; /* ascii-7 */
|
|
return 1;
|
|
}
|
|
do {
|
|
trailing++;
|
|
leadmask = (leadmask >> 1) | 0x80;
|
|
leadbits >>= 1;
|
|
tch >>= 6;
|
|
} while (tch & ~leadbits);
|
|
ret = trailing + 1;
|
|
/* produce lead byte */
|
|
*utf8buf++ = (char) (leadmask | (ch >> (6 * trailing)));
|
|
while (--trailing >= 0)
|
|
/* produce trailing bytes */
|
|
*utf8buf++ = (char) (0x80 | ((ch >> (6 * trailing)) & 0x3F));
|
|
return ret;
|
|
}
|
|
#endif /* unused */
|
|
|
|
|
|
/*===================================================================*/
|
|
|
|
/* utf8_to_ucs4_string - convert UTF-8 string to UCS string
|
|
*
|
|
* Return UCS count. Now returns int so can return -1.
|
|
*/
|
|
static int utf8_to_ucs4_string(utf8, ucs4buf, buflen)
|
|
ZCONST char *utf8;
|
|
ulg *ucs4buf;
|
|
int buflen;
|
|
{
|
|
int count = 0;
|
|
|
|
for (;;)
|
|
{
|
|
ulg ch = ucs4_char_from_utf8(&utf8);
|
|
if (ch == ~0L)
|
|
return -1;
|
|
else
|
|
{
|
|
if (ucs4buf && count < buflen)
|
|
ucs4buf[count] = ch;
|
|
if (ch == 0)
|
|
return count;
|
|
count++;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#if 0 /* currently unused */
|
|
/* ucs4_string_to_utf8
|
|
*
|
|
*
|
|
*/
|
|
static int ucs4_string_to_utf8(ucs4, utf8buf, buflen)
|
|
ZCONST ulg *ucs4;
|
|
char *utf8buf;
|
|
int buflen;
|
|
{
|
|
char mb[6];
|
|
int count = 0;
|
|
|
|
if (!ucs4)
|
|
return -1;
|
|
for (;;)
|
|
{
|
|
int mbl = utf8_from_ucs4_char(mb, *ucs4++);
|
|
int c;
|
|
if (mbl <= 0)
|
|
return -1;
|
|
/* We could optimize this a bit by passing utf8buf + count */
|
|
/* directly to utf8_from_ucs4_char when buflen >= count + 6... */
|
|
c = buflen - count;
|
|
if (mbl < c)
|
|
c = mbl;
|
|
if (utf8buf && count < buflen)
|
|
strncpy(utf8buf + count, mb, c);
|
|
if (mbl == 1 && !mb[0])
|
|
return count; /* terminating nul */
|
|
count += mbl;
|
|
}
|
|
}
|
|
|
|
|
|
/* utf8_chars
|
|
*
|
|
* Wrapper: counts the actual unicode characters in a UTF-8 string.
|
|
*/
|
|
static int utf8_chars(utf8)
|
|
ZCONST char *utf8;
|
|
{
|
|
return utf8_to_ucs4_string(utf8, NULL, 0);
|
|
}
|
|
#endif /* unused */
|
|
|
|
/* --------------------------------------------------- */
|
|
/* Unicode Support
|
|
*
|
|
* These functions common for all Unicode ports.
|
|
*
|
|
* These functions should allocate and return strings that can be
|
|
* freed with free().
|
|
*
|
|
* 8/27/05 EG
|
|
*
|
|
* Use zwchar for wide char which is unsigned long
|
|
* in zip.h and 32 bits. This avoids problems with
|
|
* different sizes of wchar_t.
|
|
*/
|
|
|
|
#if 0 /* currently unused */
|
|
/* is_ascii_string
|
|
* Checks if a string is all ascii
|
|
*/
|
|
int is_ascii_string(mbstring)
|
|
ZCONST char *mbstring;
|
|
{
|
|
char *p;
|
|
uch c;
|
|
|
|
for (p = mbstring; c = (uch)*p; p++) {
|
|
if (c > 0x7F) {
|
|
return 0;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/* local to UTF-8 */
|
|
char *local_to_utf8_string(local_string)
|
|
ZCONST char *local_string;
|
|
{
|
|
return wide_to_utf8_string(local_to_wide_string(local_string));
|
|
}
|
|
# endif /* unused */
|
|
|
|
/* wide_to_escape_string
|
|
provides a string that represents a wide char not in local char set
|
|
|
|
An initial try at an algorithm. Suggestions welcome.
|
|
|
|
According to the standard, Unicode character points are restricted to
|
|
the number range from 0 to 0x10FFFF, respective 21 bits.
|
|
For a hexadecimal notation, 2 octets are sufficient for the mostly
|
|
used characters from the "Basic Multilingual Plane", all other
|
|
Unicode characters can be represented by 3 octets (= 6 hex digits).
|
|
The Unicode standard suggests to write Unicode character points
|
|
as 4 resp. 6 hex digits, preprended by "U+".
|
|
(e.g.: U+10FFFF for the highest character point, or U+0030 for the ASCII
|
|
digit "0")
|
|
|
|
However, for the purpose of escaping non-ASCII chars in an ASCII character
|
|
stream, the "U" is not a very good escape initializer. Therefore, we
|
|
use the following convention within our Info-ZIP code:
|
|
|
|
If not an ASCII char probably need 2 bytes at least. So if
|
|
a 2-byte wide encode it as 4 hex digits with a leading #U. If
|
|
needs 3 bytes then prefix the string with #L. So
|
|
#U1234
|
|
is a 2-byte wide character with bytes 0x12 and 0x34 while
|
|
#L123456
|
|
is a 3-byte wide character with bytes 0x12, 0x34, 0x56.
|
|
On Windows, wide that need two wide characters need to be converted
|
|
to a single number.
|
|
*/
|
|
|
|
/* set this to the max bytes an escape can be */
|
|
#define MAX_ESCAPE_BYTES 8
|
|
|
|
char *wide_to_escape_string(wide_char)
|
|
zwchar wide_char;
|
|
{
|
|
int i;
|
|
zwchar w = wide_char;
|
|
uch b[sizeof(zwchar)];
|
|
char d[3];
|
|
char e[11];
|
|
int len;
|
|
char *r;
|
|
|
|
/* fill byte array with zeros */
|
|
memzero(b, sizeof(zwchar));
|
|
/* get bytes in right to left order */
|
|
for (len = 0; w; len++) {
|
|
b[len] = (char)(w % 0x100);
|
|
w /= 0x100;
|
|
}
|
|
strcpy(e, "#");
|
|
/* either 2 bytes or 3 bytes */
|
|
if (len <= 2) {
|
|
len = 2;
|
|
strcat(e, "U");
|
|
} else {
|
|
strcat(e, "L");
|
|
}
|
|
for (i = len - 1; i >= 0; i--) {
|
|
sprintf(d, "%02x", b[i]);
|
|
strcat(e, d);
|
|
}
|
|
if ((r = malloc(strlen(e) + 1)) == NULL) {
|
|
return NULL;
|
|
}
|
|
strcpy(r, e);
|
|
return r;
|
|
}
|
|
|
|
#if 0 /* currently unused */
|
|
/* returns the wide character represented by the escape string */
|
|
zwchar escape_string_to_wide(escape_string)
|
|
ZCONST char *escape_string;
|
|
{
|
|
int i;
|
|
zwchar w;
|
|
char c;
|
|
int len;
|
|
ZCONST char *e = escape_string;
|
|
|
|
if (e == NULL) {
|
|
return 0;
|
|
}
|
|
if (e[0] != '#') {
|
|
/* no leading # */
|
|
return 0;
|
|
}
|
|
len = strlen(e);
|
|
/* either #U1234 or #L123456 format */
|
|
if (len != 6 && len != 8) {
|
|
return 0;
|
|
}
|
|
w = 0;
|
|
if (e[1] == 'L') {
|
|
if (len != 8) {
|
|
return 0;
|
|
}
|
|
/* 3 bytes */
|
|
for (i = 2; i < 8; i++) {
|
|
c = e[i];
|
|
if (c < '0' || c > '9') {
|
|
return 0;
|
|
}
|
|
w = w * 0x10 + (zwchar)(c - '0');
|
|
}
|
|
} else if (e[1] == 'U') {
|
|
/* 2 bytes */
|
|
for (i = 2; i < 6; i++) {
|
|
c = e[i];
|
|
if (c < '0' || c > '9') {
|
|
return 0;
|
|
}
|
|
w = w * 0x10 + (zwchar)(c - '0');
|
|
}
|
|
}
|
|
return w;
|
|
}
|
|
#endif /* unused */
|
|
|
|
#ifndef WIN32 /* WIN32 supplies a special variant of this function */
|
|
/* convert wide character string to multi-byte character string */
|
|
char *wide_to_local_string(wide_string, escape_all)
|
|
ZCONST zwchar *wide_string;
|
|
int escape_all;
|
|
{
|
|
int i;
|
|
wchar_t wc;
|
|
int b;
|
|
int state_dependent;
|
|
int wsize = 0;
|
|
int max_bytes = MB_CUR_MAX;
|
|
char buf[9];
|
|
char *buffer = NULL;
|
|
char *local_string = NULL;
|
|
|
|
for (wsize = 0; wide_string[wsize]; wsize++) ;
|
|
|
|
if (max_bytes < MAX_ESCAPE_BYTES)
|
|
max_bytes = MAX_ESCAPE_BYTES;
|
|
|
|
if ((buffer = (char *)malloc(wsize * max_bytes + 1)) == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
/* convert it */
|
|
buffer[0] = '\0';
|
|
/* set initial state if state-dependent encoding */
|
|
wc = (wchar_t)'a';
|
|
b = wctomb(NULL, wc);
|
|
if (b == 0)
|
|
state_dependent = 0;
|
|
else
|
|
state_dependent = 1;
|
|
for (i = 0; i < wsize; i++) {
|
|
if (sizeof(wchar_t) < 4 && wide_string[i] > 0xFFFF) {
|
|
/* wchar_t probably 2 bytes */
|
|
/* could do surrogates if state_dependent and wctomb can do */
|
|
wc = zwchar_to_wchar_t_default_char;
|
|
} else {
|
|
wc = (wchar_t)wide_string[i];
|
|
}
|
|
b = wctomb(buf, wc);
|
|
if (escape_all) {
|
|
if (b == 1 && (uch)buf[0] <= 0x7f) {
|
|
/* ASCII */
|
|
strncat(buffer, buf, b);
|
|
} else {
|
|
/* use escape for wide character */
|
|
char *escape_string = wide_to_escape_string(wide_string[i]);
|
|
strcat(buffer, escape_string);
|
|
free(escape_string);
|
|
}
|
|
} else if (b > 0) {
|
|
/* multi-byte char */
|
|
strncat(buffer, buf, b);
|
|
} else {
|
|
/* no MB for this wide */
|
|
/* use escape for wide character */
|
|
char *escape_string = wide_to_escape_string(wide_string[i]);
|
|
strcat(buffer, escape_string);
|
|
free(escape_string);
|
|
}
|
|
}
|
|
if ((local_string = (char *)malloc(strlen(buffer) + 1)) != NULL) {
|
|
strcpy(local_string, buffer);
|
|
}
|
|
free(buffer);
|
|
|
|
return local_string;
|
|
}
|
|
#endif /* !WIN32 */
|
|
|
|
#if 0 /* currently unused */
|
|
/* convert local string to display character set string */
|
|
char *local_to_display_string(local_string)
|
|
ZCONST char *local_string;
|
|
{
|
|
char *display_string;
|
|
|
|
/* For Windows, OEM string should never be bigger than ANSI string, says
|
|
CharToOem description.
|
|
For all other ports, just make a copy of local_string.
|
|
*/
|
|
if ((display_string = (char *)malloc(strlen(local_string) + 1)) == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
strcpy(display_string, local_string);
|
|
|
|
#ifdef EBCDIC
|
|
{
|
|
char *ebc;
|
|
|
|
if ((ebc = malloc(strlen(display_string) + 1)) == NULL) {
|
|
return NULL;
|
|
}
|
|
strtoebc(ebc, display_string);
|
|
free(display_string);
|
|
display_string = ebc;
|
|
}
|
|
#endif
|
|
|
|
return display_string;
|
|
}
|
|
#endif /* unused */
|
|
|
|
/* UTF-8 to local */
|
|
char *utf8_to_local_string(utf8_string, escape_all)
|
|
ZCONST char *utf8_string;
|
|
int escape_all;
|
|
{
|
|
zwchar *wide = utf8_to_wide_string(utf8_string);
|
|
char *loc = wide_to_local_string(wide, escape_all);
|
|
free(wide);
|
|
return loc;
|
|
}
|
|
|
|
#if 0 /* currently unused */
|
|
/* convert multi-byte character string to wide character string */
|
|
zwchar *local_to_wide_string(local_string)
|
|
ZCONST char *local_string;
|
|
{
|
|
int wsize;
|
|
wchar_t *wc_string;
|
|
zwchar *wide_string;
|
|
|
|
/* for now try to convert as string - fails if a bad char in string */
|
|
wsize = mbstowcs(NULL, local_string, strlen(local_string) + 1);
|
|
if (wsize == (size_t)-1) {
|
|
/* could not convert */
|
|
return NULL;
|
|
}
|
|
|
|
/* convert it */
|
|
if ((wc_string = (wchar_t *)malloc((wsize + 1) * sizeof(wchar_t))) == NULL) {
|
|
return NULL;
|
|
}
|
|
wsize = mbstowcs(wc_string, local_string, strlen(local_string) + 1);
|
|
wc_string[wsize] = (wchar_t) 0;
|
|
|
|
/* in case wchar_t is not zwchar */
|
|
if ((wide_string = (zwchar *)malloc((wsize + 1) * sizeof(zwchar))) == NULL) {
|
|
return NULL;
|
|
}
|
|
for (wsize = 0; wide_string[wsize] = (zwchar)wc_string[wsize]; wsize++) ;
|
|
wide_string[wsize] = (zwchar) 0;
|
|
free(wc_string);
|
|
|
|
return wide_string;
|
|
}
|
|
|
|
|
|
/* convert wide string to UTF-8 */
|
|
char *wide_to_utf8_string(wide_string)
|
|
ZCONST zwchar *wide_string;
|
|
{
|
|
int mbcount;
|
|
char *utf8_string;
|
|
|
|
/* get size of utf8 string */
|
|
mbcount = ucs4_string_to_utf8(wide_string, NULL, 0);
|
|
if (mbcount == -1)
|
|
return NULL;
|
|
if ((utf8_string = (char *) malloc(mbcount + 1)) == NULL) {
|
|
return NULL;
|
|
}
|
|
mbcount = ucs4_string_to_utf8(wide_string, utf8_string, mbcount + 1);
|
|
if (mbcount == -1)
|
|
return NULL;
|
|
|
|
return utf8_string;
|
|
}
|
|
#endif /* unused */
|
|
|
|
/* convert UTF-8 string to wide string */
|
|
zwchar *utf8_to_wide_string(utf8_string)
|
|
ZCONST char *utf8_string;
|
|
{
|
|
int wcount;
|
|
zwchar *wide_string;
|
|
|
|
wcount = utf8_to_ucs4_string(utf8_string, NULL, 0);
|
|
if (wcount == -1)
|
|
return NULL;
|
|
if ((wide_string = (zwchar *) malloc((wcount + 1) * sizeof(zwchar)))
|
|
== NULL) {
|
|
return NULL;
|
|
}
|
|
wcount = utf8_to_ucs4_string(utf8_string, wide_string, wcount + 1);
|
|
|
|
return wide_string;
|
|
}
|
|
|
|
#endif /* UNICODE_WCHAR */
|
|
#endif /* UNICODE_SUPPORT */
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef USE_EF_UT_TIME
|
|
|
|
#ifdef IZ_HAVE_UXUIDGID
|
|
static int read_ux3_value(dbuf, uidgid_sz, p_uidgid)
|
|
ZCONST uch *dbuf; /* buffer a uid or gid value */
|
|
unsigned uidgid_sz; /* size of uid/gid value */
|
|
ulg *p_uidgid; /* return storage: uid or gid value */
|
|
{
|
|
zusz_t uidgid64;
|
|
|
|
switch (uidgid_sz) {
|
|
case 2:
|
|
*p_uidgid = (ulg)makeword(dbuf);
|
|
break;
|
|
case 4:
|
|
*p_uidgid = (ulg)makelong(dbuf);
|
|
break;
|
|
case 8:
|
|
uidgid64 = makeint64(dbuf);
|
|
#ifndef LARGE_FILE_SUPPORT
|
|
if (uidgid64 == (zusz_t)0xffffffffL)
|
|
return FALSE;
|
|
#endif
|
|
*p_uidgid = (ulg)uidgid64;
|
|
if ((zusz_t)(*p_uidgid) != uidgid64)
|
|
return FALSE;
|
|
break;
|
|
}
|
|
return TRUE;
|
|
}
|
|
#endif /* IZ_HAVE_UXUIDGID */
|
|
|
|
|
|
/*******************************/
|
|
/* Function ef_scan_for_izux() */
|
|
/*******************************/
|
|
|
|
unsigned ef_scan_for_izux(ef_buf, ef_len, ef_is_c, dos_mdatetime,
|
|
z_utim, z_uidgid)
|
|
ZCONST uch *ef_buf; /* buffer containing extra field */
|
|
unsigned ef_len; /* total length of extra field */
|
|
int ef_is_c; /* flag indicating "is central extra field" */
|
|
ulg dos_mdatetime; /* last_mod_file_date_time in DOS format */
|
|
iztimes *z_utim; /* return storage: atime, mtime, ctime */
|
|
ulg *z_uidgid; /* return storage: uid and gid */
|
|
{
|
|
unsigned flags = 0;
|
|
unsigned eb_id;
|
|
unsigned eb_len;
|
|
int have_new_type_eb = 0;
|
|
long i_time; /* buffer for Unix style 32-bit integer time value */
|
|
#ifdef TIME_T_TYPE_DOUBLE
|
|
int ut_in_archive_sgn = 0;
|
|
#else
|
|
int ut_zip_unzip_compatible = FALSE;
|
|
#endif
|
|
|
|
/*---------------------------------------------------------------------------
|
|
This function scans the extra field for EF_TIME, EF_IZUNIX2, EF_IZUNIX, or
|
|
EF_PKUNIX blocks containing Unix-style time_t (GMT) values for the entry's
|
|
access, creation, and modification time.
|
|
If a valid block is found, the time stamps are copied to the iztimes
|
|
structure (provided the z_utim pointer is not NULL).
|
|
If a IZUNIX2 block is found or the IZUNIX block contains UID/GID fields,
|
|
and the z_uidgid array pointer is valid (!= NULL), the owner info is
|
|
transfered as well.
|
|
The presence of an EF_TIME or EF_IZUNIX2 block results in ignoring all
|
|
data from probably present obsolete EF_IZUNIX blocks.
|
|
If multiple blocks of the same type are found, only the information from
|
|
the last block is used.
|
|
The return value is a combination of the EF_TIME Flags field with an
|
|
additional flag bit indicating the presence of valid UID/GID info,
|
|
or 0 in case of failure.
|
|
---------------------------------------------------------------------------*/
|
|
|
|
if (ef_len == 0 || ef_buf == NULL || (z_utim == 0 && z_uidgid == NULL))
|
|
return 0;
|
|
|
|
TTrace((stderr,"\nef_scan_for_izux: scanning extra field of length %u\n",
|
|
ef_len));
|
|
|
|
while (ef_len >= EB_HEADSIZE) {
|
|
eb_id = makeword(EB_ID + ef_buf);
|
|
eb_len = makeword(EB_LEN + ef_buf);
|
|
|
|
if (eb_len > (ef_len - EB_HEADSIZE)) {
|
|
/* discovered some extra field inconsistency! */
|
|
TTrace((stderr,
|
|
"ef_scan_for_izux: block length %u > rest ef_size %u\n", eb_len,
|
|
ef_len - EB_HEADSIZE));
|
|
break;
|
|
}
|
|
|
|
switch (eb_id) {
|
|
case EF_TIME:
|
|
flags &= ~0x0ff; /* ignore previous IZUNIX or EF_TIME fields */
|
|
have_new_type_eb = 1;
|
|
if ( eb_len >= EB_UT_MINLEN && z_utim != NULL) {
|
|
unsigned eb_idx = EB_UT_TIME1;
|
|
TTrace((stderr,"ef_scan_for_izux: found TIME extra field\n"));
|
|
flags |= (ef_buf[EB_HEADSIZE+EB_UT_FLAGS] & 0x0ff);
|
|
if ((flags & EB_UT_FL_MTIME)) {
|
|
if ((eb_idx+4) <= eb_len) {
|
|
i_time = (long)makelong((EB_HEADSIZE+eb_idx) + ef_buf);
|
|
eb_idx += 4;
|
|
TTrace((stderr," UT e.f. modification time = %ld\n",
|
|
i_time));
|
|
|
|
#ifdef TIME_T_TYPE_DOUBLE
|
|
if ((ulg)(i_time) & (ulg)(0x80000000L)) {
|
|
if (dos_mdatetime == DOSTIME_MINIMUM) {
|
|
ut_in_archive_sgn = -1;
|
|
z_utim->mtime =
|
|
(time_t)((long)i_time | (~(long)0x7fffffffL));
|
|
} else if (dos_mdatetime >= DOSTIME_2038_01_18) {
|
|
ut_in_archive_sgn = 1;
|
|
z_utim->mtime =
|
|
(time_t)((ulg)i_time & (ulg)0xffffffffL);
|
|
} else {
|
|
ut_in_archive_sgn = 0;
|
|
/* cannot determine sign of mtime;
|
|
without modtime: ignore complete UT field */
|
|
flags &= ~0x0ff; /* no time_t times available */
|
|
TTrace((stderr,
|
|
" UT modtime range error; ignore e.f.!\n"));
|
|
break; /* stop scanning this field */
|
|
}
|
|
} else {
|
|
/* cannot determine, safe assumption is FALSE */
|
|
ut_in_archive_sgn = 0;
|
|
z_utim->mtime = (time_t)i_time;
|
|
}
|
|
#else /* !TIME_T_TYPE_DOUBLE */
|
|
if ((ulg)(i_time) & (ulg)(0x80000000L)) {
|
|
ut_zip_unzip_compatible =
|
|
((time_t)0x80000000L < (time_t)0L)
|
|
? (dos_mdatetime == DOSTIME_MINIMUM)
|
|
: (dos_mdatetime >= DOSTIME_2038_01_18);
|
|
if (!ut_zip_unzip_compatible) {
|
|
/* UnZip interprets mtime differently than Zip;
|
|
without modtime: ignore complete UT field */
|
|
flags &= ~0x0ff; /* no time_t times available */
|
|
TTrace((stderr,
|
|
" UT modtime range error; ignore e.f.!\n"));
|
|
break; /* stop scanning this field */
|
|
}
|
|
} else {
|
|
/* cannot determine, safe assumption is FALSE */
|
|
ut_zip_unzip_compatible = FALSE;
|
|
}
|
|
z_utim->mtime = (time_t)i_time;
|
|
#endif /* ?TIME_T_TYPE_DOUBLE */
|
|
} else {
|
|
flags &= ~EB_UT_FL_MTIME;
|
|
TTrace((stderr," UT e.f. truncated; no modtime\n"));
|
|
}
|
|
}
|
|
if (ef_is_c) {
|
|
break; /* central version of TIME field ends here */
|
|
}
|
|
|
|
if (flags & EB_UT_FL_ATIME) {
|
|
if ((eb_idx+4) <= eb_len) {
|
|
i_time = (long)makelong((EB_HEADSIZE+eb_idx) + ef_buf);
|
|
eb_idx += 4;
|
|
TTrace((stderr," UT e.f. access time = %ld\n",
|
|
i_time));
|
|
#ifdef TIME_T_TYPE_DOUBLE
|
|
if ((ulg)(i_time) & (ulg)(0x80000000L)) {
|
|
if (ut_in_archive_sgn == -1)
|
|
z_utim->atime =
|
|
(time_t)((long)i_time | (~(long)0x7fffffffL));
|
|
} else if (ut_in_archive_sgn == 1) {
|
|
z_utim->atime =
|
|
(time_t)((ulg)i_time & (ulg)0xffffffffL);
|
|
} else {
|
|
/* sign of 32-bit time is unknown -> ignore it */
|
|
flags &= ~EB_UT_FL_ATIME;
|
|
TTrace((stderr,
|
|
" UT access time range error: skip time!\n"));
|
|
}
|
|
} else {
|
|
z_utim->atime = (time_t)i_time;
|
|
}
|
|
#else /* !TIME_T_TYPE_DOUBLE */
|
|
if (((ulg)(i_time) & (ulg)(0x80000000L)) &&
|
|
!ut_zip_unzip_compatible) {
|
|
flags &= ~EB_UT_FL_ATIME;
|
|
TTrace((stderr,
|
|
" UT access time range error: skip time!\n"));
|
|
} else {
|
|
z_utim->atime = (time_t)i_time;
|
|
}
|
|
#endif /* ?TIME_T_TYPE_DOUBLE */
|
|
} else {
|
|
flags &= ~EB_UT_FL_ATIME;
|
|
}
|
|
}
|
|
if (flags & EB_UT_FL_CTIME) {
|
|
if ((eb_idx+4) <= eb_len) {
|
|
i_time = (long)makelong((EB_HEADSIZE+eb_idx) + ef_buf);
|
|
TTrace((stderr," UT e.f. creation time = %ld\n",
|
|
i_time));
|
|
#ifdef TIME_T_TYPE_DOUBLE
|
|
if ((ulg)(i_time) & (ulg)(0x80000000L)) {
|
|
if (ut_in_archive_sgn == -1)
|
|
z_utim->ctime =
|
|
(time_t)((long)i_time | (~(long)0x7fffffffL));
|
|
} else if (ut_in_archive_sgn == 1) {
|
|
z_utim->ctime =
|
|
(time_t)((ulg)i_time & (ulg)0xffffffffL);
|
|
} else {
|
|
/* sign of 32-bit time is unknown -> ignore it */
|
|
flags &= ~EB_UT_FL_CTIME;
|
|
TTrace((stderr,
|
|
" UT creation time range error: skip time!\n"));
|
|
}
|
|
} else {
|
|
z_utim->ctime = (time_t)i_time;
|
|
}
|
|
#else /* !TIME_T_TYPE_DOUBLE */
|
|
if (((ulg)(i_time) & (ulg)(0x80000000L)) &&
|
|
!ut_zip_unzip_compatible) {
|
|
flags &= ~EB_UT_FL_CTIME;
|
|
TTrace((stderr,
|
|
" UT creation time range error: skip time!\n"));
|
|
} else {
|
|
z_utim->ctime = (time_t)i_time;
|
|
}
|
|
#endif /* ?TIME_T_TYPE_DOUBLE */
|
|
} else {
|
|
flags &= ~EB_UT_FL_CTIME;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case EF_IZUNIX2:
|
|
if (have_new_type_eb == 0) {
|
|
flags &= ~0x0ff; /* ignore any previous IZUNIX field */
|
|
have_new_type_eb = 1;
|
|
}
|
|
#ifdef IZ_HAVE_UXUIDGID
|
|
if (have_new_type_eb > 1)
|
|
break; /* IZUNIX3 overrides IZUNIX2 e.f. block ! */
|
|
if (eb_len == EB_UX2_MINLEN && z_uidgid != NULL) {
|
|
z_uidgid[0] = (ulg)makeword((EB_HEADSIZE+EB_UX2_UID) + ef_buf);
|
|
z_uidgid[1] = (ulg)makeword((EB_HEADSIZE+EB_UX2_GID) + ef_buf);
|
|
flags |= EB_UX2_VALID; /* signal success */
|
|
}
|
|
#endif
|
|
break;
|
|
|
|
case EF_IZUNIX3:
|
|
/* new 3rd generation Unix ef */
|
|
have_new_type_eb = 2;
|
|
|
|
/*
|
|
Version 1 byte version of this extra field, currently 1
|
|
UIDSize 1 byte Size of UID field
|
|
UID Variable UID for this entry
|
|
GIDSize 1 byte Size of GID field
|
|
GID Variable GID for this entry
|
|
*/
|
|
|
|
#ifdef IZ_HAVE_UXUIDGID
|
|
if (eb_len >= EB_UX3_MINLEN
|
|
&& z_uidgid != NULL
|
|
&& (*((EB_HEADSIZE + 0) + ef_buf) == 1)
|
|
/* only know about version 1 */
|
|
{
|
|
uch uid_size;
|
|
uch gid_size;
|
|
|
|
uid_size = *((EB_HEADSIZE + 1) + ef_buf);
|
|
gid_size = *((EB_HEADSIZE + uid_size + 2) + ef_buf);
|
|
|
|
flags &= ~0x0ff; /* ignore any previous UNIX field */
|
|
|
|
if ( read_ux3_value((EB_HEADSIZE + 2) + ef_buf,
|
|
uid_size, z_uidgid[0])
|
|
&&
|
|
read_ux3_value((EB_HEADSIZE + uid_size + 3) + ef_buf,
|
|
gid_size, z_uidgid[1]) )
|
|
{
|
|
flags |= EB_UX2_VALID; /* signal success */
|
|
}
|
|
}
|
|
#endif /* IZ_HAVE_UXUIDGID */
|
|
break;
|
|
|
|
case EF_IZUNIX:
|
|
case EF_PKUNIX: /* PKUNIX e.f. layout is identical to IZUNIX */
|
|
if (eb_len >= EB_UX_MINLEN) {
|
|
TTrace((stderr,"ef_scan_for_izux: found %s extra field\n",
|
|
(eb_id == EF_IZUNIX ? "IZUNIX" : "PKUNIX")));
|
|
if (have_new_type_eb > 0) {
|
|
break; /* Ignore IZUNIX extra field block ! */
|
|
}
|
|
if (z_utim != NULL) {
|
|
flags |= (EB_UT_FL_MTIME | EB_UT_FL_ATIME);
|
|
i_time = (long)makelong((EB_HEADSIZE+EB_UX_MTIME)+ef_buf);
|
|
TTrace((stderr," Unix EF modtime = %ld\n", i_time));
|
|
#ifdef TIME_T_TYPE_DOUBLE
|
|
if ((ulg)(i_time) & (ulg)(0x80000000L)) {
|
|
if (dos_mdatetime == DOSTIME_MINIMUM) {
|
|
ut_in_archive_sgn = -1;
|
|
z_utim->mtime =
|
|
(time_t)((long)i_time | (~(long)0x7fffffffL));
|
|
} else if (dos_mdatetime >= DOSTIME_2038_01_18) {
|
|
ut_in_archive_sgn = 1;
|
|
z_utim->mtime =
|
|
(time_t)((ulg)i_time & (ulg)0xffffffffL);
|
|
} else {
|
|
ut_in_archive_sgn = 0;
|
|
/* cannot determine sign of mtime;
|
|
without modtime: ignore complete UT field */
|
|
flags &= ~0x0ff; /* no time_t times available */
|
|
TTrace((stderr,
|
|
" UX modtime range error: ignore e.f.!\n"));
|
|
}
|
|
} else {
|
|
/* cannot determine, safe assumption is FALSE */
|
|
ut_in_archive_sgn = 0;
|
|
z_utim->mtime = (time_t)i_time;
|
|
}
|
|
#else /* !TIME_T_TYPE_DOUBLE */
|
|
if ((ulg)(i_time) & (ulg)(0x80000000L)) {
|
|
ut_zip_unzip_compatible =
|
|
((time_t)0x80000000L < (time_t)0L)
|
|
? (dos_mdatetime == DOSTIME_MINIMUM)
|
|
: (dos_mdatetime >= DOSTIME_2038_01_18);
|
|
if (!ut_zip_unzip_compatible) {
|
|
/* UnZip interpretes mtime differently than Zip;
|
|
without modtime: ignore complete UT field */
|
|
flags &= ~0x0ff; /* no time_t times available */
|
|
TTrace((stderr,
|
|
" UX modtime range error: ignore e.f.!\n"));
|
|
}
|
|
} else {
|
|
/* cannot determine, safe assumption is FALSE */
|
|
ut_zip_unzip_compatible = FALSE;
|
|
}
|
|
z_utim->mtime = (time_t)i_time;
|
|
#endif /* ?TIME_T_TYPE_DOUBLE */
|
|
i_time = (long)makelong((EB_HEADSIZE+EB_UX_ATIME)+ef_buf);
|
|
TTrace((stderr," Unix EF actime = %ld\n", i_time));
|
|
#ifdef TIME_T_TYPE_DOUBLE
|
|
if ((ulg)(i_time) & (ulg)(0x80000000L)) {
|
|
if (ut_in_archive_sgn == -1)
|
|
z_utim->atime =
|
|
(time_t)((long)i_time | (~(long)0x7fffffffL));
|
|
} else if (ut_in_archive_sgn == 1) {
|
|
z_utim->atime =
|
|
(time_t)((ulg)i_time & (ulg)0xffffffffL);
|
|
} else if (flags & 0x0ff) {
|
|
/* sign of 32-bit time is unknown -> ignore it */
|
|
flags &= ~EB_UT_FL_ATIME;
|
|
TTrace((stderr,
|
|
" UX access time range error: skip time!\n"));
|
|
}
|
|
} else {
|
|
z_utim->atime = (time_t)i_time;
|
|
}
|
|
#else /* !TIME_T_TYPE_DOUBLE */
|
|
if (((ulg)(i_time) & (ulg)(0x80000000L)) &&
|
|
!ut_zip_unzip_compatible && (flags & 0x0ff)) {
|
|
/* atime not in range of UnZip's time_t */
|
|
flags &= ~EB_UT_FL_ATIME;
|
|
TTrace((stderr,
|
|
" UX access time range error: skip time!\n"));
|
|
} else {
|
|
z_utim->atime = (time_t)i_time;
|
|
}
|
|
#endif /* ?TIME_T_TYPE_DOUBLE */
|
|
}
|
|
#ifdef IZ_HAVE_UXUIDGID
|
|
if (eb_len >= EB_UX_FULLSIZE && z_uidgid != NULL) {
|
|
z_uidgid[0] = makeword((EB_HEADSIZE+EB_UX_UID) + ef_buf);
|
|
z_uidgid[1] = makeword((EB_HEADSIZE+EB_UX_GID) + ef_buf);
|
|
flags |= EB_UX2_VALID;
|
|
}
|
|
#endif /* IZ_HAVE_UXUIDGID */
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* Skip this extra field block */
|
|
ef_buf += (eb_len + EB_HEADSIZE);
|
|
ef_len -= (eb_len + EB_HEADSIZE);
|
|
}
|
|
|
|
return flags;
|
|
}
|
|
|
|
#endif /* USE_EF_UT_TIME */
|
|
|
|
|
|
#if (defined(RISCOS) || defined(ACORN_FTYPE_NFS))
|
|
|
|
#define SPARKID_2 0x30435241 /* = "ARC0" */
|
|
|
|
/*******************************/
|
|
/* Function getRISCOSexfield() */
|
|
/*******************************/
|
|
|
|
zvoid *getRISCOSexfield(ef_buf, ef_len)
|
|
ZCONST uch *ef_buf; /* buffer containing extra field */
|
|
unsigned ef_len; /* total length of extra field */
|
|
{
|
|
unsigned eb_id;
|
|
unsigned eb_len;
|
|
|
|
/*---------------------------------------------------------------------------
|
|
This function scans the extra field for a Acorn SPARK filetype ef-block.
|
|
If a valid block is found, the function returns a pointer to the start
|
|
of the SPARK_EF block in the extra field buffer. Otherwise, a NULL
|
|
pointer is returned.
|
|
---------------------------------------------------------------------------*/
|
|
|
|
if (ef_len == 0 || ef_buf == NULL)
|
|
return NULL;
|
|
|
|
Trace((stderr,"\ngetRISCOSexfield: scanning extra field of length %u\n",
|
|
ef_len));
|
|
|
|
while (ef_len >= EB_HEADSIZE) {
|
|
eb_id = makeword(EB_ID + ef_buf);
|
|
eb_len = makeword(EB_LEN + ef_buf);
|
|
|
|
if (eb_len > (ef_len - EB_HEADSIZE)) {
|
|
/* discovered some extra field inconsistency! */
|
|
Trace((stderr,
|
|
"getRISCOSexfield: block length %u > rest ef_size %u\n", eb_len,
|
|
ef_len - EB_HEADSIZE));
|
|
break;
|
|
}
|
|
|
|
if (eb_id == EF_SPARK && (eb_len == 24 || eb_len == 20)) {
|
|
if (makelong(EB_HEADSIZE + ef_buf) == SPARKID_2) {
|
|
/* Return a pointer to the valid SPARK filetype ef block */
|
|
return (zvoid *)ef_buf;
|
|
}
|
|
}
|
|
|
|
/* Skip this extra field block */
|
|
ef_buf += (eb_len + EB_HEADSIZE);
|
|
ef_len -= (eb_len + EB_HEADSIZE);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
#endif /* (RISCOS || ACORN_FTYPE_NFS) */
|