sps-core · log · files · refs · tags · releases · people · languages · GitHub · commit
7f6949f
core: add unzip, zip, parser tools, sqlite, pcre2, GnuPG, and OpenSSH
Parents
Stat
README.md | 4 +- base/m4/recipe | 13 + base/pinentry/recipe | 16 + .../patches/unzip-6.0-consolidated_fixes-1.patch | 16713 +++++++++++++++++++ base/unzip/patches/unzip-6.0-gcc14-1.patch | 63 + base/unzip/recipe | 17 + base/zip/recipe | 13 + devel/bison/recipe | 14 + devel/flex/recipe | 14 + libs/libassuan/recipe | 14 + libs/libgcrypt/recipe | 14 + libs/libgpg-error/recipe | 13 + libs/libksba/recipe | 14 + libs/npth/recipe | 13 + libs/pcre2/recipe | 13 + libs/sqlite/recipe | 15 + net/gnupg/recipe | 24 + net/openssh/recipe | 15 + 18 files changed, 17000 insertions(+), 2 deletions(-)
Diff
diff --git a/README.md b/README.md
index d8218c0..16e878c 100644
--- a/README.md
+++ b/README.md
@@ -10,10 +10,10 @@ identity; the category is not part of the name. Core uses these categories as
packages are added:
```text
-base/ boot/ devel/ libs/ net/ system/
+base/ devel/ libs/ net/
```
-No category directory is kept empty.
+Category directories are created only when they contain a package.
## Configuration
diff --git a/base/m4/recipe b/base/m4/recipe
new file mode 100644
index 0000000..42ebb54
--- /dev/null
+++ b/base/m4/recipe
@@ -0,0 +1,13 @@
+name m4
+version 1.4.19
+release 1
+description GNU m4: macro processor
+
+source https://ftp.gnu.org/gnu/m4/m4-${version}.tar.gz
+hash sha256:3be4a26d825ffdfda52a56fc43246456989a3630093cced3fbddf4771ee58a70
+
+depend filesystem
+
+configure ./configure --prefix=/usr CFLAGS="-O2 -std=gnu17"
+build make -j"$MAKEJOBS"
+install make install DESTDIR="$PKG"
diff --git a/base/pinentry/recipe b/base/pinentry/recipe
new file mode 100644
index 0000000..626afae
--- /dev/null
+++ b/base/pinentry/recipe
@@ -0,0 +1,16 @@
+name pinentry
+version 1.3.3
+release 1
+description pinentry: passphrase entry dialogs for GnuPG
+
+source https://gnupg.org/ftp/gcrypt/pinentry/pinentry-${version}.tar.bz2
+hash sha256:c2970f16d6afb66ecddfca767d743936c86239bff936eed7fd7597a678414b63
+
+depend filesystem
+depend ncurses
+depend libgpg-error
+depend libassuan
+
+configure ./configure --prefix=/usr --enable-pinentry-curses --enable-fallback-curses --enable-pinentry-tty --disable-pinentry-emacs --disable-inside-emacs --disable-pinentry-efl --disable-pinentry-gtk2 --disable-pinentry-gnome3 --disable-libsecret --disable-pinentry-qt --disable-pinentry-qt5 --disable-pinentry-fltk
+build make -j"$MAKEJOBS"
+install make install DESTDIR="$PKG"
diff --git a/base/unzip/patches/unzip-6.0-consolidated_fixes-1.patch b/base/unzip/patches/unzip-6.0-consolidated_fixes-1.patch
new file mode 100644
index 0000000..35fecc0
--- /dev/null
+++ b/base/unzip/patches/unzip-6.0-consolidated_fixes-1.patch
@@ -0,0 +1,16713 @@
+Submitted By: Ken Moffat <ken at linuxfromscratch dot org>
+Date: 2019-12-03
+Initial Package Version: 6.0
+Upstream Status: Some from upstream, but that now appears to be defunct.
+Origin: Found at fedora (omitting their manpage and symlinks patches)
+Description:
+
+1. Allow it to use system libbz2.so, this allows unzip and zipinfo to
+ deal with archives which contain bz2 compressed members.
+
+2. Locale fixes to handle non-latin non-unicode filenames,
+ originally from Arch but updated by Canonical to fix buffer overflow
+ and to print them (apparently from fedora or redhat) - not tested!
+
+3. A large number of security fixes, including for CVE-2014-81{39..41},
+ CVE-2016-9844, CVE-2018-18384, CVE-2018-1000035 as well as several
+ similar overflow fixes not labelled with CVE numbers and a few general
+ fixes.
+
+diff -Naur a/crc_i386.S b/crc_i386.S
+--- a/crc_i386.S 2007-01-07 05:02:58.000000000 +0000
++++ b/crc_i386.S 2019-12-01 23:48:05.278335299 +0000
+@@ -302,3 +302,6 @@
+ #endif /* i386 || _i386 || _I386 || __i386 */
+
+ #endif /* !USE_ZLIB && !CRC_TABLE_ONLY */
++
++.section .note.GNU-stack, "", @progbits
++.previous
+diff -Naur a/crypt.c b/crypt.c
+--- a/crypt.c 2007-01-05 15:47:36.000000000 +0000
++++ b/crypt.c 2019-12-02 00:37:13.921788251 +0000
+@@ -465,7 +465,17 @@
+ GLOBAL(pInfo->encrypted) = FALSE;
+ defer_leftover_input(__G);
+ for (n = 0; n < RAND_HEAD_LEN; n++) {
+- b = NEXTBYTE;
++ /* 2012-11-23 SMS. (OUSPG report.)
++ * Quit early if compressed size < HEAD_LEN. The resulting
++ * error message ("unable to get password") could be improved,
++ * but it's better than trying to read nonexistent data, and
++ * then continuing with a negative G.csize. (See
++ * fileio.c:readbyte()).
++ */
++ if ((b = NEXTBYTE) == (ush)EOF)
++ {
++ return PK_ERR;
++ }
+ h[n] = (uch)b;
+ Trace((stdout, " (%02x)", h[n]));
+ }
+diff -Naur a/extract.c b/extract.c
+--- a/extract.c 2009-03-14 01:32:52.000000000 +0000
++++ b/extract.c 2019-12-02 01:05:52.857702371 +0000
+@@ -1,5 +1,5 @@
+ /*
+- Copyright (c) 1990-2009 Info-ZIP. All rights reserved.
++ Copyright (c) 1990-2014 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.
+@@ -298,6 +298,8 @@
+ #ifndef SFX
+ static ZCONST char Far InconsistEFlength[] = "bad extra-field entry:\n \
+ EF block length (%u bytes) exceeds remaining EF data (%u bytes)\n";
++ static ZCONST char Far TooSmallEBlength[] = "bad extra-field entry:\n \
++ EF block length (%u bytes) invalid (< %d)\n";
+ static ZCONST char Far InvalidComprDataEAs[] =
+ " invalid compressed data for EAs\n";
+ # if (defined(WIN32) && defined(NTSD_EAS))
+@@ -472,8 +474,8 @@
+ */
+ Info(slide, 0x401, ((char *)slide,
+ LoadFarString(CentSigMsg), j + blknum*DIR_BLKSIZ + 1));
+- Info(slide, 0x401, ((char *)slide,
+- LoadFarString(ReportMsg)));
++ Info(slide, 0x401,
++ ((char *)slide,"%s", LoadFarString(ReportMsg)));
+ error_in_archive = PK_BADERR;
+ }
+ reached_end = TRUE; /* ...so no more left to do */
+@@ -752,8 +754,8 @@
+
+ #ifndef SFX
+ if (no_endsig_found) { /* just to make sure */
+- Info(slide, 0x401, ((char *)slide, LoadFarString(EndSigMsg)));
+- Info(slide, 0x401, ((char *)slide, LoadFarString(ReportMsg)));
++ Info(slide, 0x401, ((char *)slide,"%s", LoadFarString(EndSigMsg)));
++ Info(slide, 0x401, ((char *)slide,"%s", LoadFarString(ReportMsg)));
+ if (!error_in_archive) /* don't overwrite stronger error */
+ error_in_archive = PK_WARN;
+ }
+@@ -1255,8 +1257,17 @@
+ if (G.lrec.compression_method == STORED) {
+ zusz_t csiz_decrypted = G.lrec.csize;
+
+- if (G.pInfo->encrypted)
++ if (G.pInfo->encrypted) {
++ if (csiz_decrypted < 12) {
++ /* handle the error now to prevent unsigned overflow */
++ Info(slide, 0x401, ((char *)slide,
++ LoadFarStringSmall(ErrUnzipNoFile),
++ LoadFarString(InvalidComprData),
++ LoadFarStringSmall2(Inflate)));
++ return PK_ERR;
++ }
+ csiz_decrypted -= 12;
++ }
+ if (G.lrec.ucsize != csiz_decrypted) {
+ Info(slide, 0x401, ((char *)slide,
+ LoadFarStringSmall2(WrnStorUCSizCSizDiff),
+@@ -1924,24 +1935,21 @@
+
+ #ifdef VMS /* VMS: required even for stdout! (final flush) */
+ if (!uO.tflag) /* don't close NULL file */
+- close_outfile(__G);
++ error = close_outfile(__G);
+ #else
+ #ifdef DLL
+ if (!uO.tflag && (!uO.cflag || G.redirect_data)) {
+ if (G.redirect_data)
+ FINISH_REDIRECT();
+ else
+- close_outfile(__G);
++ error = close_outfile(__G);
+ }
+ #else
+ if (!uO.tflag && !uO.cflag) /* don't close NULL file or stdout */
+- close_outfile(__G);
++ error = close_outfile(__G);
+ #endif
+ #endif /* VMS */
+
+- /* GRR: CONVERT close_outfile() TO NON-VOID: CHECK FOR ERRORS! */
+-
+-
+ if (G.disk_full) { /* set by flush() */
+ if (G.disk_full > 1) {
+ #if (defined(DELETE_IF_FULL) && defined(HAVE_UNLINK))
+@@ -2023,7 +2031,8 @@
+ ebID = makeword(ef);
+ ebLen = (unsigned)makeword(ef+EB_LEN);
+
+- if (ebLen > (ef_len - EB_HEADSIZE)) {
++ if (ebLen > (ef_len - EB_HEADSIZE))
++ {
+ /* Discovered some extra field inconsistency! */
+ if (uO.qflag)
+ Info(slide, 1, ((char *)slide, "%-22s ",
+@@ -2158,11 +2167,29 @@
+ }
+ break;
+ case EF_PKVMS:
+- if (makelong(ef+EB_HEADSIZE) !=
+- crc32(CRCVAL_INITIAL, ef+(EB_HEADSIZE+4),
+- (extent)(ebLen-4)))
+- Info(slide, 1, ((char *)slide,
+- LoadFarString(BadCRC_EAs)));
++ /* 2015-01-30 SMS. Added sufficient-bytes test/message
++ * here. (Removed defective ebLen test above.)
++ *
++ * If sufficient bytes (EB_PKVMS_MINLEN) are available,
++ * then compare the stored CRC value with the calculated
++ * CRC for the remainder of the data (and complain about
++ * a mismatch).
++ */
++ if (ebLen < EB_PKVMS_MINLEN)
++ {
++ /* Insufficient bytes available. */
++ Info( slide, 1,
++ ((char *)slide, LoadFarString( TooSmallEBlength),
++ ebLen, EB_PKVMS_MINLEN));
++ }
++ else if (makelong(ef+ EB_HEADSIZE) !=
++ crc32(CRCVAL_INITIAL,
++ (ef+ EB_HEADSIZE+ EB_PKVMS_MINLEN),
++ (extent)(ebLen- EB_PKVMS_MINLEN)))
++ {
++ Info(slide, 1, ((char *)slide,
++ LoadFarString(BadCRC_EAs)));
++ }
+ break;
+ case EF_PKW32:
+ case EF_PKUNIX:
+@@ -2217,14 +2244,28 @@
+ ulg eb_ucsize;
+ uch *eb_ucptr;
+ int r;
++ ush method;
+
+ if (compr_offset < 4) /* field is not compressed: */
+ return PK_OK; /* do nothing and signal OK */
+
++ /* Return no/bad-data error status if any problem is found:
++ * 1. eb_size is too small to hold the uncompressed size
++ * (eb_ucsize). (Else extract eb_ucsize.)
++ * 2. eb_ucsize is zero (invalid). 2014-12-04 SMS.
++ * 3. eb_ucsize is positive, but eb_size is too small to hold
++ * the compressed data header.
++ */
+ if ((eb_size < (EB_UCSIZE_P + 4)) ||
+- ((eb_ucsize = makelong(eb+(EB_HEADSIZE+EB_UCSIZE_P))) > 0L &&
+- eb_size <= (compr_offset + EB_CMPRHEADLEN)))
+- return IZ_EF_TRUNC; /* no compressed data! */
++ ((eb_ucsize = makelong( eb+ (EB_HEADSIZE+ EB_UCSIZE_P))) == 0L) ||
++ ((eb_ucsize > 0L) && (eb_size <= (compr_offset + EB_CMPRHEADLEN))))
++ return IZ_EF_TRUNC; /* no/bad compressed data! */
++
++ method = makeword(eb + (EB_HEADSIZE + compr_offset));
++ if ((method == STORED) && (eb_size != compr_offset + EB_CMPRHEADLEN + eb_ucsize))
++ return PK_ERR; /* compressed & uncompressed
++ * should match in STORED
++ * method */
+
+ if (
+ #ifdef INT_16BIT
+@@ -2542,8 +2583,21 @@
+ } /* end function set_deferred_symlink() */
+ #endif /* SYMLINKS */
+
++/*
++ * If Unicode is supported, assume we have what we need to do this
++ * check using wide characters, avoiding MBCS issues.
++ */
+
+-
++#ifndef UZ_FNFILTER_REPLACECHAR
++ /* A convenient choice for the replacement of unprintable char codes is
++ * the "single char wildcard", as this character is quite unlikely to
++ * appear in filenames by itself. The following default definition
++ * sets the replacement char to a question mark as the most common
++ * "single char wildcard"; this setting should be overridden in the
++ * appropiate system-specific configuration header when needed.
++ */
++# define UZ_FNFILTER_REPLACECHAR '?'
++#endif
+
+ /*************************/
+ /* Function fnfilter() */ /* here instead of in list.c for SFX */
+@@ -2555,48 +2609,168 @@
+ extent size;
+ {
+ #ifndef NATIVE /* ASCII: filter ANSI escape codes, etc. */
+- ZCONST uch *r=(ZCONST uch *)raw;
++ ZCONST uch *r; // =(ZCONST uch *)raw;
+ uch *s=space;
+ uch *slim=NULL;
+ uch *se=NULL;
+ int have_overflow = FALSE;
+
+- if (size > 0) {
+- slim = space + size
+-#ifdef _MBCS
+- - (MB_CUR_MAX - 1)
+-#endif
+- - 4;
++# if defined( UNICODE_SUPPORT) && defined( _MBCS)
++/* If Unicode support is enabled, and we have multi-byte characters,
++ * then do the isprint() checks by first converting to wide characters
++ * and checking those. This avoids our having to parse multi-byte
++ * characters for ourselves. After the wide-char replacements have been
++ * made, the wide string is converted back to the local character set.
++ */
++ wchar_t *wstring; /* wchar_t version of raw */
++ size_t wslen; /* length of wstring */
++ wchar_t *wostring; /* wchar_t version of output string */
++ size_t woslen; /* length of wostring */
++ char *newraw; /* new raw */
++
++ /* 2012-11-06 SMS.
++ * Changed to check the value returned by mbstowcs(), and bypass the
++ * Unicode processing if it fails. This seems to fix a problem
++ * reported in the SourceForge forum, but it's not clear that we
++ * should be doing any Unicode processing without some evidence that
++ * the name actually is Unicode. (Check bit 11 in the flags before
++ * coming here?)
++ * http://sourceforge.net/p/infozip/bugs/40/
++ */
++
++ if (MB_CUR_MAX <= 1)
++ {
++ /* There's no point to converting multi-byte chars if there are
++ * no multi-byte chars.
++ */
++ wslen = (size_t)-1;
+ }
+- while (*r) {
+- if (size > 0 && s >= slim && se == NULL) {
+- se = s;
+- }
+-#ifdef QDOS
+- if (qlflag & 2) {
+- if (*r == '/' || *r == '.') {
++ else
++ {
++ /* Get Unicode wide character count (for storage allocation). */
++ wslen = mbstowcs( NULL, raw, 0);
++ }
++
++ if (wslen != (size_t)-1)
++ {
++ /* Apparently valid Unicode. Allocate wide-char storage. */
++ wstring = (wchar_t *)malloc((wslen + 1) * sizeof(wchar_t));
++ if (wstring == NULL) {
++ strcpy( (char *)space, raw);
++ return (char *)space;
++ }
++ wostring = (wchar_t *)malloc(2 * (wslen + 1) * sizeof(wchar_t));
++ if (wostring == NULL) {
++ free(wstring);
++ strcpy( (char *)space, raw);
++ return (char *)space;
++ }
++
++ /* Convert the multi-byte Unicode to wide chars. */
++ wslen = mbstowcs(wstring, raw, wslen + 1);
++
++ /* Filter the wide-character string. */
++ fnfilterw( wstring, wostring, (2 * (wslen + 1) * sizeof(wchar_t)));
++
++ /* Convert filtered wide chars back to multi-byte. */
++ woslen = wcstombs( NULL, wostring, 0);
++ if ((newraw = malloc(woslen + 1)) == NULL) {
++ free(wstring);
++ free(wostring);
++ strcpy( (char *)space, raw);
++ return (char *)space;
++ }
++ woslen = wcstombs( newraw, wostring, (woslen * MB_CUR_MAX) + 1);
++
++ if (size > 0) {
++ slim = space + size - 4;
++ }
++ r = (ZCONST uch *)newraw;
++ while (*r) {
++ if (size > 0 && s >= slim && se == NULL) {
++ se = s;
++ }
++# ifdef QDOS
++ if (qlflag & 2) {
++ if (*r == '/' || *r == '.') {
++ if (se != NULL && (s > (space + (size-3)))) {
++ have_overflow = TRUE;
++ break;
++ }
++ ++r;
++ *s++ = '_';
++ continue;
++ }
++ } else
++# endif
++ {
+ if (se != NULL && (s > (space + (size-3)))) {
+ have_overflow = TRUE;
+ break;
+ }
+- ++r;
+- *s++ = '_';
+- continue;
++ *s++ = *r++;
+ }
+- } else
++ }
++ if (have_overflow) {
++ strcpy((char *)se, "...");
++ } else {
++ *s = '\0';
++ }
++
++ free(wstring);
++ free(wostring);
++ free(newraw);
++ }
++ else
++# endif /* defined( UNICODE_SUPPORT) && defined( _MBCS) */
++ {
++ /* No Unicode support, or apparently invalid Unicode. */
++ r = (ZCONST uch *)raw;
++
++ if (size > 0) {
++ slim = space + size
++#ifdef _MBCS
++ - (MB_CUR_MAX - 1)
++#endif
++ - 4;
++ }
++ while (*r) {
++ if (size > 0 && s >= slim && se == NULL) {
++ se = s;
++ }
++#ifdef QDOS
++ if (qlflag & 2) {
++ if (*r == '/' || *r == '.') {
++ if (se != NULL && (s > (space + (size-3)))) {
++ have_overflow = TRUE;
++ break;
++ }
++ ++r;
++ *s++ = '_';
++ continue;
++ }
++ } else
+ #endif
+ #ifdef HAVE_WORKING_ISPRINT
+-# ifndef UZ_FNFILTER_REPLACECHAR
+- /* A convenient choice for the replacement of unprintable char codes is
+- * the "single char wildcard", as this character is quite unlikely to
+- * appear in filenames by itself. The following default definition
+- * sets the replacement char to a question mark as the most common
+- * "single char wildcard"; this setting should be overridden in the
+- * appropiate system-specific configuration header when needed.
+- */
+-# define UZ_FNFILTER_REPLACECHAR '?'
+-# endif
+- if (!isprint(*r)) {
++ if (!isprint(*r)) {
++ if (*r < 32) {
++ /* ASCII control codes are escaped as "^{letter}". */
++ if (se != NULL && (s > (space + (size-4)))) {
++ have_overflow = TRUE;
++ break;
++ }
++ *s++ = '^', *s++ = (uch)(64 + *r++);
++ } else {
++ /* Other unprintable codes are replaced by the
++ * placeholder character. */
++ if (se != NULL && (s > (space + (size-3)))) {
++ have_overflow = TRUE;
++ break;
++ }
++ *s++ = UZ_FNFILTER_REPLACECHAR;
++ INCSTR(r);
++ }
++#else /* !HAVE_WORKING_ISPRINT */
+ if (*r < 32) {
+ /* ASCII control codes are escaped as "^{letter}". */
+ if (se != NULL && (s > (space + (size-4)))) {
+@@ -2604,47 +2778,30 @@
+ break;
+ }
+ *s++ = '^', *s++ = (uch)(64 + *r++);
++#endif /* ?HAVE_WORKING_ISPRINT */
+ } else {
+- /* Other unprintable codes are replaced by the
+- * placeholder character. */
++#ifdef _MBCS
++ unsigned i = CLEN(r);
++ if (se != NULL && (s > (space + (size-i-2)))) {
++ have_overflow = TRUE;
++ break;
++ }
++ for (; i > 0; i--)
++ *s++ = *r++;
++#else
+ if (se != NULL && (s > (space + (size-3)))) {
+ have_overflow = TRUE;
+ break;
+ }
+- *s++ = UZ_FNFILTER_REPLACECHAR;
+- INCSTR(r);
+- }
+-#else /* !HAVE_WORKING_ISPRINT */
+- if (*r < 32) {
+- /* ASCII control codes are escaped as "^{letter}". */
+- if (se != NULL && (s > (space + (size-4)))) {
+- have_overflow = TRUE;
+- break;
+- }
+- *s++ = '^', *s++ = (uch)(64 + *r++);
+-#endif /* ?HAVE_WORKING_ISPRINT */
+- } else {
+-#ifdef _MBCS
+- unsigned i = CLEN(r);
+- if (se != NULL && (s > (space + (size-i-2)))) {
+- have_overflow = TRUE;
+- break;
+- }
+- for (; i > 0; i--)
+ *s++ = *r++;
+-#else
+- if (se != NULL && (s > (space + (size-3)))) {
+- have_overflow = TRUE;
+- break;
+- }
+- *s++ = *r++;
+ #endif
+- }
+- }
+- if (have_overflow) {
+- strcpy((char *)se, "...");
+- } else {
+- *s = '\0';
++ }
++ }
++ if (have_overflow) {
++ strcpy((char *)se, "...");
++ } else {
++ *s = '\0';
++ }
+ }
+
+ #ifdef WINDLL
+@@ -2666,6 +2823,53 @@
+ } /* end function fnfilter() */
+
+
++#if defined( UNICODE_SUPPORT) && defined( _MBCS)
++
++/****************************/
++/* Function fnfilter[w]() */ /* (Here instead of in list.c for SFX.) */
++/****************************/
++
++/* fnfilterw() - Convert wide name to safely printable form. */
++
++/* fnfilterw() - Convert wide-character name to safely printable form. */
++
++wchar_t *fnfilterw( src, dst, siz)
++ ZCONST wchar_t *src; /* Pointer to source char (string). */
++ wchar_t *dst; /* Pointer to destination char (string). */
++ extent siz; /* Not used (!). */
++{
++ wchar_t *dsx = dst;
++
++ /* Filter the wide chars. */
++ while (*src)
++ {
++ if (iswprint( *src))
++ {
++ /* Printable code. Copy it. */
++ *dst++ = *src;
++ }
++ else
++ {
++ /* Unprintable code. Substitute something printable for it. */
++ if (*src < 32)
++ {
++ /* Replace ASCII control code with "^{letter}". */
++ *dst++ = (wchar_t)'^';
++ *dst++ = (wchar_t)(64 + *src);
++ }
++ else
++ {
++ /* Replace other unprintable code with the placeholder. */
++ *dst++ = (wchar_t)UZ_FNFILTER_REPLACECHAR;
++ }
++ }
++ src++;
++ }
++ *dst = (wchar_t)0; /* NUL-terminate the destination string. */
++ return dsx;
++} /* fnfilterw(). */
++
++#endif /* defined( UNICODE_SUPPORT) && defined( _MBCS) */
+
+
+ #ifdef SET_DIR_ATTRIB
+@@ -2701,6 +2905,12 @@
+ int repeated_buf_err;
+ bz_stream bstrm;
+
++ if (G.incnt <= 0 && G.csize <= 0L) {
++ /* avoid an infinite loop */
++ Trace((stderr, "UZbunzip2() got empty input\n"));
++ return 2;
++ }
++
+ #if (defined(DLL) && !defined(NO_SLIDE_REDIR))
+ if (G.redirect_slide)
+ wsize = G.redirect_size, redirSlide = G.redirect_buffer;
+diff -Naur a/extract.c.orig b/extract.c.orig
+--- a/extract.c.orig 1970-01-01 01:00:00.000000000 +0100
++++ b/extract.c.orig 2019-12-02 00:37:13.921788251 +0000
+@@ -0,0 +1,2867 @@
++/*
++ Copyright (c) 1990-2014 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
++*/
++/*---------------------------------------------------------------------------
++
++ extract.c
++
++ This file contains the high-level routines ("driver routines") for extrac-
++ ting and testing zipfile members. It calls the low-level routines in files
++ explode.c, inflate.c, unreduce.c and unshrink.c.
++
++ Contains: extract_or_test_files()
++ store_info()
++ find_compr_idx()
++ extract_or_test_entrylist()
++ extract_or_test_member()
++ TestExtraField()
++ test_compr_eb()
++ memextract()
++ memflush()
++ extract_izvms_block() (VMS or VMS_TEXT_CONV)
++ set_deferred_symlink() (SYMLINKS only)
++ fnfilter()
++ dircomp() (SET_DIR_ATTRIB only)
++ UZbunzip2() (USE_BZIP2 only)
++
++ ---------------------------------------------------------------------------*/
++
++
++#define __EXTRACT_C /* identifies this source module */
++#define UNZIP_INTERNAL
++#include "unzip.h"
++#ifdef WINDLL
++# ifdef POCKET_UNZIP
++# include "wince/intrface.h"
++# else
++# include "windll/windll.h"
++# endif
++#endif
++#include "crc32.h"
++#include "crypt.h"
++
++#define GRRDUMP(buf,len) { \
++ int i, j; \
++ \
++ for (j = 0; j < (len)/16; ++j) { \
++ printf(" "); \
++ for (i = 0; i < 16; ++i) \
++ printf("%02x ", (uch)(buf)[i+(j<<4)]); \
++ printf("\n "); \
++ for (i = 0; i < 16; ++i) { \
++ char c = (char)(buf)[i+(j<<4)]; \
++ \
++ if (c == '\n') \
++ printf("\\n "); \
++ else if (c == '\r') \
++ printf("\\r "); \
++ else \
++ printf(" %c ", c); \
++ } \
++ printf("\n"); \
++ } \
++ if ((len) % 16) { \
++ printf(" "); \
++ for (i = j<<4; i < (len); ++i) \
++ printf("%02x ", (uch)(buf)[i]); \
++ printf("\n "); \
++ for (i = j<<4; i < (len); ++i) { \
++ char c = (char)(buf)[i]; \
++ \
++ if (c == '\n') \
++ printf("\\n "); \
++ else if (c == '\r') \
++ printf("\\r "); \
++ else \
++ printf(" %c ", c); \
++ } \
++ printf("\n"); \
++ } \
++}
++
++static int store_info OF((__GPRO));
++#ifdef SET_DIR_ATTRIB
++static int extract_or_test_entrylist OF((__GPRO__ unsigned numchunk,
++ ulg *pfilnum, ulg *pnum_bad_pwd, zoff_t *pold_extra_bytes,
++ unsigned *pnum_dirs, direntry **pdirlist,
++ int error_in_archive));
++#else
++static int extract_or_test_entrylist OF((__GPRO__ unsigned numchunk,
++ ulg *pfilnum, ulg *pnum_bad_pwd, zoff_t *pold_extra_bytes,
++ int error_in_archive));
++#endif
++static int extract_or_test_member OF((__GPRO));
++#ifndef SFX
++ static int TestExtraField OF((__GPRO__ uch *ef, unsigned ef_len));
++ static int test_compr_eb OF((__GPRO__ uch *eb, unsigned eb_size,
++ unsigned compr_offset,
++ int (*test_uc_ebdata)(__GPRO__ uch *eb, unsigned eb_size,
++ uch *eb_ucptr, ulg eb_ucsize)));
++#endif
++#if (defined(VMS) || defined(VMS_TEXT_CONV))
++ static void decompress_bits OF((uch *outptr, unsigned needlen,
++ ZCONST uch *bitptr));
++#endif
++#ifdef SYMLINKS
++ static void set_deferred_symlink OF((__GPRO__ slinkentry *slnk_entry));
++#endif
++#ifdef SET_DIR_ATTRIB
++ static int Cdecl dircomp OF((ZCONST zvoid *a, ZCONST zvoid *b));
++#endif
++
++
++
++/*******************************/
++/* Strings used in extract.c */
++/*******************************/
++
++static ZCONST char Far VersionMsg[] =
++ " skipping: %-22s need %s compat. v%u.%u (can do v%u.%u)\n";
++static ZCONST char Far ComprMsgNum[] =
++ " skipping: %-22s unsupported compression method %u\n";
++#ifndef SFX
++ static ZCONST char Far ComprMsgName[] =
++ " skipping: %-22s `%s' method not supported\n";
++ static ZCONST char Far CmprNone[] = "store";
++ static ZCONST char Far CmprShrink[] = "shrink";
++ static ZCONST char Far CmprReduce[] = "reduce";
++ static ZCONST char Far CmprImplode[] = "implode";
++ static ZCONST char Far CmprTokenize[] = "tokenize";
++ static ZCONST char Far CmprDeflate[] = "deflate";
++ static ZCONST char Far CmprDeflat64[] = "deflate64";
++ static ZCONST char Far CmprDCLImplode[] = "DCL implode";
++ static ZCONST char Far CmprBzip[] = "bzip2";
++ static ZCONST char Far CmprLZMA[] = "LZMA";
++ static ZCONST char Far CmprIBMTerse[] = "IBM/Terse";
++ static ZCONST char Far CmprIBMLZ77[] = "IBM LZ77";
++ static ZCONST char Far CmprWavPack[] = "WavPack";
++ static ZCONST char Far CmprPPMd[] = "PPMd";
++ static ZCONST char Far *ComprNames[NUM_METHODS] = {
++ CmprNone, CmprShrink, CmprReduce, CmprReduce, CmprReduce, CmprReduce,
++ CmprImplode, CmprTokenize, CmprDeflate, CmprDeflat64, CmprDCLImplode,
++ CmprBzip, CmprLZMA, CmprIBMTerse, CmprIBMLZ77, CmprWavPack, CmprPPMd
++ };
++ static ZCONST unsigned ComprIDs[NUM_METHODS] = {
++ STORED, SHRUNK, REDUCED1, REDUCED2, REDUCED3, REDUCED4,
++ IMPLODED, TOKENIZED, DEFLATED, ENHDEFLATED, DCLIMPLODED,
++ BZIPPED, LZMAED, IBMTERSED, IBMLZ77ED, WAVPACKED, PPMDED
++ };
++#endif /* !SFX */
++static ZCONST char Far FilNamMsg[] =
++ "%s: bad filename length (%s)\n";
++#ifndef SFX
++ static ZCONST char Far WarnNoMemCFName[] =
++ "%s: warning, no memory for comparison with local header\n";
++ static ZCONST char Far LvsCFNamMsg[] =
++ "%s: mismatching \"local\" filename (%s),\n\
++ continuing with \"central\" filename version\n";
++#endif /* !SFX */
++#if (!defined(SFX) && defined(UNICODE_SUPPORT))
++ static ZCONST char Far GP11FlagsDiffer[] =
++ "file #%lu (%s):\n\
++ mismatch between local and central GPF bit 11 (\"UTF-8\"),\n\
++ continuing with central flag (IsUTF8 = %d)\n";
++#endif /* !SFX && UNICODE_SUPPORT */
++static ZCONST char Far WrnStorUCSizCSizDiff[] =
++ "%s: ucsize %s <> csize %s for STORED entry\n\
++ continuing with \"compressed\" size value\n";
++static ZCONST char Far ExtFieldMsg[] =
++ "%s: bad extra field length (%s)\n";
++static ZCONST char Far OffsetMsg[] =
++ "file #%lu: bad zipfile offset (%s): %ld\n";
++static ZCONST char Far ExtractMsg[] =
++ "%8sing: %-22s %s%s";
++#ifndef SFX
++ static ZCONST char Far LengthMsg[] =
++ "%s %s: %s bytes required to uncompress to %s bytes;\n %s\
++ supposed to require %s bytes%s%s%s\n";
++#endif
++
++static ZCONST char Far BadFileCommLength[] = "%s: bad file comment length\n";
++static ZCONST char Far LocalHdrSig[] = "local header sig";
++static ZCONST char Far BadLocalHdr[] = "file #%lu: bad local header\n";
++static ZCONST char Far AttemptRecompensate[] =
++ " (attempting to re-compensate)\n";
++#ifndef SFX
++ static ZCONST char Far BackslashPathSep[] =
++ "warning: %s appears to use backslashes as path separators\n";
++#endif
++static ZCONST char Far AbsolutePathWarning[] =
++ "warning: stripped absolute path spec from %s\n";
++static ZCONST char Far SkipVolumeLabel[] =
++ " skipping: %-22s %svolume label\n";
++
++#ifdef SET_DIR_ATTRIB /* messages of code for setting directory attributes */
++ static ZCONST char Far DirlistEntryNoMem[] =
++ "warning: cannot alloc memory for dir times/permissions/UID/GID\n";
++ static ZCONST char Far DirlistSortNoMem[] =
++ "warning: cannot alloc memory to sort dir times/perms/etc.\n";
++ static ZCONST char Far DirlistSetAttrFailed[] =
++ "warning: set times/attribs failed for %s\n";
++ static ZCONST char Far DirlistFailAttrSum[] =
++ " failed setting times/attribs for %lu dir entries";
++#endif
++
++#ifdef SYMLINKS /* messages of the deferred symlinks handler */
++ static ZCONST char Far SymLnkWarnNoMem[] =
++ "warning: deferred symlink (%s) failed:\n\
++ out of memory\n";
++ static ZCONST char Far SymLnkWarnInvalid[] =
++ "warning: deferred symlink (%s) failed:\n\
++ invalid placeholder file\n";
++ static ZCONST char Far SymLnkDeferred[] =
++ "finishing deferred symbolic links:\n";
++ static ZCONST char Far SymLnkFinish[] =
++ " %-22s -> %s\n";
++#endif
++
++#ifndef WINDLL
++ static ZCONST char Far ReplaceQuery[] =
++# ifdef VMS
++ "new version of %s? [y]es, [n]o, [A]ll, [N]one, [r]ename: ";
++# else
++ "replace %s? [y]es, [n]o, [A]ll, [N]one, [r]ename: ";
++# endif
++ static ZCONST char Far AssumeNone[] =
++ " NULL\n(EOF or read error, treating as \"[N]one\" ...)\n";
++ static ZCONST char Far NewNameQuery[] = "new name: ";
++ static ZCONST char Far InvalidResponse[] =
++ "error: invalid response [%s]\n";
++#endif /* !WINDLL */
++
++static ZCONST char Far ErrorInArchive[] =
++ "At least one %serror was detected in %s.\n";
++static ZCONST char Far ZeroFilesTested[] =
++ "Caution: zero files tested in %s.\n";
++
++#ifndef VMS
++ static ZCONST char Far VMSFormatQuery[] =
++ "\n%s: stored in VMS format. Extract anyway? (y/n) ";
++#endif
++
++#if CRYPT
++ static ZCONST char Far SkipCannotGetPasswd[] =
++ " skipping: %-22s unable to get password\n";
++ static ZCONST char Far SkipIncorrectPasswd[] =
++ " skipping: %-22s incorrect password\n";
++ static ZCONST char Far FilesSkipBadPasswd[] =
++ "%lu file%s skipped because of incorrect password.\n";
++ static ZCONST char Far MaybeBadPasswd[] =
++ " (may instead be incorrect password)\n";
++#else
++ static ZCONST char Far SkipEncrypted[] =
++ " skipping: %-22s encrypted (not supported)\n";
++#endif
++
++static ZCONST char Far NoErrInCompData[] =
++ "No errors detected in compressed data of %s.\n";
++static ZCONST char Far NoErrInTestedFiles[] =
++ "No errors detected in %s for the %lu file%s tested.\n";
++static ZCONST char Far FilesSkipped[] =
++ "%lu file%s skipped because of unsupported compression or encoding.\n";
++
++static ZCONST char Far ErrUnzipFile[] = " error: %s%s %s\n";
++static ZCONST char Far ErrUnzipNoFile[] = "\n error: %s%s\n";
++static ZCONST char Far NotEnoughMem[] = "not enough memory to ";
++static ZCONST char Far InvalidComprData[] = "invalid compressed data to ";
++static ZCONST char Far Inflate[] = "inflate";
++#ifdef USE_BZIP2
++ static ZCONST char Far BUnzip[] = "bunzip";
++#endif
++
++#ifndef SFX
++ static ZCONST char Far Explode[] = "explode";
++#ifndef LZW_CLEAN
++ static ZCONST char Far Unshrink[] = "unshrink";
++#endif
++#endif
++
++#if (!defined(DELETE_IF_FULL) || !defined(HAVE_UNLINK))
++ static ZCONST char Far FileTruncated[] =
++ "warning: %s is probably truncated\n";
++#endif
++
++static ZCONST char Far FileUnknownCompMethod[] =
++ "%s: unknown compression method\n";
++static ZCONST char Far BadCRC[] = " bad CRC %08lx (should be %08lx)\n";
++
++ /* TruncEAs[] also used in OS/2 mapname(), close_outfile() */
++char ZCONST Far TruncEAs[] = " compressed EA data missing (%d bytes)%s";
++char ZCONST Far TruncNTSD[] =
++ " compressed WinNT security data missing (%d bytes)%s";
++
++#ifndef SFX
++ static ZCONST char Far InconsistEFlength[] = "bad extra-field entry:\n \
++ EF block length (%u bytes) exceeds remaining EF data (%u bytes)\n";
++ static ZCONST char Far TooSmallEBlength[] = "bad extra-field entry:\n \
++ EF block length (%u bytes) invalid (< %d)\n";
++ static ZCONST char Far InvalidComprDataEAs[] =
++ " invalid compressed data for EAs\n";
++# if (defined(WIN32) && defined(NTSD_EAS))
++ static ZCONST char Far InvalidSecurityEAs[] =
++ " EAs fail security check\n";
++# endif
++ static ZCONST char Far UnsuppNTSDVersEAs[] =
++ " unsupported NTSD EAs version %d\n";
++ static ZCONST char Far BadCRC_EAs[] = " bad CRC for extended attributes\n";
++ static ZCONST char Far UnknComprMethodEAs[] =
++ " unknown compression method for EAs (%u)\n";
++ static ZCONST char Far NotEnoughMemEAs[] =
++ " out of memory while inflating EAs\n";
++ static ZCONST char Far UnknErrorEAs[] =
++ " unknown error on extended attributes\n";
++#endif /* !SFX */
++
++static ZCONST char Far UnsupportedExtraField[] =
++ "\nerror: unsupported extra-field compression type (%u)--skipping\n";
++static ZCONST char Far BadExtraFieldCRC[] =
++ "error [%s]: bad extra-field CRC %08lx (should be %08lx)\n";
++
++
++
++
++
++/**************************************/
++/* Function extract_or_test_files() */
++/**************************************/
++
++int extract_or_test_files(__G) /* return PK-type error code */
++ __GDEF
++{
++ unsigned i, j;
++ zoff_t cd_bufstart;
++ uch *cd_inptr;
++ int cd_incnt;
++ ulg filnum=0L, blknum=0L;
++ int reached_end;
++#ifndef SFX
++ int no_endsig_found;
++#endif
++ int error, error_in_archive=PK_COOL;
++ int *fn_matched=NULL, *xn_matched=NULL;
++ zucn_t members_processed;
++ ulg num_skipped=0L, num_bad_pwd=0L;
++ zoff_t old_extra_bytes = 0L;
++#ifdef SET_DIR_ATTRIB
++ unsigned num_dirs=0;
++ direntry *dirlist=(direntry *)NULL, **sorted_dirlist=(direntry **)NULL;
++#endif
++
++ /*
++ * First, two general initializations are applied. These have been moved
++ * here from process_zipfiles() because they are only needed for accessing
++ * and/or extracting the data content of the zip archive.
++ */
++
++ /* a) initialize the CRC table pointer (once) */
++ if (CRC_32_TAB == NULL) {
++ if ((CRC_32_TAB = get_crc_table()) == NULL) {
++ return PK_MEM;
++ }
++ }
++
++#if (!defined(SFX) || defined(SFX_EXDIR))
++ /* b) check out if specified extraction root directory exists */
++ if (uO.exdir != (char *)NULL && G.extract_flag) {
++ G.create_dirs = !uO.fflag;
++ if ((error = checkdir(__G__ uO.exdir, ROOT)) > MPN_INF_SKIP) {
++ /* out of memory, or file in way */
++ return (error == MPN_NOMEM ? PK_MEM : PK_ERR);
++ }
++ }
++#endif /* !SFX || SFX_EXDIR */
++
++/*---------------------------------------------------------------------------
++ The basic idea of this function is as follows. Since the central di-
++ rectory lies at the end of the zipfile and the member files lie at the
++ beginning or middle or wherever, it is not very desirable to simply
++ read a central directory entry, jump to the member and extract it, and
++ then jump back to the central directory. In the case of a large zipfile
++ this would lead to a whole lot of disk-grinding, especially if each mem-
++ ber file is small. Instead, we read from the central directory the per-
++ tinent information for a block of files, then go extract/test the whole
++ block. Thus this routine contains two small(er) loops within a very
++ large outer loop: the first of the small ones reads a block of files
++ from the central directory; the second extracts or tests each file; and
++ the outer one loops over blocks. There's some file-pointer positioning
++ stuff in between, but that's about it. Btw, it's because of this jump-
++ ing around that we can afford to be lenient if an error occurs in one of
++ the member files: we should still be able to go find the other members,
++ since we know the offset of each from the beginning of the zipfile.
++ ---------------------------------------------------------------------------*/
++
++ G.pInfo = G.info;
++
++#if CRYPT
++ G.newzip = TRUE;
++#endif
++#ifndef SFX
++ G.reported_backslash = FALSE;
++#endif
++
++ /* malloc space for check on unmatched filespecs (OK if one or both NULL) */
++ if (G.filespecs > 0 &&
++ (fn_matched=(int *)malloc(G.filespecs*sizeof(int))) != (int *)NULL)
++ for (i = 0; i < G.filespecs; ++i)
++ fn_matched[i] = FALSE;
++ if (G.xfilespecs > 0 &&
++ (xn_matched=(int *)malloc(G.xfilespecs*sizeof(int))) != (int *)NULL)
++ for (i = 0; i < G.xfilespecs; ++i)
++ xn_matched[i] = FALSE;
++
++/*---------------------------------------------------------------------------
++ Begin main loop over blocks of member files. We know the entire central
++ directory is on this disk: we would not have any of this information un-
++ less the end-of-central-directory record was on this disk, and we would
++ not have gotten to this routine unless this is also the disk on which
++ the central directory starts. In practice, this had better be the ONLY
++ disk in the archive, but we'll add multi-disk support soon.
++ ---------------------------------------------------------------------------*/
++
++ members_processed = 0;
++#ifndef SFX
++ no_endsig_found = FALSE;
++#endif
++ reached_end = FALSE;
++ while (!reached_end) {
++ j = 0;
++#ifdef AMIGA
++ memzero(G.filenotes, DIR_BLKSIZ * sizeof(char *));
++#endif
++
++ /*
++ * Loop through files in central directory, storing offsets, file
++ * attributes, case-conversion and text-conversion flags until block
++ * size is reached.
++ */
++
++ while ((j < DIR_BLKSIZ)) {
++ G.pInfo = &G.info[j];
++
++ if (readbuf(__G__ G.sig, 4) == 0) {
++ error_in_archive = PK_EOF;
++ reached_end = TRUE; /* ...so no more left to do */
++ break;
++ }
++ if (memcmp(G.sig, central_hdr_sig, 4)) { /* is it a new entry? */
++ /* no new central directory entry
++ * -> is the number of processed entries compatible with the
++ * number of entries as stored in the end_central record?
++ */
++ if ((members_processed
++ & (G.ecrec.have_ecr64 ? MASK_ZUCN64 : MASK_ZUCN16))
++ == G.ecrec.total_entries_central_dir) {
++#ifndef SFX
++ /* yes, so look if we ARE back at the end_central record
++ */
++ no_endsig_found =
++ ( (memcmp(G.sig,
++ (G.ecrec.have_ecr64 ?
++ end_central64_sig : end_central_sig),
++ 4) != 0)
++ && (!G.ecrec.is_zip64_archive)
++ && (memcmp(G.sig, end_central_sig, 4) != 0)
++ );
++#endif /* !SFX */
++ } else {
++ /* no; we have found an error in the central directory
++ * -> report it and stop searching for more Zip entries
++ */
++ Info(slide, 0x401, ((char *)slide,
++ LoadFarString(CentSigMsg), j + blknum*DIR_BLKSIZ + 1));
++ Info(slide, 0x401,
++ ((char *)slide,"%s", LoadFarString(ReportMsg)));
++ error_in_archive = PK_BADERR;
++ }
++ reached_end = TRUE; /* ...so no more left to do */
++ break;
++ }
++ /* process_cdir_file_hdr() sets pInfo->hostnum, pInfo->lcflag */
++ if ((error = process_cdir_file_hdr(__G)) != PK_COOL) {
++ error_in_archive = error; /* only PK_EOF defined */
++ reached_end = TRUE; /* ...so no more left to do */
++ break;
++ }
++ if ((error = do_string(__G__ G.crec.filename_length, DS_FN)) !=
++ PK_COOL)
++ {
++ if (error > error_in_archive)
++ error_in_archive = error;
++ if (error > PK_WARN) { /* fatal: no more left to do */
++ Info(slide, 0x401, ((char *)slide,
++ LoadFarString(FilNamMsg),
++ FnFilter1(G.filename), "central"));
++ reached_end = TRUE;
++ break;
++ }
++ }
++ if ((error = do_string(__G__ G.crec.extra_field_length,
++ EXTRA_FIELD)) != 0)
++ {
++ if (error > error_in_archive)
++ error_in_archive = error;
++ if (error > PK_WARN) { /* fatal */
++ Info(slide, 0x401, ((char *)slide,
++ LoadFarString(ExtFieldMsg),
++ FnFilter1(G.filename), "central"));
++ reached_end = TRUE;
++ break;
++ }
++ }
++#ifdef AMIGA
++ G.filenote_slot = j;
++ if ((error = do_string(__G__ G.crec.file_comment_length,
++ uO.N_flag ? FILENOTE : SKIP)) != PK_COOL)
++#else
++ if ((error = do_string(__G__ G.crec.file_comment_length, SKIP))
++ != PK_COOL)
++#endif
++ {
++ if (error > error_in_archive)
++ error_in_archive = error;
++ if (error > PK_WARN) { /* fatal */
++ Info(slide, 0x421, ((char *)slide,
++ LoadFarString(BadFileCommLength),
++ FnFilter1(G.filename)));
++ reached_end = TRUE;
++ break;
++ }
++ }
++ if (G.process_all_files) {
++ if (store_info(__G))
++ ++j; /* file is OK; info[] stored; continue with next */
++ else
++ ++num_skipped;
++ } else {
++ int do_this_file;
++
++ if (G.filespecs == 0)
++ do_this_file = TRUE;
++ else { /* check if this entry matches an `include' argument */
++ do_this_file = FALSE;
++ for (i = 0; i < G.filespecs; i++)
++ if (match(G.filename, G.pfnames[i], uO.C_flag WISEP)) {
++ do_this_file = TRUE; /* ^-- ignore case or not? */
++ if (fn_matched)
++ fn_matched[i] = TRUE;
++ break; /* found match, so stop looping */
++ }
++ }
++ if (do_this_file) { /* check if this is an excluded file */
++ for (i = 0; i < G.xfilespecs; i++)
++ if (match(G.filename, G.pxnames[i], uO.C_flag WISEP)) {
++ do_this_file = FALSE; /* ^-- ignore case or not? */
++ if (xn_matched)
++ xn_matched[i] = TRUE;
++ break;
++ }
++ }
++ if (do_this_file) {
++ if (store_info(__G))
++ ++j; /* file is OK */
++ else
++ ++num_skipped; /* unsupp. compression or encryption */
++ }
++ } /* end if (process_all_files) */
++
++ members_processed++;
++
++ } /* end while-loop (adding files to current block) */
++
++ /* save position in central directory so can come back later */
++ cd_bufstart = G.cur_zipfile_bufstart;
++ cd_inptr = G.inptr;
++ cd_incnt = G.incnt;
++
++ /*-----------------------------------------------------------------------
++ Second loop: process files in current block, extracting or testing
++ each one.
++ -----------------------------------------------------------------------*/
++
++ error = extract_or_test_entrylist(__G__ j,
++ &filnum, &num_bad_pwd, &old_extra_bytes,
++#ifdef SET_DIR_ATTRIB
++ &num_dirs, &dirlist,
++#endif
++ error_in_archive);
++ if (error != PK_COOL) {
++ if (error > error_in_archive)
++ error_in_archive = error;
++ /* ...and keep going (unless disk full or user break) */
++ if (G.disk_full > 1 || error_in_archive == IZ_CTRLC) {
++ /* clear reached_end to signal premature stop ... */
++ reached_end = FALSE;
++ /* ... and cancel scanning the central directory */
++ break;
++ }
++ }
++
++
++ /*
++ * Jump back to where we were in the central directory, then go and do
++ * the next batch of files.
++ */
++
++#ifdef USE_STRM_INPUT
++ zfseeko(G.zipfd, cd_bufstart, SEEK_SET);
++ G.cur_zipfile_bufstart = zftello(G.zipfd);
++#else /* !USE_STRM_INPUT */
++ G.cur_zipfile_bufstart =
++ zlseek(G.zipfd, cd_bufstart, SEEK_SET);
++#endif /* ?USE_STRM_INPUT */
++ read(G.zipfd, (char *)G.inbuf, INBUFSIZ); /* been here before... */
++ G.inptr = cd_inptr;
++ G.incnt = cd_incnt;
++ ++blknum;
++
++#ifdef TEST
++ printf("\ncd_bufstart = %ld (%.8lXh)\n", cd_bufstart, cd_bufstart);
++ printf("cur_zipfile_bufstart = %ld (%.8lXh)\n", cur_zipfile_bufstart,
++ cur_zipfile_bufstart);
++ printf("inptr-inbuf = %d\n", G.inptr-G.inbuf);
++ printf("incnt = %d\n\n", G.incnt);
++#endif
++
++ } /* end while-loop (blocks of files in central directory) */
++
++/*---------------------------------------------------------------------------
++ Process the list of deferred symlink extractions and finish up
++ the symbolic links.
++ ---------------------------------------------------------------------------*/
++
++#ifdef SYMLINKS
++ if (G.slink_last != NULL) {
++ if (QCOND2)
++ Info(slide, 0, ((char *)slide, LoadFarString(SymLnkDeferred)));
++ while (G.slink_head != NULL) {
++ set_deferred_symlink(__G__ G.slink_head);
++ /* remove the processed entry from the chain and free its memory */
++ G.slink_last = G.slink_head;
++ G.slink_head = G.slink_last->next;
++ free(G.slink_last);
++ }
++ G.slink_last = NULL;
++ }
++#endif /* SYMLINKS */
++
++/*---------------------------------------------------------------------------
++ Go back through saved list of directories, sort and set times/perms/UIDs
++ and GIDs from the deepest level on up.
++ ---------------------------------------------------------------------------*/
++
++#ifdef SET_DIR_ATTRIB
++ if (num_dirs > 0) {
++ sorted_dirlist = (direntry **)malloc(num_dirs*sizeof(direntry *));
++ if (sorted_dirlist == (direntry **)NULL) {
++ Info(slide, 0x401, ((char *)slide,
++ LoadFarString(DirlistSortNoMem)));
++ while (dirlist != (direntry *)NULL) {
++ direntry *d = dirlist;
++
++ dirlist = dirlist->next;
++ free(d);
++ }
++ } else {
++ ulg ndirs_fail = 0;
++
++ if (num_dirs == 1)
++ sorted_dirlist[0] = dirlist;
++ else {
++ for (i = 0; i < num_dirs; ++i) {
++ sorted_dirlist[i] = dirlist;
++ dirlist = dirlist->next;
++ }
++ qsort((char *)sorted_dirlist, num_dirs, sizeof(direntry *),
++ dircomp);
++ }
++
++ Trace((stderr, "setting directory times/perms/attributes\n"));
++ for (i = 0; i < num_dirs; ++i) {
++ direntry *d = sorted_dirlist[i];
++
++ Trace((stderr, "dir = %s\n", d->fn));
++ if ((error = set_direc_attribs(__G__ d)) != PK_OK) {
++ ndirs_fail++;
++ Info(slide, 0x201, ((char *)slide,
++ LoadFarString(DirlistSetAttrFailed), d->fn));
++ if (!error_in_archive)
++ error_in_archive = error;
++ }
++ free(d);
++ }
++ free(sorted_dirlist);
++ if (!uO.tflag && QCOND2) {
++ if (ndirs_fail > 0)
++ Info(slide, 0, ((char *)slide,
++ LoadFarString(DirlistFailAttrSum), ndirs_fail));
++ }
++ }
++ }
++#endif /* SET_DIR_ATTRIB */
++
++/*---------------------------------------------------------------------------
++ Check for unmatched filespecs on command line and print warning if any
++ found. Free allocated memory. (But suppress check when central dir
++ scan was interrupted prematurely.)
++ ---------------------------------------------------------------------------*/
++
++ if (fn_matched) {
++ if (reached_end) for (i = 0; i < G.filespecs; ++i)
++ if (!fn_matched[i]) {
++#ifdef DLL
++ if (!G.redirect_data && !G.redirect_text)
++ Info(slide, 0x401, ((char *)slide,
++ LoadFarString(FilenameNotMatched), G.pfnames[i]));
++ else
++ setFileNotFound(__G);
++#else
++ Info(slide, 1, ((char *)slide,
++ LoadFarString(FilenameNotMatched), G.pfnames[i]));
++#endif
++ if (error_in_archive <= PK_WARN)
++ error_in_archive = PK_FIND; /* some files not found */
++ }
++ free((zvoid *)fn_matched);
++ }
++ if (xn_matched) {
++ if (reached_end) for (i = 0; i < G.xfilespecs; ++i)
++ if (!xn_matched[i])
++ Info(slide, 0x401, ((char *)slide,
++ LoadFarString(ExclFilenameNotMatched), G.pxnames[i]));
++ free((zvoid *)xn_matched);
++ }
++
++/*---------------------------------------------------------------------------
++ Now, all locally allocated memory has been released. When the central
++ directory processing has been interrupted prematurely, it is safe to
++ return immediately. All completeness checks and summary messages are
++ skipped in this case.
++ ---------------------------------------------------------------------------*/
++ if (!reached_end)
++ return error_in_archive;
++
++/*---------------------------------------------------------------------------
++ Double-check that we're back at the end-of-central-directory record, and
++ print quick summary of results, if we were just testing the archive. We
++ send the summary to stdout so that people doing the testing in the back-
++ ground and redirecting to a file can just do a "tail" on the output file.
++ ---------------------------------------------------------------------------*/
++
++#ifndef SFX
++ if (no_endsig_found) { /* just to make sure */
++ Info(slide, 0x401, ((char *)slide,"%s", LoadFarString(EndSigMsg)));
++ Info(slide, 0x401, ((char *)slide,"%s", LoadFarString(ReportMsg)));
++ if (!error_in_archive) /* don't overwrite stronger error */
++ error_in_archive = PK_WARN;
++ }
++#endif /* !SFX */
++ if (uO.tflag) {
++ ulg num = filnum - num_bad_pwd;
++
++ if (uO.qflag < 2) { /* GRR 930710: was (uO.qflag == 1) */
++ if (error_in_archive)
++ Info(slide, 0, ((char *)slide, LoadFarString(ErrorInArchive),
++ (error_in_archive == PK_WARN)? "warning-" : "", G.zipfn));
++ else if (num == 0L)
++ Info(slide, 0, ((char *)slide, LoadFarString(ZeroFilesTested),
++ G.zipfn));
++ else if (G.process_all_files && (num_skipped+num_bad_pwd == 0L))
++ Info(slide, 0, ((char *)slide, LoadFarString(NoErrInCompData),
++ G.zipfn));
++ else
++ Info(slide, 0, ((char *)slide, LoadFarString(NoErrInTestedFiles)
++ , G.zipfn, num, (num==1L)? "":"s"));
++ if (num_skipped > 0L)
++ Info(slide, 0, ((char *)slide, LoadFarString(FilesSkipped),
++ num_skipped, (num_skipped==1L)? "":"s"));
++#if CRYPT
++ if (num_bad_pwd > 0L)
++ Info(slide, 0, ((char *)slide, LoadFarString(FilesSkipBadPasswd)
++ , num_bad_pwd, (num_bad_pwd==1L)? "":"s"));
++#endif /* CRYPT */
++ }
++ }
++
++ /* give warning if files not tested or extracted (first condition can still
++ * happen if zipfile is empty and no files specified on command line) */
++
++ if ((filnum == 0) && error_in_archive <= PK_WARN) {
++ if (num_skipped > 0L)
++ error_in_archive = IZ_UNSUP; /* unsupport. compression/encryption */
++ else
++ error_in_archive = PK_FIND; /* no files found at all */
++ }
++#if CRYPT
++ else if ((filnum == num_bad_pwd) && error_in_archive <= PK_WARN)
++ error_in_archive = IZ_BADPWD; /* bad passwd => all files skipped */
++#endif
++ else if ((num_skipped > 0L) && error_in_archive <= PK_WARN)
++ error_in_archive = IZ_UNSUP; /* was PK_WARN; Jean-loup complained */
++#if CRYPT
++ else if ((num_bad_pwd > 0L) && !error_in_archive)
++ error_in_archive = PK_WARN;
++#endif
++
++ return error_in_archive;
++
++} /* end function extract_or_test_files() */
++
++
++
++
++
++/***************************/
++/* Function store_info() */
++/***************************/
++
++static int store_info(__G) /* return 0 if skipping, 1 if OK */
++ __GDEF
++{
++#ifdef USE_BZIP2
++# define UNKN_BZ2 (G.crec.compression_method!=BZIPPED)
++#else
++# define UNKN_BZ2 TRUE /* bzip2 unknown */
++#endif
++
++#ifdef USE_LZMA
++# define UNKN_LZMA (G.crec.compression_method!=LZMAED)
++#else
++# define UNKN_LZMA TRUE /* LZMA unknown */
++#endif
++
++#ifdef USE_WAVP
++# define UNKN_WAVP (G.crec.compression_method!=WAVPACKED)
++#else
++# define UNKN_WAVP TRUE /* WavPack unknown */
++#endif
++
++#ifdef USE_PPMD
++# define UNKN_PPMD (G.crec.compression_method!=PPMDED)
++#else
++# define UNKN_PPMD TRUE /* PPMd unknown */
++#endif
++
++#ifdef SFX
++# ifdef USE_DEFLATE64
++# define UNKN_COMPR \
++ (G.crec.compression_method!=STORED && G.crec.compression_method<DEFLATED \
++ && G.crec.compression_method>ENHDEFLATED \
++ && UNKN_BZ2 && UNKN_LZMA && UNKN_WAVP && UNKN_PPMD)
++# else
++# define UNKN_COMPR \
++ (G.crec.compression_method!=STORED && G.crec.compression_method!=DEFLATED\
++ && UNKN_BZ2 && UNKN_LZMA && UNKN_WAVP && UNKN_PPMD)
++# endif
++#else
++# ifdef COPYRIGHT_CLEAN /* no reduced files */
++# define UNKN_RED (G.crec.compression_method >= REDUCED1 && \
++ G.crec.compression_method <= REDUCED4)
++# else
++# define UNKN_RED FALSE /* reducing not unknown */
++# endif
++# ifdef LZW_CLEAN /* no shrunk files */
++# define UNKN_SHR (G.crec.compression_method == SHRUNK)
++# else
++# define UNKN_SHR FALSE /* unshrinking not unknown */
++# endif
++# ifdef USE_DEFLATE64
++# define UNKN_COMPR (UNKN_RED || UNKN_SHR || \
++ G.crec.compression_method==TOKENIZED || \
++ (G.crec.compression_method>ENHDEFLATED && UNKN_BZ2 && UNKN_LZMA \
++ && UNKN_WAVP && UNKN_PPMD))
++# else
++# define UNKN_COMPR (UNKN_RED || UNKN_SHR || \
++ G.crec.compression_method==TOKENIZED || \
++ (G.crec.compression_method>DEFLATED && UNKN_BZ2 && UNKN_LZMA \
++ && UNKN_WAVP && UNKN_PPMD))
++# endif
++#endif
++
++#if (defined(USE_BZIP2) && (UNZIP_VERSION < UNZIP_BZ2VERS))
++ int unzvers_support = (UNKN_BZ2 ? UNZIP_VERSION : UNZIP_BZ2VERS);
++# define UNZVERS_SUPPORT unzvers_support
++#else
++# define UNZVERS_SUPPORT UNZIP_VERSION
++#endif
++
++/*---------------------------------------------------------------------------
++ Check central directory info for version/compatibility requirements.
++ ---------------------------------------------------------------------------*/
++
++ G.pInfo->encrypted = G.crec.general_purpose_bit_flag & 1; /* bit field */
++ G.pInfo->ExtLocHdr = (G.crec.general_purpose_bit_flag & 8) == 8; /* bit */
++ G.pInfo->textfile = G.crec.internal_file_attributes & 1; /* bit field */
++ G.pInfo->crc = G.crec.crc32;
++ G.pInfo->compr_size = G.crec.csize;
++ G.pInfo->uncompr_size = G.crec.ucsize;
++
++ switch (uO.aflag) {
++ case 0:
++ G.pInfo->textmode = FALSE; /* bit field */
++ break;
++ case 1:
++ G.pInfo->textmode = G.pInfo->textfile; /* auto-convert mode */
++ break;
++ default: /* case 2: */
++ G.pInfo->textmode = TRUE;
++ break;
++ }
++
++ if (G.crec.version_needed_to_extract[1] == VMS_) {
++ if (G.crec.version_needed_to_extract[0] > VMS_UNZIP_VERSION) {
++ if (!((uO.tflag && uO.qflag) || (!uO.tflag && !QCOND2)))
++ Info(slide, 0x401, ((char *)slide, LoadFarString(VersionMsg),
++ FnFilter1(G.filename), "VMS",
++ G.crec.version_needed_to_extract[0] / 10,
++ G.crec.version_needed_to_extract[0] % 10,
++ VMS_UNZIP_VERSION / 10, VMS_UNZIP_VERSION % 10));
++ return 0;
++ }
++#ifndef VMS /* won't be able to use extra field, but still have data */
++ else if (!uO.tflag && !IS_OVERWRT_ALL) { /* if -o, extract anyway */
++ Info(slide, 0x481, ((char *)slide, LoadFarString(VMSFormatQuery),
++ FnFilter1(G.filename)));
++ fgets(G.answerbuf, sizeof(G.answerbuf), stdin);
++ if ((*G.answerbuf != 'y') && (*G.answerbuf != 'Y'))
++ return 0;
++ }
++#endif /* !VMS */
++ /* usual file type: don't need VMS to extract */
++ } else if (G.crec.version_needed_to_extract[0] > UNZVERS_SUPPORT) {
++ if (!((uO.tflag && uO.qflag) || (!uO.tflag && !QCOND2)))
++ Info(slide, 0x401, ((char *)slide, LoadFarString(VersionMsg),
++ FnFilter1(G.filename), "PK",
++ G.crec.version_needed_to_extract[0] / 10,
++ G.crec.version_needed_to_extract[0] % 10,
++ UNZVERS_SUPPORT / 10, UNZVERS_SUPPORT % 10));
++ return 0;
++ }
++
++ if (UNKN_COMPR) {
++ if (!((uO.tflag && uO.qflag) || (!uO.tflag && !QCOND2))) {
++#ifndef SFX
++ unsigned cmpridx;
++
++ if ((cmpridx = find_compr_idx(G.crec.compression_method))
++ < NUM_METHODS)
++ Info(slide, 0x401, ((char *)slide, LoadFarString(ComprMsgName),
++ FnFilter1(G.filename),
++ LoadFarStringSmall(ComprNames[cmpridx])));
++ else
++#endif
++ Info(slide, 0x401, ((char *)slide, LoadFarString(ComprMsgNum),
++ FnFilter1(G.filename),
++ G.crec.compression_method));
++ }
++ return 0;
++ }
++#if (!CRYPT)
++ if (G.pInfo->encrypted) {
++ if (!((uO.tflag && uO.qflag) || (!uO.tflag && !QCOND2)))
++ Info(slide, 0x401, ((char *)slide, LoadFarString(SkipEncrypted),
++ FnFilter1(G.filename)));
++ return 0;
++ }
++#endif /* !CRYPT */
++
++#ifndef SFX
++ /* store a copy of the central header filename for later comparison */
++ if ((G.pInfo->cfilname = zfmalloc(strlen(G.filename) + 1)) == NULL) {
++ Info(slide, 0x401, ((char *)slide, LoadFarString(WarnNoMemCFName),
++ FnFilter1(G.filename)));
++ } else
++ zfstrcpy(G.pInfo->cfilname, G.filename);
++#endif /* !SFX */
++
++ /* map whatever file attributes we have into the local format */
++ mapattr(__G); /* GRR: worry about return value later */
++
++ G.pInfo->diskstart = G.crec.disk_number_start;
++ G.pInfo->offset = (zoff_t)G.crec.relative_offset_local_header;
++ return 1;
++
++} /* end function store_info() */
++
++
++
++
++
++#ifndef SFX
++/*******************************/
++/* Function find_compr_idx() */
++/*******************************/
++
++unsigned find_compr_idx(compr_methodnum)
++ unsigned compr_methodnum;
++{
++ unsigned i;
++
++ for (i = 0; i < NUM_METHODS; i++) {
++ if (ComprIDs[i] == compr_methodnum) break;
++ }
++ return i;
++}
++#endif /* !SFX */
++
++
++
++
++
++/******************************************/
++/* Function extract_or_test_entrylist() */
++/******************************************/
++
++static int extract_or_test_entrylist(__G__ numchunk,
++ pfilnum, pnum_bad_pwd, pold_extra_bytes,
++#ifdef SET_DIR_ATTRIB
++ pnum_dirs, pdirlist,
++#endif
++ error_in_archive) /* return PK-type error code */
++ __GDEF
++ unsigned numchunk;
++ ulg *pfilnum;
++ ulg *pnum_bad_pwd;
++ zoff_t *pold_extra_bytes;
++#ifdef SET_DIR_ATTRIB
++ unsigned *pnum_dirs;
++ direntry **pdirlist;
++#endif
++ int error_in_archive;
++{
++ unsigned i;
++ int renamed, query;
++ int skip_entry;
++ zoff_t bufstart, inbuf_offset, request;
++ int error, errcode;
++
++/* possible values for local skip_entry flag: */
++#define SKIP_NO 0 /* do not skip this entry */
++#define SKIP_Y_EXISTING 1 /* skip this entry, do not overwrite file */
++#define SKIP_Y_NONEXIST 2 /* skip this entry, do not create new file */
++
++ /*-----------------------------------------------------------------------
++ Second loop: process files in current block, extracting or testing
++ each one.
++ -----------------------------------------------------------------------*/
++
++ for (i = 0; i < numchunk; ++i) {
++ (*pfilnum)++; /* *pfilnum = i + blknum*DIR_BLKSIZ + 1; */
++ G.pInfo = &G.info[i];
++#ifdef NOVELL_BUG_FAILSAFE
++ G.dne = FALSE; /* assume file exists until stat() says otherwise */
++#endif
++
++ /* if the target position is not within the current input buffer
++ * (either haven't yet read far enough, or (maybe) skipping back-
++ * ward), skip to the target position and reset readbuf(). */
++
++ /* seek_zipf(__G__ pInfo->offset); */
++ request = G.pInfo->offset + G.extra_bytes;
++ inbuf_offset = request % INBUFSIZ;
++ bufstart = request - inbuf_offset;
++
++ Trace((stderr, "\ndebug: request = %ld, inbuf_offset = %ld\n",
++ (long)request, (long)inbuf_offset));
++ Trace((stderr,
++ "debug: bufstart = %ld, cur_zipfile_bufstart = %ld\n",
++ (long)bufstart, (long)G.cur_zipfile_bufstart));
++ if (request < 0) {
++ Info(slide, 0x401, ((char *)slide, LoadFarStringSmall(SeekMsg),
++ G.zipfn, LoadFarString(ReportMsg)));
++ error_in_archive = PK_ERR;
++ if (*pfilnum == 1 && G.extra_bytes != 0L) {
++ Info(slide, 0x401, ((char *)slide,
++ LoadFarString(AttemptRecompensate)));
++ *pold_extra_bytes = G.extra_bytes;
++ G.extra_bytes = 0L;
++ request = G.pInfo->offset; /* could also check if != 0 */
++ inbuf_offset = request % INBUFSIZ;
++ bufstart = request - inbuf_offset;
++ Trace((stderr, "debug: request = %ld, inbuf_offset = %ld\n",
++ (long)request, (long)inbuf_offset));
++ Trace((stderr,
++ "debug: bufstart = %ld, cur_zipfile_bufstart = %ld\n",
++ (long)bufstart, (long)G.cur_zipfile_bufstart));
++ /* try again */
++ if (request < 0) {
++ Trace((stderr,
++ "debug: recompensated request still < 0\n"));
++ Info(slide, 0x401, ((char *)slide,
++ LoadFarStringSmall(SeekMsg),
++ G.zipfn, LoadFarString(ReportMsg)));
++ error_in_archive = PK_BADERR;
++ continue;
++ }
++ } else {
++ error_in_archive = PK_BADERR;
++ continue; /* this one hosed; try next */
++ }
++ }
++
++ if (bufstart != G.cur_zipfile_bufstart) {
++ Trace((stderr, "debug: bufstart != cur_zipfile_bufstart\n"));
++#ifdef USE_STRM_INPUT
++ zfseeko(G.zipfd, bufstart, SEEK_SET);
++ G.cur_zipfile_bufstart = zftello(G.zipfd);
++#else /* !USE_STRM_INPUT */
++ G.cur_zipfile_bufstart =
++ zlseek(G.zipfd, bufstart, SEEK_SET);
++#endif /* ?USE_STRM_INPUT */
++ if ((G.incnt = read(G.zipfd, (char *)G.inbuf, INBUFSIZ)) <= 0)
++ {
++ Info(slide, 0x401, ((char *)slide, LoadFarString(OffsetMsg),
++ *pfilnum, "lseek", (long)bufstart));
++ error_in_archive = PK_BADERR;
++ continue; /* can still do next file */
++ }
++ G.inptr = G.inbuf + (int)inbuf_offset;
++ G.incnt -= (int)inbuf_offset;
++ } else {
++ G.incnt += (int)(G.inptr-G.inbuf) - (int)inbuf_offset;
++ G.inptr = G.inbuf + (int)inbuf_offset;
++ }
++
++ /* should be in proper position now, so check for sig */
++ if (readbuf(__G__ G.sig, 4) == 0) { /* bad offset */
++ Info(slide, 0x401, ((char *)slide, LoadFarString(OffsetMsg),
++ *pfilnum, "EOF", (long)request));
++ error_in_archive = PK_BADERR;
++ continue; /* but can still try next one */
++ }
++ if (memcmp(G.sig, local_hdr_sig, 4)) {
++ Info(slide, 0x401, ((char *)slide, LoadFarString(OffsetMsg),
++ *pfilnum, LoadFarStringSmall(LocalHdrSig), (long)request));
++ /*
++ GRRDUMP(G.sig, 4)
++ GRRDUMP(local_hdr_sig, 4)
++ */
++ error_in_archive = PK_ERR;
++ if ((*pfilnum == 1 && G.extra_bytes != 0L) ||
++ (G.extra_bytes == 0L && *pold_extra_bytes != 0L)) {
++ Info(slide, 0x401, ((char *)slide,
++ LoadFarString(AttemptRecompensate)));
++ if (G.extra_bytes) {
++ *pold_extra_bytes = G.extra_bytes;
++ G.extra_bytes = 0L;
++ } else
++ G.extra_bytes = *pold_extra_bytes; /* third attempt */
++ if (((error = seek_zipf(__G__ G.pInfo->offset)) != PK_OK) ||
++ (readbuf(__G__ G.sig, 4) == 0)) { /* bad offset */
++ if (error != PK_BADERR)
++ Info(slide, 0x401, ((char *)slide,
++ LoadFarString(OffsetMsg), *pfilnum, "EOF",
++ (long)request));
++ error_in_archive = PK_BADERR;
++ continue; /* but can still try next one */
++ }
++ if (memcmp(G.sig, local_hdr_sig, 4)) {
++ Info(slide, 0x401, ((char *)slide,
++ LoadFarString(OffsetMsg), *pfilnum,
++ LoadFarStringSmall(LocalHdrSig), (long)request));
++ error_in_archive = PK_BADERR;
++ continue;
++ }
++ } else
++ continue; /* this one hosed; try next */
++ }
++ if ((error = process_local_file_hdr(__G)) != PK_COOL) {
++ Info(slide, 0x421, ((char *)slide, LoadFarString(BadLocalHdr),
++ *pfilnum));
++ error_in_archive = error; /* only PK_EOF defined */
++ continue; /* can still try next one */
++ }
++#if (!defined(SFX) && defined(UNICODE_SUPPORT))
++ if (((G.lrec.general_purpose_bit_flag & (1 << 11)) == (1 << 11))
++ != (G.pInfo->GPFIsUTF8 != 0)) {
++ if (QCOND2) {
++# ifdef SMALL_MEM
++ char *temp_cfilnam = slide + (7 * (WSIZE>>3));
++
++ zfstrcpy((char Far *)temp_cfilnam, G.pInfo->cfilname);
++# define cFile_PrintBuf temp_cfilnam
++# else
++# define cFile_PrintBuf G.pInfo->cfilname
++# endif
++ Info(slide, 0x421, ((char *)slide,
++ LoadFarStringSmall2(GP11FlagsDiffer),
++ *pfilnum, FnFilter1(cFile_PrintBuf), G.pInfo->GPFIsUTF8));
++# undef cFile_PrintBuf
++ }
++ if (error_in_archive < PK_WARN)
++ error_in_archive = PK_WARN;
++ }
++#endif /* !SFX && UNICODE_SUPPORT */
++ if ((error = do_string(__G__ G.lrec.filename_length, DS_FN_L)) !=
++ PK_COOL)
++ {
++ if (error > error_in_archive)
++ error_in_archive = error;
++ if (error > PK_WARN) {
++ Info(slide, 0x401, ((char *)slide, LoadFarString(FilNamMsg),
++ FnFilter1(G.filename), "local"));
++ continue; /* go on to next one */
++ }
++ }
++ if (G.extra_field != (uch *)NULL) {
++ free(G.extra_field);
++ G.extra_field = (uch *)NULL;
++ }
++ if ((error =
++ do_string(__G__ G.lrec.extra_field_length, EXTRA_FIELD)) != 0)
++ {
++ if (error > error_in_archive)
++ error_in_archive = error;
++ if (error > PK_WARN) {
++ Info(slide, 0x401, ((char *)slide,
++ LoadFarString(ExtFieldMsg),
++ FnFilter1(G.filename), "local"));
++ continue; /* go on */
++ }
++ }
++#ifndef SFX
++ /* Filename consistency checks must come after reading in the local
++ * extra field, so that a UTF-8 entry name e.f. block has already
++ * been processed.
++ */
++ if (G.pInfo->cfilname != (char Far *)NULL) {
++ if (zfstrcmp(G.pInfo->cfilname, G.filename) != 0) {
++# ifdef SMALL_MEM
++ char *temp_cfilnam = slide + (7 * (WSIZE>>3));
++
++ zfstrcpy((char Far *)temp_cfilnam, G.pInfo->cfilname);
++# define cFile_PrintBuf temp_cfilnam
++# else
++# define cFile_PrintBuf G.pInfo->cfilname
++# endif
++ Info(slide, 0x401, ((char *)slide,
++ LoadFarStringSmall2(LvsCFNamMsg),
++ FnFilter2(cFile_PrintBuf), FnFilter1(G.filename)));
++# undef cFile_PrintBuf
++ zfstrcpy(G.filename, G.pInfo->cfilname);
++ if (error_in_archive < PK_WARN)
++ error_in_archive = PK_WARN;
++ }
++ zffree(G.pInfo->cfilname);
++ G.pInfo->cfilname = (char Far *)NULL;
++ }
++#endif /* !SFX */
++ /* Size consistency checks must come after reading in the local extra
++ * field, so that any Zip64 extension local e.f. block has already
++ * been processed.
++ */
++ if (G.lrec.compression_method == STORED) {
++ zusz_t csiz_decrypted = G.lrec.csize;
++
++ if (G.pInfo->encrypted) {
++ if (csiz_decrypted < 12) {
++ /* handle the error now to prevent unsigned overflow */
++ Info(slide, 0x401, ((char *)slide,
++ LoadFarStringSmall(ErrUnzipNoFile),
++ LoadFarString(InvalidComprData),
++ LoadFarStringSmall2(Inflate)));
++ return PK_ERR;
++ }
++ csiz_decrypted -= 12;
++ }
++ if (G.lrec.ucsize != csiz_decrypted) {
++ Info(slide, 0x401, ((char *)slide,
++ LoadFarStringSmall2(WrnStorUCSizCSizDiff),
++ FnFilter1(G.filename),
++ FmZofft(G.lrec.ucsize, NULL, "u"),
++ FmZofft(csiz_decrypted, NULL, "u")));
++ G.lrec.ucsize = csiz_decrypted;
++ if (error_in_archive < PK_WARN)
++ error_in_archive = PK_WARN;
++ }
++ }
++
++#if CRYPT
++ if (G.pInfo->encrypted &&
++ (error = decrypt(__G__ uO.pwdarg)) != PK_COOL) {
++ if (error == PK_WARN) {
++ if (!((uO.tflag && uO.qflag) || (!uO.tflag && !QCOND2)))
++ Info(slide, 0x401, ((char *)slide,
++ LoadFarString(SkipIncorrectPasswd),
++ FnFilter1(G.filename)));
++ ++(*pnum_bad_pwd);
++ } else { /* (error > PK_WARN) */
++ if (error > error_in_archive)
++ error_in_archive = error;
++ Info(slide, 0x401, ((char *)slide,
++ LoadFarString(SkipCannotGetPasswd),
++ FnFilter1(G.filename)));
++ }
++ continue; /* go on to next file */
++ }
++#endif /* CRYPT */
++
++ /*
++ * just about to extract file: if extracting to disk, check if
++ * already exists, and if so, take appropriate action according to
++ * fflag/uflag/overwrite_all/etc. (we couldn't do this in upper
++ * loop because we don't store the possibly renamed filename[] in
++ * info[])
++ */
++#ifdef DLL
++ if (!uO.tflag && !uO.cflag && !G.redirect_data)
++#else
++ if (!uO.tflag && !uO.cflag)
++#endif
++ {
++ renamed = FALSE; /* user hasn't renamed output file yet */
++
++startover:
++ query = FALSE;
++ skip_entry = SKIP_NO;
++ /* for files from DOS FAT, check for use of backslash instead
++ * of slash as directory separator (bug in some zipper(s); so
++ * far, not a problem in HPFS, NTFS or VFAT systems)
++ */
++#ifndef SFX
++ if (G.pInfo->hostnum == FS_FAT_ && !MBSCHR(G.filename, '/')) {
++ char *p=G.filename;
++
++ if (*p) do {
++ if (*p == '\\') {
++ if (!G.reported_backslash) {
++ Info(slide, 0x21, ((char *)slide,
++ LoadFarString(BackslashPathSep), G.zipfn));
++ G.reported_backslash = TRUE;
++ if (!error_in_archive)
++ error_in_archive = PK_WARN;
++ }
++ *p = '/';
++ }
++ } while (*PREINCSTR(p));
++ }
++#endif /* !SFX */
++
++ if (!renamed) {
++ /* remove absolute path specs */
++ if (G.filename[0] == '/') {
++ Info(slide, 0x401, ((char *)slide,
++ LoadFarString(AbsolutePathWarning),
++ FnFilter1(G.filename)));
++ if (!error_in_archive)
++ error_in_archive = PK_WARN;
++ do {
++ char *p = G.filename + 1;
++ do {
++ *(p-1) = *p;
++ } while (*p++ != '\0');
++ } while (G.filename[0] == '/');
++ }
++ }
++
++ /* mapname can create dirs if not freshening or if renamed */
++ error = mapname(__G__ renamed);
++ if ((errcode = error & ~MPN_MASK) != PK_OK &&
++ error_in_archive < errcode)
++ error_in_archive = errcode;
++ if ((errcode = error & MPN_MASK) > MPN_INF_TRUNC) {
++ if (errcode == MPN_CREATED_DIR) {
++#ifdef SET_DIR_ATTRIB
++ direntry *d_entry;
++
++ error = defer_dir_attribs(__G__ &d_entry);
++ if (d_entry == (direntry *)NULL) {
++ /* There may be no dir_attribs info available, or
++ * we have encountered a mem allocation error.
++ * In case of an error, report it and set program
++ * error state to warning level.
++ */
++ if (error) {
++ Info(slide, 0x401, ((char *)slide,
++ LoadFarString(DirlistEntryNoMem)));
++ if (!error_in_archive)
++ error_in_archive = PK_WARN;
++ }
++ } else {
++ d_entry->next = (*pdirlist);
++ (*pdirlist) = d_entry;
++ ++(*pnum_dirs);
++ }
++#endif /* SET_DIR_ATTRIB */
++ } else if (errcode == MPN_VOL_LABEL) {
++#ifdef DOS_OS2_W32
++ Info(slide, 0x401, ((char *)slide,
++ LoadFarString(SkipVolumeLabel),
++ FnFilter1(G.filename),
++ uO.volflag? "hard disk " : ""));
++#else
++ Info(slide, 1, ((char *)slide,
++ LoadFarString(SkipVolumeLabel),
++ FnFilter1(G.filename), ""));
++#endif
++ } else if (errcode > MPN_INF_SKIP &&
++ error_in_archive < PK_ERR)
++ error_in_archive = PK_ERR;
++ Trace((stderr, "mapname(%s) returns error code = %d\n",
++ FnFilter1(G.filename), error));
++ continue; /* go on to next file */
++ }
++
++#ifdef QDOS
++ QFilename(__G__ G.filename);
++#endif
++ switch (check_for_newer(__G__ G.filename)) {
++ case DOES_NOT_EXIST:
++#ifdef NOVELL_BUG_FAILSAFE
++ G.dne = TRUE; /* stat() says file DOES NOT EXIST */
++#endif
++ /* freshen (no new files): skip unless just renamed */
++ if (uO.fflag && !renamed)
++ skip_entry = SKIP_Y_NONEXIST;
++ break;
++ case EXISTS_AND_OLDER:
++#ifdef UNIXBACKUP
++ if (!uO.B_flag)
++#endif
++ {
++ if (IS_OVERWRT_NONE)
++ /* never overwrite: skip file */
++ skip_entry = SKIP_Y_EXISTING;
++ else if (!IS_OVERWRT_ALL)
++ query = TRUE;
++ }
++ break;
++ case EXISTS_AND_NEWER: /* (or equal) */
++#ifdef UNIXBACKUP
++ if ((!uO.B_flag && IS_OVERWRT_NONE) ||
++#else
++ if (IS_OVERWRT_NONE ||
++#endif
++ (uO.uflag && !renamed)) {
++ /* skip if update/freshen & orig name */
++ skip_entry = SKIP_Y_EXISTING;
++ } else {
++#ifdef UNIXBACKUP
++ if (!IS_OVERWRT_ALL && !uO.B_flag)
++#else
++ if (!IS_OVERWRT_ALL)
++#endif
++ query = TRUE;
++ }
++ break;
++ }
++#ifdef VMS
++ /* 2008-07-24 SMS.
++ * On VMS, if the file name includes a version number,
++ * and "-V" ("retain VMS version numbers", V_flag) is in
++ * effect, then the VMS-specific code will handle any
++ * conflicts with an existing file, making this query
++ * redundant. (Implicit "y" response here.)
++ */
++ if (query && uO.V_flag) {
++ /* Not discarding file versions. Look for one. */
++ int cndx = strlen(G.filename) - 1;
++
++ while ((cndx > 0) && (isdigit(G.filename[cndx])))
++ cndx--;
++ if (G.filename[cndx] == ';')
++ /* File version found; skip the generic query,
++ * proceeding with its default response "y".
++ */
++ query = FALSE;
++ }
++#endif /* VMS */
++ if (query) {
++#ifdef WINDLL
++ switch (G.lpUserFunctions->replace != NULL ?
++ (*G.lpUserFunctions->replace)(G.filename, FILNAMSIZ) :
++ IDM_REPLACE_NONE) {
++ case IDM_REPLACE_RENAME:
++ _ISO_INTERN(G.filename);
++ renamed = TRUE;
++ goto startover;
++ case IDM_REPLACE_ALL:
++ G.overwrite_mode = OVERWRT_ALWAYS;
++ /* FALL THROUGH, extract */
++ case IDM_REPLACE_YES:
++ break;
++ case IDM_REPLACE_NONE:
++ G.overwrite_mode = OVERWRT_NEVER;
++ /* FALL THROUGH, skip */
++ case IDM_REPLACE_NO:
++ skip_entry = SKIP_Y_EXISTING;
++ break;
++ }
++#else /* !WINDLL */
++ extent fnlen;
++reprompt:
++ Info(slide, 0x81, ((char *)slide,
++ LoadFarString(ReplaceQuery),
++ FnFilter1(G.filename)));
++ if (fgets(G.answerbuf, sizeof(G.answerbuf), stdin)
++ == (char *)NULL) {
++ Info(slide, 1, ((char *)slide,
++ LoadFarString(AssumeNone)));
++ *G.answerbuf = 'N';
++ if (!error_in_archive)
++ error_in_archive = 1; /* not extracted: warning */
++ }
++ switch (*G.answerbuf) {
++ case 'r':
++ case 'R':
++ do {
++ Info(slide, 0x81, ((char *)slide,
++ LoadFarString(NewNameQuery)));
++ fgets(G.filename, FILNAMSIZ, stdin);
++ /* usually get \n here: better check for it */
++ fnlen = strlen(G.filename);
++ if (lastchar(G.filename, fnlen) == '\n')
++ G.filename[--fnlen] = '\0';
++ } while (fnlen == 0);
++#ifdef WIN32 /* WIN32 fgets( ... , stdin) returns OEM coded strings */
++ _OEM_INTERN(G.filename);
++#endif
++ renamed = TRUE;
++ goto startover; /* sorry for a goto */
++ case 'A': /* dangerous option: force caps */
++ G.overwrite_mode = OVERWRT_ALWAYS;
++ /* FALL THROUGH, extract */
++ case 'y':
++ case 'Y':
++ break;
++ case 'N':
++ G.overwrite_mode = OVERWRT_NEVER;
++ /* FALL THROUGH, skip */
++ case 'n':
++ /* skip file */
++ skip_entry = SKIP_Y_EXISTING;
++ break;
++ case '\n':
++ case '\r':
++ /* Improve echo of '\n' and/or '\r'
++ (sizeof(G.answerbuf) == 10 (see globals.h), so
++ there is enough space for the provided text...) */
++ strcpy(G.answerbuf, "{ENTER}");
++ /* fall through ... */
++ default:
++ /* usually get \n here: remove it for nice display
++ (fnlen can be re-used here, we are outside the
++ "enter new filename" loop) */
++ fnlen = strlen(G.answerbuf);
++ if (lastchar(G.answerbuf, fnlen) == '\n')
++ G.answerbuf[--fnlen] = '\0';
++ Info(slide, 1, ((char *)slide,
++ LoadFarString(InvalidResponse), G.answerbuf));
++ goto reprompt; /* yet another goto? */
++ } /* end switch (*answerbuf) */
++#endif /* ?WINDLL */
++ } /* end if (query) */
++ if (skip_entry != SKIP_NO) {
++#ifdef WINDLL
++ if (skip_entry == SKIP_Y_EXISTING) {
++ /* report skipping of an existing entry */
++ Info(slide, 0, ((char *)slide,
++ ((IS_OVERWRT_NONE || !uO.uflag || renamed) ?
++ "Target file exists. Skipping %s\n" :
++ "Target file newer. Skipping %s\n"),
++ FnFilter1(G.filename)));
++ }
++#endif /* WINDLL */
++ continue;
++ }
++ } /* end if (extracting to disk) */
++
++#ifdef DLL
++ if ((G.statreportcb != NULL) &&
++ (*G.statreportcb)(__G__ UZ_ST_START_EXTRACT, G.zipfn,
++ G.filename, NULL)) {
++ return IZ_CTRLC; /* cancel operation by user request */
++ }
++#endif
++#ifdef MACOS /* MacOS is no preemptive OS, thus call event-handling by hand */
++ UserStop();
++#endif
++#ifdef AMIGA
++ G.filenote_slot = i;
++#endif
++ G.disk_full = 0;
++ if ((error = extract_or_test_member(__G)) != PK_COOL) {
++ if (error > error_in_archive)
++ error_in_archive = error; /* ...and keep going */
++#ifdef DLL
++ if (G.disk_full > 1 || error_in_archive == IZ_CTRLC) {
++#else
++ if (G.disk_full > 1) {
++#endif
++ return error_in_archive; /* (unless disk full) */
++ }
++ }
++#ifdef DLL
++ if ((G.statreportcb != NULL) &&
++ (*G.statreportcb)(__G__ UZ_ST_FINISH_MEMBER, G.zipfn,
++ G.filename, (zvoid *)&G.lrec.ucsize)) {
++ return IZ_CTRLC; /* cancel operation by user request */
++ }
++#endif
++#ifdef MACOS /* MacOS is no preemptive OS, thus call event-handling by hand */
++ UserStop();
++#endif
++ } /* end for-loop (i: files in current block) */
++
++ return error_in_archive;
++
++} /* end function extract_or_test_entrylist() */
++
++
++
++
++
++/* wsize is used in extract_or_test_member() and UZbunzip2() */
++#if (defined(DLL) && !defined(NO_SLIDE_REDIR))
++# define wsize G._wsize /* wsize is a variable */
++#else
++# define wsize WSIZE /* wsize is a constant */
++#endif
++
++/***************************************/
++/* Function extract_or_test_member() */
++/***************************************/
++
++static int extract_or_test_member(__G) /* return PK-type error code */
++ __GDEF
++{
++ char *nul="[empty] ", *txt="[text] ", *bin="[binary]";
++#ifdef CMS_MVS
++ char *ebc="[ebcdic]";
++#endif
++ register int b;
++ int r, error=PK_COOL;
++
++
++/*---------------------------------------------------------------------------
++ Initialize variables, buffers, etc.
++ ---------------------------------------------------------------------------*/
++
++ G.bits_left = 0;
++ G.bitbuf = 0L; /* unreduce and unshrink only */
++ G.zipeof = 0;
++ G.newfile = TRUE;
++ G.crc32val = CRCVAL_INITIAL;
++
++#ifdef SYMLINKS
++ /* If file is a (POSIX-compatible) symbolic link and we are extracting
++ * to disk, prepare to restore the link. */
++ G.symlnk = (G.pInfo->symlink &&
++ !uO.tflag && !uO.cflag && (G.lrec.ucsize > 0));
++#endif /* SYMLINKS */
++
++ if (uO.tflag) {
++ if (!uO.qflag)
++ Info(slide, 0, ((char *)slide, LoadFarString(ExtractMsg), "test",
++ FnFilter1(G.filename), "", ""));
++ } else {
++#ifdef DLL
++ if (uO.cflag && !G.redirect_data)
++#else
++ if (uO.cflag)
++#endif
++ {
++#if (defined(OS2) && defined(__IBMC__) && (__IBMC__ >= 200))
++ G.outfile = freopen("", "wb", stdout); /* VAC++ ignores setmode */
++#else
++ G.outfile = stdout;
++#endif
++#ifdef DOS_FLX_NLM_OS2_W32
++#if (defined(__HIGHC__) && !defined(FLEXOS))
++ setmode(G.outfile, _BINARY);
++#else /* !(defined(__HIGHC__) && !defined(FLEXOS)) */
++ setmode(fileno(G.outfile), O_BINARY);
++#endif /* ?(defined(__HIGHC__) && !defined(FLEXOS)) */
++# define NEWLINE "\r\n"
++#else /* !DOS_FLX_NLM_OS2_W32 */
++# define NEWLINE "\n"
++#endif /* ?DOS_FLX_NLM_OS2_W32 */
++#ifdef VMS
++ /* VMS: required even for stdout! */
++ if ((r = open_outfile(__G)) != 0)
++ switch (r) {
++ case OPENOUT_SKIPOK:
++ return PK_OK;
++ case OPENOUT_SKIPWARN:
++ return PK_WARN;
++ default:
++ return PK_DISK;
++ }
++ } else if ((r = open_outfile(__G)) != 0)
++ switch (r) {
++ case OPENOUT_SKIPOK:
++ return PK_OK;
++ case OPENOUT_SKIPWARN:
++ return PK_WARN;
++ default:
++ return PK_DISK;
++ }
++#else /* !VMS */
++ } else if (open_outfile(__G))
++ return PK_DISK;
++#endif /* ?VMS */
++ }
++
++/*---------------------------------------------------------------------------
++ Unpack the file.
++ ---------------------------------------------------------------------------*/
++
++ defer_leftover_input(__G); /* so NEXTBYTE bounds check will work */
++ switch (G.lrec.compression_method) {
++ case STORED:
++ if (!uO.tflag && QCOND2) {
++#ifdef SYMLINKS
++ if (G.symlnk) /* can also be deflated, but rarer... */
++ Info(slide, 0, ((char *)slide, LoadFarString(ExtractMsg),
++ "link", FnFilter1(G.filename), "", ""));
++ else
++#endif /* SYMLINKS */
++ Info(slide, 0, ((char *)slide, LoadFarString(ExtractMsg),
++ "extract", FnFilter1(G.filename),
++ (uO.aflag != 1 /* && G.pInfo->textfile==G.pInfo->textmode */)?
++ "" : (G.lrec.ucsize == 0L? nul : (G.pInfo->textfile? txt :
++ bin)), uO.cflag? NEWLINE : ""));
++ }
++#if (defined(DLL) && !defined(NO_SLIDE_REDIR))
++ if (G.redirect_slide) {
++ wsize = G.redirect_size; redirSlide = G.redirect_buffer;
++ } else {
++ wsize = WSIZE; redirSlide = slide;
++ }
++#endif
++ G.outptr = redirSlide;
++ G.outcnt = 0L;
++ while ((b = NEXTBYTE) != EOF) {
++ *G.outptr++ = (uch)b;
++ if (++G.outcnt == wsize) {
++ error = flush(__G__ redirSlide, G.outcnt, 0);
++ G.outptr = redirSlide;
++ G.outcnt = 0L;
++ if (error != PK_COOL || G.disk_full) break;
++ }
++ }
++ if (G.outcnt) { /* flush final (partial) buffer */
++ r = flush(__G__ redirSlide, G.outcnt, 0);
++ if (error < r) error = r;
++ }
++ break;
++
++#ifndef SFX
++#ifndef LZW_CLEAN
++ case SHRUNK:
++ if (!uO.tflag && QCOND2) {
++ Info(slide, 0, ((char *)slide, LoadFarString(ExtractMsg),
++ LoadFarStringSmall(Unshrink), FnFilter1(G.filename),
++ (uO.aflag != 1 /* && G.pInfo->textfile==G.pInfo->textmode */)?
++ "" : (G.pInfo->textfile? txt : bin), uO.cflag? NEWLINE : ""));
++ }
++ if ((r = unshrink(__G)) != PK_COOL) {
++ if (r < PK_DISK) {
++ if ((uO.tflag && uO.qflag) || (!uO.tflag && !QCOND2))
++ Info(slide, 0x401, ((char *)slide,
++ LoadFarStringSmall(ErrUnzipFile), r == PK_MEM3 ?
++ LoadFarString(NotEnoughMem) :
++ LoadFarString(InvalidComprData),
++ LoadFarStringSmall2(Unshrink),
++ FnFilter1(G.filename)));
++ else
++ Info(slide, 0x401, ((char *)slide,
++ LoadFarStringSmall(ErrUnzipNoFile), r == PK_MEM3 ?
++ LoadFarString(NotEnoughMem) :
++ LoadFarString(InvalidComprData),
++ LoadFarStringSmall2(Unshrink)));
++ }
++ error = r;
++ }
++ break;
++#endif /* !LZW_CLEAN */
++
++#ifndef COPYRIGHT_CLEAN
++ case REDUCED1:
++ case REDUCED2:
++ case REDUCED3:
++ case REDUCED4:
++ if (!uO.tflag && QCOND2) {
++ Info(slide, 0, ((char *)slide, LoadFarString(ExtractMsg),
++ "unreduc", FnFilter1(G.filename),
++ (uO.aflag != 1 /* && G.pInfo->textfile==G.pInfo->textmode */)?
++ "" : (G.pInfo->textfile? txt : bin), uO.cflag? NEWLINE : ""));
++ }
++ if ((r = unreduce(__G)) != PK_COOL) {
++ /* unreduce() returns only PK_COOL, PK_DISK, or IZ_CTRLC */
++ error = r;
++ }
++ break;
++#endif /* !COPYRIGHT_CLEAN */
++
++ case IMPLODED:
++ if (!uO.tflag && QCOND2) {
++ Info(slide, 0, ((char *)slide, LoadFarString(ExtractMsg),
++ "explod", FnFilter1(G.filename),
++ (uO.aflag != 1 /* && G.pInfo->textfile==G.pInfo->textmode */)?
++ "" : (G.pInfo->textfile? txt : bin), uO.cflag? NEWLINE : ""));
++ }
++ if ((r = explode(__G)) != 0) {
++ if (r == 5) { /* treat 5 specially */
++ int warning = ((zusz_t)G.used_csize <= G.lrec.csize);
++
++ if ((uO.tflag && uO.qflag) || (!uO.tflag && !QCOND2))
++ Info(slide, 0x401, ((char *)slide,
++ LoadFarString(LengthMsg),
++ "", warning ? "warning" : "error",
++ FmZofft(G.used_csize, NULL, NULL),
++ FmZofft(G.lrec.ucsize, NULL, "u"),
++ warning ? " " : "",
++ FmZofft(G.lrec.csize, NULL, "u"),
++ " [", FnFilter1(G.filename), "]"));
++ else
++ Info(slide, 0x401, ((char *)slide,
++ LoadFarString(LengthMsg),
++ "\n", warning ? "warning" : "error",
++ FmZofft(G.used_csize, NULL, NULL),
++ FmZofft(G.lrec.ucsize, NULL, "u"),
++ warning ? " " : "",
++ FmZofft(G.lrec.csize, NULL, "u"),
++ "", "", "."));
++ error = warning ? PK_WARN : PK_ERR;
++ } else if (r < PK_DISK) {
++ if ((uO.tflag && uO.qflag) || (!uO.tflag && !QCOND2))
++ Info(slide, 0x401, ((char *)slide,
++ LoadFarStringSmall(ErrUnzipFile), r == 3?
++ LoadFarString(NotEnoughMem) :
++ LoadFarString(InvalidComprData),
++ LoadFarStringSmall2(Explode),
++ FnFilter1(G.filename)));
++ else
++ Info(slide, 0x401, ((char *)slide,
++ LoadFarStringSmall(ErrUnzipNoFile), r == 3?
++ LoadFarString(NotEnoughMem) :
++ LoadFarString(InvalidComprData),
++ LoadFarStringSmall2(Explode)));
++ error = ((r == 3) ? PK_MEM3 : PK_ERR);
++ } else {
++ error = r;
++ }
++ }
++ break;
++#endif /* !SFX */
++
++ case DEFLATED:
++#ifdef USE_DEFLATE64
++ case ENHDEFLATED:
++#endif
++ if (!uO.tflag && QCOND2) {
++ Info(slide, 0, ((char *)slide, LoadFarString(ExtractMsg),
++ "inflat", FnFilter1(G.filename),
++ (uO.aflag != 1 /* && G.pInfo->textfile==G.pInfo->textmode */)?
++ "" : (G.pInfo->textfile? txt : bin), uO.cflag? NEWLINE : ""));
++ }
++#ifndef USE_ZLIB /* zlib's function is called inflate(), too */
++# define UZinflate inflate
++#endif
++ if ((r = UZinflate(__G__
++ (G.lrec.compression_method == ENHDEFLATED)))
++ != 0) {
++ if (r < PK_DISK) {
++ if ((uO.tflag && uO.qflag) || (!uO.tflag && !QCOND2))
++ Info(slide, 0x401, ((char *)slide,
++ LoadFarStringSmall(ErrUnzipFile), r == 3?
++ LoadFarString(NotEnoughMem) :
++ LoadFarString(InvalidComprData),
++ LoadFarStringSmall2(Inflate),
++ FnFilter1(G.filename)));
++ else
++ Info(slide, 0x401, ((char *)slide,
++ LoadFarStringSmall(ErrUnzipNoFile), r == 3?
++ LoadFarString(NotEnoughMem) :
++ LoadFarString(InvalidComprData),
++ LoadFarStringSmall2(Inflate)));
++ error = ((r == 3) ? PK_MEM3 : PK_ERR);
++ } else {
++ error = r;
++ }
++ }
++ break;
++
++#ifdef USE_BZIP2
++ case BZIPPED:
++ if (!uO.tflag && QCOND2) {
++ Info(slide, 0, ((char *)slide, LoadFarString(ExtractMsg),
++ "bunzipp", FnFilter1(G.filename),
++ (uO.aflag != 1 /* && G.pInfo->textfile==G.pInfo->textmode */)?
++ "" : (G.pInfo->textfile? txt : bin), uO.cflag? NEWLINE : ""));
++ }
++ if ((r = UZbunzip2(__G)) != 0) {
++ if (r < PK_DISK) {
++ if ((uO.tflag && uO.qflag) || (!uO.tflag && !QCOND2))
++ Info(slide, 0x401, ((char *)slide,
++ LoadFarStringSmall(ErrUnzipFile), r == 3?
++ LoadFarString(NotEnoughMem) :
++ LoadFarString(InvalidComprData),
++ LoadFarStringSmall2(BUnzip),
++ FnFilter1(G.filename)));
++ else
++ Info(slide, 0x401, ((char *)slide,
++ LoadFarStringSmall(ErrUnzipNoFile), r == 3?
++ LoadFarString(NotEnoughMem) :
++ LoadFarString(InvalidComprData),
++ LoadFarStringSmall2(BUnzip)));
++ error = ((r == 3) ? PK_MEM3 : PK_ERR);
++ } else {
++ error = r;
++ }
++ }
++ break;
++#endif /* USE_BZIP2 */
++
++ default: /* should never get to this point */
++ Info(slide, 0x401, ((char *)slide,
++ LoadFarString(FileUnknownCompMethod), FnFilter1(G.filename)));
++ /* close and delete file before return? */
++ undefer_input(__G);
++ return PK_WARN;
++
++ } /* end switch (compression method) */
++
++/*---------------------------------------------------------------------------
++ Close the file and set its date and time (not necessarily in that order),
++ and make sure the CRC checked out OK. Logical-AND the CRC for 64-bit
++ machines (redundant on 32-bit machines).
++ ---------------------------------------------------------------------------*/
++
++#ifdef VMS /* VMS: required even for stdout! (final flush) */
++ if (!uO.tflag) /* don't close NULL file */
++ error = close_outfile(__G);
++#else
++#ifdef DLL
++ if (!uO.tflag && (!uO.cflag || G.redirect_data)) {
++ if (G.redirect_data)
++ FINISH_REDIRECT();
++ else
++ error = close_outfile(__G);
++ }
++#else
++ if (!uO.tflag && !uO.cflag) /* don't close NULL file or stdout */
++ error = close_outfile(__G);
++#endif
++#endif /* VMS */
++
++ if (G.disk_full) { /* set by flush() */
++ if (G.disk_full > 1) {
++#if (defined(DELETE_IF_FULL) && defined(HAVE_UNLINK))
++ /* delete the incomplete file if we can */
++ if (unlink(G.filename) != 0)
++ Trace((stderr, "extract.c: could not delete %s\n",
++ FnFilter1(G.filename)));
++#else
++ /* warn user about the incomplete file */
++ Info(slide, 0x421, ((char *)slide, LoadFarString(FileTruncated),
++ FnFilter1(G.filename)));
++#endif
++ error = PK_DISK;
++ } else {
++ error = PK_WARN;
++ }
++ }
++
++ if (error > PK_WARN) {/* don't print redundant CRC error if error already */
++ undefer_input(__G);
++ return error;
++ }
++ if (G.crc32val != G.lrec.crc32) {
++ /* if quiet enough, we haven't output the filename yet: do it */
++ if ((uO.tflag && uO.qflag) || (!uO.tflag && !QCOND2))
++ Info(slide, 0x401, ((char *)slide, "%-22s ",
++ FnFilter1(G.filename)));
++ Info(slide, 0x401, ((char *)slide, LoadFarString(BadCRC), G.crc32val,
++ G.lrec.crc32));
++#if CRYPT
++ if (G.pInfo->encrypted)
++ Info(slide, 0x401, ((char *)slide, LoadFarString(MaybeBadPasswd)));
++#endif
++ error = PK_ERR;
++ } else if (uO.tflag) {
++#ifndef SFX
++ if (G.extra_field) {
++ if ((r = TestExtraField(__G__ G.extra_field,
++ G.lrec.extra_field_length)) > error)
++ error = r;
++ } else
++#endif /* !SFX */
++ if (!uO.qflag)
++ Info(slide, 0, ((char *)slide, " OK\n"));
++ } else {
++ if (QCOND2 && !error) /* GRR: is stdout reset to text mode yet? */
++ Info(slide, 0, ((char *)slide, "\n"));
++ }
++
++ undefer_input(__G);
++ return error;
++
++} /* end function extract_or_test_member() */
++
++
++
++
++
++#ifndef SFX
++
++/*******************************/
++/* Function TestExtraField() */
++/*******************************/
++
++static int TestExtraField(__G__ ef, ef_len)
++ __GDEF
++ uch *ef;
++ unsigned ef_len;
++{
++ ush ebID;
++ unsigned ebLen;
++ unsigned eb_cmpr_offs = 0;
++ int r;
++
++ /* we know the regular compressed file data tested out OK, or else we
++ * wouldn't be here ==> print filename if any extra-field errors found
++ */
++ while (ef_len >= EB_HEADSIZE) {
++ ebID = makeword(ef);
++ ebLen = (unsigned)makeword(ef+EB_LEN);
++
++ if (ebLen > (ef_len - EB_HEADSIZE))
++ {
++ /* Discovered some extra field inconsistency! */
++ if (uO.qflag)
++ Info(slide, 1, ((char *)slide, "%-22s ",
++ FnFilter1(G.filename)));
++ Info(slide, 1, ((char *)slide, LoadFarString(InconsistEFlength),
++ ebLen, (ef_len - EB_HEADSIZE)));
++ return PK_ERR;
++ }
++
++ switch (ebID) {
++ case EF_OS2:
++ case EF_ACL:
++ case EF_MAC3:
++ case EF_BEOS:
++ case EF_ATHEOS:
++ switch (ebID) {
++ case EF_OS2:
++ case EF_ACL:
++ eb_cmpr_offs = EB_OS2_HLEN;
++ break;
++ case EF_MAC3:
++ if (ebLen >= EB_MAC3_HLEN &&
++ (makeword(ef+(EB_HEADSIZE+EB_FLGS_OFFS))
++ & EB_M3_FL_UNCMPR) &&
++ (makelong(ef+EB_HEADSIZE) == ebLen - EB_MAC3_HLEN))
++ eb_cmpr_offs = 0;
++ else
++ eb_cmpr_offs = EB_MAC3_HLEN;
++ break;
++ case EF_BEOS:
++ case EF_ATHEOS:
++ if (ebLen >= EB_BEOS_HLEN &&
++ (*(ef+(EB_HEADSIZE+EB_FLGS_OFFS)) & EB_BE_FL_UNCMPR) &&
++ (makelong(ef+EB_HEADSIZE) == ebLen - EB_BEOS_HLEN))
++ eb_cmpr_offs = 0;
++ else
++ eb_cmpr_offs = EB_BEOS_HLEN;
++ break;
++ }
++ if ((r = test_compr_eb(__G__ ef, ebLen, eb_cmpr_offs, NULL))
++ != PK_OK) {
++ if (uO.qflag)
++ Info(slide, 1, ((char *)slide, "%-22s ",
++ FnFilter1(G.filename)));
++ switch (r) {
++ case IZ_EF_TRUNC:
++ Info(slide, 1, ((char *)slide,
++ LoadFarString(TruncEAs),
++ ebLen-(eb_cmpr_offs+EB_CMPRHEADLEN), "\n"));
++ break;
++ case PK_ERR:
++ Info(slide, 1, ((char *)slide,
++ LoadFarString(InvalidComprDataEAs)));
++ break;
++ case PK_MEM3:
++ case PK_MEM4:
++ Info(slide, 1, ((char *)slide,
++ LoadFarString(NotEnoughMemEAs)));
++ break;
++ default:
++ if ((r & 0xff) != PK_ERR)
++ Info(slide, 1, ((char *)slide,
++ LoadFarString(UnknErrorEAs)));
++ else {
++ ush m = (ush)(r >> 8);
++ if (m == DEFLATED) /* GRR KLUDGE! */
++ Info(slide, 1, ((char *)slide,
++ LoadFarString(BadCRC_EAs)));
++ else
++ Info(slide, 1, ((char *)slide,
++ LoadFarString(UnknComprMethodEAs), m));
++ }
++ break;
++ }
++ return r;
++ }
++ break;
++
++ case EF_NTSD:
++ Trace((stderr, "ebID: %i / ebLen: %u\n", ebID, ebLen));
++ r = ebLen < EB_NTSD_L_LEN ? IZ_EF_TRUNC :
++ ((ef[EB_HEADSIZE+EB_NTSD_VERSION] > EB_NTSD_MAX_VER) ?
++ (PK_WARN | 0x4000) :
++ test_compr_eb(__G__ ef, ebLen, EB_NTSD_L_LEN, TEST_NTSD));
++ if (r != PK_OK) {
++ if (uO.qflag)
++ Info(slide, 1, ((char *)slide, "%-22s ",
++ FnFilter1(G.filename)));
++ switch (r) {
++ case IZ_EF_TRUNC:
++ Info(slide, 1, ((char *)slide,
++ LoadFarString(TruncNTSD),
++ ebLen-(EB_NTSD_L_LEN+EB_CMPRHEADLEN), "\n"));
++ break;
++#if (defined(WIN32) && defined(NTSD_EAS))
++ case PK_WARN:
++ Info(slide, 1, ((char *)slide,
++ LoadFarString(InvalidSecurityEAs)));
++ break;
++#endif
++ case PK_ERR:
++ Info(slide, 1, ((char *)slide,
++ LoadFarString(InvalidComprDataEAs)));
++ break;
++ case PK_MEM3:
++ case PK_MEM4:
++ Info(slide, 1, ((char *)slide,
++ LoadFarString(NotEnoughMemEAs)));
++ break;
++ case (PK_WARN | 0x4000):
++ Info(slide, 1, ((char *)slide,
++ LoadFarString(UnsuppNTSDVersEAs),
++ (int)ef[EB_HEADSIZE+EB_NTSD_VERSION]));
++ r = PK_WARN;
++ break;
++ default:
++ if ((r & 0xff) != PK_ERR)
++ Info(slide, 1, ((char *)slide,
++ LoadFarString(UnknErrorEAs)));
++ else {
++ ush m = (ush)(r >> 8);
++ if (m == DEFLATED) /* GRR KLUDGE! */
++ Info(slide, 1, ((char *)slide,
++ LoadFarString(BadCRC_EAs)));
++ else
++ Info(slide, 1, ((char *)slide,
++ LoadFarString(UnknComprMethodEAs), m));
++ }
++ break;
++ }
++ return r;
++ }
++ break;
++ case EF_PKVMS:
++ /* 2015-01-30 SMS. Added sufficient-bytes test/message
++ * here. (Removed defective ebLen test above.)
++ *
++ * If sufficient bytes (EB_PKVMS_MINLEN) are available,
++ * then compare the stored CRC value with the calculated
++ * CRC for the remainder of the data (and complain about
++ * a mismatch).
++ */
++ if (ebLen < EB_PKVMS_MINLEN)
++ {
++ /* Insufficient bytes available. */
++ Info( slide, 1,
++ ((char *)slide, LoadFarString( TooSmallEBlength),
++ ebLen, EB_PKVMS_MINLEN));
++ }
++ else if (makelong(ef+ EB_HEADSIZE) !=
++ crc32(CRCVAL_INITIAL,
++ (ef+ EB_HEADSIZE+ EB_PKVMS_MINLEN),
++ (extent)(ebLen- EB_PKVMS_MINLEN)))
++ {
++ Info(slide, 1, ((char *)slide,
++ LoadFarString(BadCRC_EAs)));
++ }
++ break;
++ case EF_PKW32:
++ case EF_PKUNIX:
++ case EF_ASIUNIX:
++ case EF_IZVMS:
++ case EF_IZUNIX:
++ case EF_VMCMS:
++ case EF_MVS:
++ case EF_SPARK:
++ case EF_TANDEM:
++ case EF_THEOS:
++ case EF_AV:
++ default:
++ break;
++ }
++ ef_len -= (ebLen + EB_HEADSIZE);
++ ef += (ebLen + EB_HEADSIZE);
++ }
++
++ if (!uO.qflag)
++ Info(slide, 0, ((char *)slide, " OK\n"));
++
++ return PK_COOL;
++
++} /* end function TestExtraField() */
++
++
++
++
++
++/******************************/
++/* Function test_compr_eb() */
++/******************************/
++
++#ifdef PROTO
++static int test_compr_eb(
++ __GPRO__
++ uch *eb,
++ unsigned eb_size,
++ unsigned compr_offset,
++ int (*test_uc_ebdata)(__GPRO__ uch *eb, unsigned eb_size,
++ uch *eb_ucptr, ulg eb_ucsize))
++#else /* !PROTO */
++static int test_compr_eb(__G__ eb, eb_size, compr_offset, test_uc_ebdata)
++ __GDEF
++ uch *eb;
++ unsigned eb_size;
++ unsigned compr_offset;
++ int (*test_uc_ebdata)();
++#endif /* ?PROTO */
++{
++ ulg eb_ucsize;
++ uch *eb_ucptr;
++ int r;
++ ush method;
++
++ if (compr_offset < 4) /* field is not compressed: */
++ return PK_OK; /* do nothing and signal OK */
++
++ /* Return no/bad-data error status if any problem is found:
++ * 1. eb_size is too small to hold the uncompressed size
++ * (eb_ucsize). (Else extract eb_ucsize.)
++ * 2. eb_ucsize is zero (invalid). 2014-12-04 SMS.
++ * 3. eb_ucsize is positive, but eb_size is too small to hold
++ * the compressed data header.
++ */
++ if ((eb_size < (EB_UCSIZE_P + 4)) ||
++ ((eb_ucsize = makelong( eb+ (EB_HEADSIZE+ EB_UCSIZE_P))) == 0L) ||
++ ((eb_ucsize > 0L) && (eb_size <= (compr_offset + EB_CMPRHEADLEN))))
++ return IZ_EF_TRUNC; /* no/bad compressed data! */
++
++ method = makeword(eb + (EB_HEADSIZE + compr_offset));
++ if ((method == STORED) && (eb_size != compr_offset + EB_CMPRHEADLEN + eb_ucsize))
++ return PK_ERR; /* compressed & uncompressed
++ * should match in STORED
++ * method */
++
++ if (
++#ifdef INT_16BIT
++ (((ulg)(extent)eb_ucsize) != eb_ucsize) ||
++#endif
++ (eb_ucptr = (uch *)malloc((extent)eb_ucsize)) == (uch *)NULL)
++ return PK_MEM4;
++
++ r = memextract(__G__ eb_ucptr, eb_ucsize,
++ eb + (EB_HEADSIZE + compr_offset),
++ (ulg)(eb_size - compr_offset));
++
++ if (r == PK_OK && test_uc_ebdata != NULL)
++ r = (*test_uc_ebdata)(__G__ eb, eb_size, eb_ucptr, eb_ucsize);
++
++ free(eb_ucptr);
++ return r;
++
++} /* end function test_compr_eb() */
++
++#endif /* !SFX */
++
++
++
++
++
++/***************************/
++/* Function memextract() */
++/***************************/
++
++int memextract(__G__ tgt, tgtsize, src, srcsize) /* extract compressed */
++ __GDEF /* extra field block; */
++ uch *tgt; /* return PK-type error */
++ ulg tgtsize; /* level */
++ ZCONST uch *src;
++ ulg srcsize;
++{
++ zoff_t old_csize=G.csize;
++ uch *old_inptr=G.inptr;
++ int old_incnt=G.incnt;
++ int r, error=PK_OK;
++ ush method;
++ ulg extra_field_crc;
++
++
++ method = makeword(src);
++ extra_field_crc = makelong(src+2);
++
++ /* compressed extra field exists completely in memory at this location: */
++ G.inptr = (uch *)src + (2 + 4); /* method and extra_field_crc */
++ G.incnt = (int)(G.csize = (long)(srcsize - (2 + 4)));
++ G.mem_mode = TRUE;
++ G.outbufptr = tgt;
++ G.outsize = tgtsize;
++
++ switch (method) {
++ case STORED:
++ memcpy((char *)tgt, (char *)G.inptr, (extent)G.incnt);
++ G.outcnt = (ulg)G.csize; /* for CRC calculation */
++ break;
++ case DEFLATED:
++#ifdef USE_DEFLATE64
++ case ENHDEFLATED:
++#endif
++ G.outcnt = 0L;
++ if ((r = UZinflate(__G__ (method == ENHDEFLATED))) != 0) {
++ if (!uO.tflag)
++ Info(slide, 0x401, ((char *)slide,
++ LoadFarStringSmall(ErrUnzipNoFile), r == 3?
++ LoadFarString(NotEnoughMem) :
++ LoadFarString(InvalidComprData),
++ LoadFarStringSmall2(Inflate)));
++ error = (r == 3)? PK_MEM3 : PK_ERR;
++ }
++ if (G.outcnt == 0L) /* inflate's final FLUSH sets outcnt */
++ break;
++ break;
++ default:
++ if (uO.tflag)
++ error = PK_ERR | ((int)method << 8);
++ else {
++ Info(slide, 0x401, ((char *)slide,
++ LoadFarString(UnsupportedExtraField), method));
++ error = PK_ERR; /* GRR: should be passed on up via SetEAs() */
++ }
++ break;
++ }
++
++ G.inptr = old_inptr;
++ G.incnt = old_incnt;
++ G.csize = old_csize;
++ G.mem_mode = FALSE;
++
++ if (!error) {
++ register ulg crcval = crc32(CRCVAL_INITIAL, tgt, (extent)G.outcnt);
++
++ if (crcval != extra_field_crc) {
++ if (uO.tflag)
++ error = PK_ERR | (DEFLATED << 8); /* kludge for now */
++ else {
++ Info(slide, 0x401, ((char *)slide,
++ LoadFarString(BadExtraFieldCRC), G.zipfn, crcval,
++ extra_field_crc));
++ error = PK_ERR;
++ }
++ }
++ }
++ return error;
++
++} /* end function memextract() */
++
++
++
++
++
++/*************************/
++/* Function memflush() */
++/*************************/
++
++int memflush(__G__ rawbuf, size)
++ __GDEF
++ ZCONST uch *rawbuf;
++ ulg size;
++{
++ if (size > G.outsize)
++ /* Here, PK_DISK is a bit off-topic, but in the sense of marking
++ "overflow of output space", its use may be tolerated. */
++ return PK_DISK; /* more data than output buffer can hold */
++
++
++
++ memcpy((char *)G.outbufptr, (char *)rawbuf, (extent)size);
++ G.outbufptr += (unsigned int)size;
++ G.outsize -= size;
++ G.outcnt += size;
++
++ return 0;
++
++} /* end function memflush() */
++
++
++
++
++
++#if (defined(VMS) || defined(VMS_TEXT_CONV))
++
++/************************************/
++/* Function extract_izvms_block() */
++/************************************/
++
++/*
++ * Extracts block from p. If resulting length is less than needed, fill
++ * extra space with corresponding bytes from 'init'.
++ * Currently understands 3 formats of block compression:
++ * - Simple storing
++ * - Compression of zero bytes to zero bits
++ * - Deflation (see memextract())
++ * The IZVMS block data is returned in malloc'd space.
++ */
++uch *extract_izvms_block(__G__ ebdata, size, retlen, init, needlen)
++ __GDEF
++ ZCONST uch *ebdata;
++ unsigned size;
++ unsigned *retlen;
++ ZCONST uch *init;
++ unsigned needlen;
++{
++ uch *ucdata; /* Pointer to block allocated */
++ int cmptype;
++ unsigned usiz, csiz;
++
++ cmptype = (makeword(ebdata+EB_IZVMS_FLGS) & EB_IZVMS_BCMASK);
++ csiz = size - EB_IZVMS_HLEN;
++ usiz = (cmptype == EB_IZVMS_BCSTOR ?
++ csiz : makeword(ebdata+EB_IZVMS_UCSIZ));
++
++ if (retlen)
++ *retlen = usiz;
++
++ if ((ucdata = (uch *)malloc(MAX(needlen, usiz))) == NULL)
++ return NULL;
++
++ if (init && (usiz < needlen))
++ memcpy((char *)ucdata, (ZCONST char *)init, needlen);
++
++ switch (cmptype)
++ {
++ case EB_IZVMS_BCSTOR: /* The simplest case */
++ memcpy(ucdata, ebdata+EB_IZVMS_HLEN, usiz);
++ break;
++ case EB_IZVMS_BC00:
++ decompress_bits(ucdata, usiz, ebdata+EB_IZVMS_HLEN);
++ break;
++ case EB_IZVMS_BCDEFL:
++ memextract(__G__ ucdata, (ulg)usiz,
++ ebdata+EB_IZVMS_HLEN, (ulg)csiz);
++ break;
++ default:
++ free(ucdata);
++ ucdata = NULL;
++ }
++ return ucdata;
++
++} /* end of extract_izvms_block */
++
++
++
++
++
++/********************************/
++/* Function decompress_bits() */
++/********************************/
++/*
++ * Simple uncompression routine. The compression uses bit stream.
++ * Compression scheme:
++ *
++ * if (byte!=0)
++ * putbit(1),putbyte(byte)
++ * else
++ * putbit(0)
++ */
++static void decompress_bits(outptr, needlen, bitptr)
++ uch *outptr; /* Pointer into output block */
++ unsigned needlen; /* Size of uncompressed block */
++ ZCONST uch *bitptr; /* Pointer into compressed data */
++{
++ ulg bitbuf = 0;
++ int bitcnt = 0;
++
++#define _FILL { bitbuf |= (*bitptr++) << bitcnt;\
++ bitcnt += 8; \
++ }
++
++ while (needlen--)
++ {
++ if (bitcnt <= 0)
++ _FILL;
++
++ if (bitbuf & 1)
++ {
++ bitbuf >>= 1;
++ if ((bitcnt -= 1) < 8)
++ _FILL;
++ *outptr++ = (uch)bitbuf;
++ bitcnt -= 8;
++ bitbuf >>= 8;
++ }
++ else
++ {
++ *outptr++ = '\0';
++ bitcnt -= 1;
++ bitbuf >>= 1;
++ }
++ }
++} /* end function decompress_bits() */
++
++#endif /* VMS || VMS_TEXT_CONV */
++
++
++
++
++
++#ifdef SYMLINKS
++/***********************************/
++/* Function set_deferred_symlink() */
++/***********************************/
++
++static void set_deferred_symlink(__G__ slnk_entry)
++ __GDEF
++ slinkentry *slnk_entry;
++{
++ extent ucsize = slnk_entry->targetlen;
++ char *linkfname = slnk_entry->fname;
++ char *linktarget = (char *)malloc(ucsize+1);
++
++ if (!linktarget) {
++ Info(slide, 0x201, ((char *)slide,
++ LoadFarString(SymLnkWarnNoMem), FnFilter1(linkfname)));
++ return;
++ }
++ linktarget[ucsize] = '\0';
++ G.outfile = zfopen(linkfname, FOPR); /* open link placeholder for reading */
++ /* Check that the following conditions are all fulfilled:
++ * a) the placeholder file exists,
++ * b) the placeholder file contains exactly "ucsize" bytes
++ * (read the expected placeholder content length + 1 extra byte, this
++ * should return the expected content length),
++ * c) the placeholder content matches the link target specification as
++ * stored in the symlink control structure.
++ */
++ if (!G.outfile ||
++ fread(linktarget, 1, ucsize+1, G.outfile) != ucsize ||
++ strcmp(slnk_entry->target, linktarget))
++ {
++ Info(slide, 0x201, ((char *)slide,
++ LoadFarString(SymLnkWarnInvalid), FnFilter1(linkfname)));
++ free(linktarget);
++ if (G.outfile)
++ fclose(G.outfile);
++ return;
++ }
++ fclose(G.outfile); /* close "data" file for good... */
++ unlink(linkfname); /* ...and delete it */
++ if (QCOND2)
++ Info(slide, 0, ((char *)slide, LoadFarString(SymLnkFinish),
++ FnFilter1(linkfname), FnFilter2(linktarget)));
++ if (symlink(linktarget, linkfname)) /* create the real link */
++ perror("symlink error");
++ free(linktarget);
++#ifdef SET_SYMLINK_ATTRIBS
++ set_symlnk_attribs(__G__ slnk_entry);
++#endif
++ return; /* can't set time on symlinks */
++
++} /* end function set_deferred_symlink() */
++#endif /* SYMLINKS */
++
++
++
++
++/*************************/
++/* Function fnfilter() */ /* here instead of in list.c for SFX */
++/*************************/
++
++char *fnfilter(raw, space, size) /* convert name to safely printable form */
++ ZCONST char *raw;
++ uch *space;
++ extent size;
++{
++#ifndef NATIVE /* ASCII: filter ANSI escape codes, etc. */
++ ZCONST uch *r=(ZCONST uch *)raw;
++ uch *s=space;
++ uch *slim=NULL;
++ uch *se=NULL;
++ int have_overflow = FALSE;
++
++ if (size > 0) {
++ slim = space + size
++#ifdef _MBCS
++ - (MB_CUR_MAX - 1)
++#endif
++ - 4;
++ }
++ while (*r) {
++ if (size > 0 && s >= slim && se == NULL) {
++ se = s;
++ }
++#ifdef QDOS
++ if (qlflag & 2) {
++ if (*r == '/' || *r == '.') {
++ if (se != NULL && (s > (space + (size-3)))) {
++ have_overflow = TRUE;
++ break;
++ }
++ ++r;
++ *s++ = '_';
++ continue;
++ }
++ } else
++#endif
++#ifdef HAVE_WORKING_ISPRINT
++# ifndef UZ_FNFILTER_REPLACECHAR
++ /* A convenient choice for the replacement of unprintable char codes is
++ * the "single char wildcard", as this character is quite unlikely to
++ * appear in filenames by itself. The following default definition
++ * sets the replacement char to a question mark as the most common
++ * "single char wildcard"; this setting should be overridden in the
++ * appropiate system-specific configuration header when needed.
++ */
++# define UZ_FNFILTER_REPLACECHAR '?'
++# endif
++ if (!isprint(*r)) {
++ if (*r < 32) {
++ /* ASCII control codes are escaped as "^{letter}". */
++ if (se != NULL && (s > (space + (size-4)))) {
++ have_overflow = TRUE;
++ break;
++ }
++ *s++ = '^', *s++ = (uch)(64 + *r++);
++ } else {
++ /* Other unprintable codes are replaced by the
++ * placeholder character. */
++ if (se != NULL && (s > (space + (size-3)))) {
++ have_overflow = TRUE;
++ break;
++ }
++ *s++ = UZ_FNFILTER_REPLACECHAR;
++ INCSTR(r);
++ }
++#else /* !HAVE_WORKING_ISPRINT */
++ if (*r < 32) {
++ /* ASCII control codes are escaped as "^{letter}". */
++ if (se != NULL && (s > (space + (size-4)))) {
++ have_overflow = TRUE;
++ break;
++ }
++ *s++ = '^', *s++ = (uch)(64 + *r++);
++#endif /* ?HAVE_WORKING_ISPRINT */
++ } else {
++#ifdef _MBCS
++ unsigned i = CLEN(r);
++ if (se != NULL && (s > (space + (size-i-2)))) {
++ have_overflow = TRUE;
++ break;
++ }
++ for (; i > 0; i--)
++ *s++ = *r++;
++#else
++ if (se != NULL && (s > (space + (size-3)))) {
++ have_overflow = TRUE;
++ break;
++ }
++ *s++ = *r++;
++#endif
++ }
++ }
++ if (have_overflow) {
++ strcpy((char *)se, "...");
++ } else {
++ *s = '\0';
++ }
++
++#ifdef WINDLL
++ INTERN_TO_ISO((char *)space, (char *)space); /* translate to ANSI */
++#else
++#if (defined(WIN32) && !defined(_WIN32_WCE))
++ /* Win9x console always uses OEM character coding, and
++ WinNT console is set to OEM charset by default, too */
++ INTERN_TO_OEM((char *)space, (char *)space);
++#endif /* (WIN32 && !_WIN32_WCE) */
++#endif /* ?WINDLL */
++
++ return (char *)space;
++
++#else /* NATIVE: EBCDIC or whatever */
++ return (char *)raw;
++#endif
++
++} /* end function fnfilter() */
++
++
++
++
++#ifdef SET_DIR_ATTRIB
++/* must sort saved directories so can set perms from bottom up */
++
++/************************/
++/* Function dircomp() */
++/************************/
++
++static int Cdecl dircomp(a, b) /* used by qsort(); swiped from Zip */
++ ZCONST zvoid *a, *b;
++{
++ /* order is significant: this sorts in reverse order (deepest first) */
++ return strcmp((*(direntry **)b)->fn, (*(direntry **)a)->fn);
++ /* return namecmp((*(direntry **)b)->fn, (*(direntry **)a)->fn); */
++}
++
++#endif /* SET_DIR_ATTRIB */
++
++
++#ifdef USE_BZIP2
++
++/**************************/
++/* Function UZbunzip2() */
++/**************************/
++
++int UZbunzip2(__G)
++__GDEF
++/* decompress a bzipped entry using the libbz2 routines */
++{
++ int retval = 0; /* return code: 0 = "no error" */
++ int err=BZ_OK;
++ int repeated_buf_err;
++ bz_stream bstrm;
++
++ if (G.incnt <= 0 && G.csize <= 0L) {
++ /* avoid an infinite loop */
++ Trace((stderr, "UZbunzip2() got empty input\n"));
++ return 2;
++ }
++
++#if (defined(DLL) && !defined(NO_SLIDE_REDIR))
++ if (G.redirect_slide)
++ wsize = G.redirect_size, redirSlide = G.redirect_buffer;
++ else
++ wsize = WSIZE, redirSlide = slide;
++#endif
++
++ bstrm.next_out = (char *)redirSlide;
++ bstrm.avail_out = wsize;
++
++ bstrm.next_in = (char *)G.inptr;
++ bstrm.avail_in = G.incnt;
++
++ {
++ /* local buffer for efficiency */
++ /* $TODO Check for BZIP LIB version? */
++
++ bstrm.bzalloc = NULL;
++ bstrm.bzfree = NULL;
++ bstrm.opaque = NULL;
++
++ Trace((stderr, "initializing bzlib()\n"));
++ err = BZ2_bzDecompressInit(&bstrm, 0, 0);
++
++ if (err == BZ_MEM_ERROR)
++ return 3;
++ else if (err != BZ_OK)
++ Trace((stderr, "oops! (BZ2_bzDecompressInit() err = %d)\n", err));
++ }
++
++#ifdef FUNZIP
++ while (err != BZ_STREAM_END) {
++#else /* !FUNZIP */
++ while (G.csize > 0) {
++ Trace((stderr, "first loop: G.csize = %ld\n", G.csize));
++#endif /* ?FUNZIP */
++ while (bstrm.avail_out > 0) {
++ err = BZ2_bzDecompress(&bstrm);
++
++ if (err == BZ_DATA_ERROR) {
++ retval = 2; goto uzbunzip_cleanup_exit;
++ } else if (err == BZ_MEM_ERROR) {
++ retval = 3; goto uzbunzip_cleanup_exit;
++ } else if (err != BZ_OK && err != BZ_STREAM_END)
++ Trace((stderr, "oops! (bzip(first loop) err = %d)\n", err));
++
++#ifdef FUNZIP
++ if (err == BZ_STREAM_END) /* "END-of-entry-condition" ? */
++#else /* !FUNZIP */
++ if (G.csize <= 0L) /* "END-of-entry-condition" ? */
++#endif /* ?FUNZIP */
++ break;
++
++ if (bstrm.avail_in == 0) {
++ if (fillinbuf(__G) == 0) {
++ /* no "END-condition" yet, but no more data */
++ retval = 2; goto uzbunzip_cleanup_exit;
++ }
++
++ bstrm.next_in = (char *)G.inptr;
++ bstrm.avail_in = G.incnt;
++ }
++ Trace((stderr, " avail_in = %u\n", bstrm.avail_in));
++ }
++ /* flush slide[] */
++ if ((retval = FLUSH(wsize - bstrm.avail_out)) != 0)
++ goto uzbunzip_cleanup_exit;
++ Trace((stderr, "inside loop: flushing %ld bytes (ptr diff = %ld)\n",
++ (long)(wsize - bstrm.avail_out),
++ (long)(bstrm.next_out-(char *)redirSlide)));
++ bstrm.next_out = (char *)redirSlide;
++ bstrm.avail_out = wsize;
++ }
++
++ /* no more input, so loop until we have all output */
++ Trace((stderr, "beginning final loop: err = %d\n", err));
++ repeated_buf_err = FALSE;
++ while (err != BZ_STREAM_END) {
++ err = BZ2_bzDecompress(&bstrm);
++ if (err == BZ_DATA_ERROR) {
++ retval = 2; goto uzbunzip_cleanup_exit;
++ } else if (err == BZ_MEM_ERROR) {
++ retval = 3; goto uzbunzip_cleanup_exit;
++ } else if (err != BZ_OK && err != BZ_STREAM_END) {
++ Trace((stderr, "oops! (bzip(final loop) err = %d)\n", err));
++ DESTROYGLOBALS();
++ EXIT(PK_MEM3);
++ }
++ /* final flush of slide[] */
++ if ((retval = FLUSH(wsize - bstrm.avail_out)) != 0)
++ goto uzbunzip_cleanup_exit;
++ Trace((stderr, "final loop: flushing %ld bytes (ptr diff = %ld)\n",
++ (long)(wsize - bstrm.avail_out),
++ (long)(bstrm.next_out-(char *)redirSlide)));
++ bstrm.next_out = (char *)redirSlide;
++ bstrm.avail_out = wsize;
++ }
++#ifdef LARGE_FILE_SUPPORT
++ Trace((stderr, "total in = %llu, total out = %llu\n",
++ (zusz_t)(bstrm.total_in_lo32) + ((zusz_t)(bstrm.total_in_hi32))<<32,
++ (zusz_t)(bstrm.total_out_lo32) + ((zusz_t)(bstrm.total_out_hi32))<<32));
++#else
++ Trace((stderr, "total in = %lu, total out = %lu\n", bstrm.total_in_lo32,
++ bstrm.total_out_lo32));
++#endif
++
++ G.inptr = (uch *)bstrm.next_in;
++ G.incnt = (G.inbuf + INBUFSIZ) - G.inptr; /* reset for other routines */
++
++uzbunzip_cleanup_exit:
++ err = BZ2_bzDecompressEnd(&bstrm);
++ if (err != BZ_OK)
++ Trace((stderr, "oops! (BZ2_bzDecompressEnd() err = %d)\n", err));
++
++ return retval;
++} /* end function UZbunzip2() */
++#endif /* USE_BZIP2 */
+diff -Naur a/fileio.c b/fileio.c
+--- a/fileio.c 2009-04-20 01:03:44.000000000 +0100
++++ b/fileio.c 2019-12-02 01:45:40.913845391 +0000
+@@ -1,5 +1,5 @@
+ /*
+- Copyright (c) 1990-2009 Info-ZIP. All rights reserved.
++ Copyright (c) 1990-2017 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.
+@@ -176,6 +176,8 @@
+ #endif
+ static ZCONST char Far ExtraFieldTooLong[] =
+ "warning: extra field too long (%d). Ignoring...\n";
++static ZCONST char Far ExtraFieldCorrupt[] =
++ "warning: extra field (type: 0x%04x) corrupt. Continuing...\n";
+
+ #ifdef WINDLL
+ static ZCONST char Far DiskFullQuery[] =
+@@ -1580,6 +1582,8 @@
+ int r = IZ_PW_ENTERED;
+ char *m;
+ char *prompt;
++ char *ep;
++ char *zp;
+
+ #ifndef REENTRANT
+ /* tell picky compilers to shut up about "unused variable" warnings */
+@@ -1588,9 +1592,12 @@
+
+ if (*rcnt == 0) { /* First call for current entry */
+ *rcnt = 2;
+- if ((prompt = (char *)malloc(2*FILNAMSIZ + 15)) != (char *)NULL) {
+- sprintf(prompt, LoadFarString(PasswPrompt),
+- FnFilter1(zfn), FnFilter2(efn));
++ zp = FnFilter1( zfn);
++ ep = FnFilter2( efn);
++ prompt = (char *)malloc( /* Slightly too long (2* "%s"). */
++ sizeof( PasswPrompt)+ strlen( zp)+ strlen( ep));
++ if (prompt != (char *)NULL) {
++ sprintf(prompt, LoadFarString(PasswPrompt), zp, ep);
+ m = prompt;
+ } else
+ m = (char *)LoadFarString(PasswPrompt2);
+@@ -2006,6 +2013,7 @@
+ unsigned comment_bytes_left;
+ unsigned int block_len;
+ int error=PK_OK;
++ unsigned int length2;
+ #ifdef AMIGA
+ char tmp_fnote[2 * AMIGA_FILENOTELEN]; /* extra room for squozen chars */
+ #endif
+@@ -2292,10 +2300,20 @@
+ seek_zipf(__G__ G.cur_zipfile_bufstart - G.extra_bytes +
+ (G.inptr-G.inbuf) + length);
+ } else {
+- if (readbuf(__G__ (char *)G.extra_field, length) == 0)
++ if ((length2 = readbuf(__G__ (char *)G.extra_field, length)) == 0)
+ return PK_EOF;
++ if(length2 < length) {
++ memset (__G__ (char *)G.extra_field+length2, 0 , length-length2);
++ length = length2;
++ }
+ /* Looks like here is where extra fields are read */
+- getZip64Data(__G__ G.extra_field, length);
++ if (getZip64Data(__G__ G.extra_field, length) != PK_COOL)
++ {
++ Info(slide, 0x401, ((char *)slide,
++ LoadFarString( ExtraFieldCorrupt), EF_PKSZ64));
++ error = PK_WARN;
++ }
++
+ #ifdef UNICODE_SUPPORT
+ G.unipath_filename = NULL;
+ if (G.UzO.U_flag < 2) {
+diff -Naur a/list.c b/list.c
+--- a/list.c 2009-02-08 17:11:34.000000000 +0000
++++ b/list.c 2019-12-02 01:48:07.035331859 +0000
+@@ -97,7 +97,7 @@
+ {
+ int do_this_file=FALSE, cfactor, error, error_in_archive=PK_COOL;
+ #ifndef WINDLL
+- char sgn, cfactorstr[10];
++ char sgn, cfactorstr[1+10+1+1]; /* <sgn><int>%NUL */
+ int longhdr=(uO.vflag>1);
+ #endif
+ int date_format;
+@@ -181,7 +181,7 @@
+ Info(slide, 0x401,
+ ((char *)slide, LoadFarString(CentSigMsg), j));
+ Info(slide, 0x401,
+- ((char *)slide, LoadFarString(ReportMsg)));
++ ((char *)slide,"%s", LoadFarString(ReportMsg)));
+ return PK_BADERR; /* sig not found */
+ }
+ }
+@@ -339,7 +339,19 @@
+ G.crec.compression_method == ENHDEFLATED) {
+ methbuf[5] = dtype[(G.crec.general_purpose_bit_flag>>1) & 3];
+ } else if (methnum >= NUM_METHODS) {
+- sprintf(&methbuf[4], "%03u", G.crec.compression_method);
++ /* 2013-02-26 SMS.
++ * http://sourceforge.net/tracker/?func=detail
++ * &aid=2861648&group_id=118012&atid=679786
++ * Unexpectedly large compression methods overflow
++ * &methbuf[]. Use the old, three-digit decimal format
++ * for values which fit. Otherwise, sacrifice the
++ * colon, and use four-digit hexadecimal.
++ */
++ if (G.crec.compression_method <= 999) {
++ sprintf( &methbuf[ 4], "%03u", G.crec.compression_method);
++ } else {
++ sprintf( &methbuf[ 3], "%04X", G.crec.compression_method);
++ }
+ }
+
+ #if 0 /* GRR/Euro: add this? */
+@@ -378,9 +390,9 @@
+ }
+ #else /* !WINDLL */
+ if (cfactor == 100)
+- sprintf(cfactorstr, LoadFarString(CompFactor100));
++ snprintf(cfactorstr, sizeof(cfactorstr), LoadFarString(CompFactor100));
+ else
+- sprintf(cfactorstr, LoadFarString(CompFactorStr), sgn, cfactor);
++ snprintf(cfactorstr, sizeof(cfactorstr), LoadFarString(CompFactorStr), sgn, cfactor);
+ if (longhdr)
+ Info(slide, 0, ((char *)slide, LoadFarString(LongHdrStats),
+ FmZofft(G.crec.ucsize, "8", "u"), methbuf,
+@@ -460,9 +472,9 @@
+
+ #else /* !WINDLL */
+ if (cfactor == 100)
+- sprintf(cfactorstr, LoadFarString(CompFactor100));
++ snprintf(cfactorstr, sizeof(cfactorstr), LoadFarString(CompFactor100));
+ else
+- sprintf(cfactorstr, LoadFarString(CompFactorStr), sgn, cfactor);
++ snprintf(cfactorstr, sizeof(cfactorstr), LoadFarString(CompFactorStr), sgn, cfactor);
+ if (longhdr) {
+ Info(slide, 0, ((char *)slide, LoadFarString(LongFileTrailer),
+ FmZofft(tot_ucsize, "8", "u"), FmZofft(tot_csize, "8", "u"),
+@@ -507,7 +519,8 @@
+ && (!G.ecrec.is_zip64_archive)
+ && (memcmp(G.sig, end_central_sig, 4) != 0)
+ ) { /* just to make sure again */
+- Info(slide, 0x401, ((char *)slide, LoadFarString(EndSigMsg)));
++ Info(slide, 0x401,
++ ((char *)slide,"%s", LoadFarString(EndSigMsg)));
+ error_in_archive = PK_WARN; /* didn't find sig */
+ }
+
+@@ -591,7 +604,7 @@
+ Info(slide, 0x401,
+ ((char *)slide, LoadFarString(CentSigMsg), j));
+ Info(slide, 0x401,
+- ((char *)slide, LoadFarString(ReportMsg)));
++ ((char *)slide,"%s", LoadFarString(ReportMsg)));
+ return PK_BADERR; /* sig not found */
+ }
+ }
+@@ -674,7 +687,7 @@
+ ---------------------------------------------------------------------------*/
+
+ if (memcmp(G.sig, end_central_sig, 4)) { /* just to make sure again */
+- Info(slide, 0x401, ((char *)slide, LoadFarString(EndSigMsg)));
++ Info(slide, 0x401, ((char *)slide,"%s", LoadFarString(EndSigMsg)));
+ error_in_archive = PK_WARN;
+ }
+ if (*nmember == 0L && error_in_archive <= PK_WARN)
+diff -Naur a/list.c.orig b/list.c.orig
+--- a/list.c.orig 1970-01-01 01:00:00.000000000 +0100
++++ b/list.c.orig 2019-12-02 00:34:56.656315493 +0000
+@@ -0,0 +1,746 @@
++/*
++ 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
++*/
++/*---------------------------------------------------------------------------
++
++ list.c
++
++ This file contains the non-ZipInfo-specific listing routines for UnZip.
++
++ Contains: list_files()
++ get_time_stamp() [optional feature]
++ ratio()
++ fnprint()
++
++ ---------------------------------------------------------------------------*/
++
++
++#define UNZIP_INTERNAL
++#include "unzip.h"
++#ifdef WINDLL
++# ifdef POCKET_UNZIP
++# include "wince/intrface.h"
++# else
++# include "windll/windll.h"
++# endif
++#endif
++
++
++#ifdef TIMESTAMP
++ static int fn_is_dir OF((__GPRO));
++#endif
++
++#ifndef WINDLL
++ static ZCONST char Far CompFactorStr[] = "%c%d%%";
++ static ZCONST char Far CompFactor100[] = "100%%";
++
++#ifdef OS2_EAS
++ static ZCONST char Far HeadersS[] =
++ " Length EAs ACLs Date Time Name";
++ static ZCONST char Far HeadersS1[] =
++ "--------- --- ---- ---------- ----- ----";
++#else
++ static ZCONST char Far HeadersS[] =
++ " Length Date Time Name";
++ static ZCONST char Far HeadersS1[] =
++ "--------- ---------- ----- ----";
++#endif
++
++ static ZCONST char Far HeadersL[] =
++ " Length Method Size Cmpr Date Time CRC-32 Name";
++ static ZCONST char Far HeadersL1[] =
++ "-------- ------ ------- ---- ---------- ----- -------- ----";
++ static ZCONST char Far *Headers[][2] =
++ { {HeadersS, HeadersS1}, {HeadersL, HeadersL1} };
++
++ static ZCONST char Far CaseConversion[] =
++ "%s (\"^\" ==> case\n%s conversion)\n";
++ static ZCONST char Far LongHdrStats[] =
++ "%s %-7s%s %4s %02u%c%02u%c%02u %02u:%02u %08lx %c";
++ static ZCONST char Far LongFileTrailer[] =
++ "-------- ------- --- \
++ -------\n%s %s %4s %lu file%s\n";
++#ifdef OS2_EAS
++ static ZCONST char Far ShortHdrStats[] =
++ "%s %6lu %6lu %02u%c%02u%c%02u %02u:%02u %c";
++ static ZCONST char Far ShortFileTrailer[] =
++ "--------- ----- ----- \
++ -------\n%s %6lu %6lu %lu file%s\n";
++ static ZCONST char Far OS2ExtAttrTrailer[] =
++ "%lu file%s %lu bytes of OS/2 extended attributes attached.\n";
++ static ZCONST char Far OS2ACLTrailer[] =
++ "%lu file%s %lu bytes of access control lists attached.\n";
++#else
++ static ZCONST char Far ShortHdrStats[] =
++ "%s %02u%c%02u%c%02u %02u:%02u %c";
++ static ZCONST char Far ShortFileTrailer[] =
++ "--------- -------\n%s\
++ %lu file%s\n";
++#endif /* ?OS2_EAS */
++#endif /* !WINDLL */
++
++
++
++
++
++/*************************/
++/* Function list_files() */
++/*************************/
++
++int list_files(__G) /* return PK-type error code */
++ __GDEF
++{
++ int do_this_file=FALSE, cfactor, error, error_in_archive=PK_COOL;
++#ifndef WINDLL
++ char sgn, cfactorstr[13];
++ int longhdr=(uO.vflag>1);
++#endif
++ int date_format;
++ char dt_sepchar;
++ ulg members=0L;
++ zusz_t j;
++ unsigned methnum;
++#ifdef USE_EF_UT_TIME
++ iztimes z_utime;
++ struct tm *t;
++#endif
++ unsigned yr, mo, dy, hh, mm;
++ zusz_t csiz, tot_csize=0L, tot_ucsize=0L;
++#ifdef OS2_EAS
++ ulg ea_size, tot_easize=0L, tot_eafiles=0L;
++ ulg acl_size, tot_aclsize=0L, tot_aclfiles=0L;
++#endif
++ min_info info;
++ char methbuf[8];
++ static ZCONST char dtype[]="NXFS"; /* see zi_short() */
++ static ZCONST char Far method[NUM_METHODS+1][8] =
++ {"Stored", "Shrunk", "Reduce1", "Reduce2", "Reduce3", "Reduce4",
++ "Implode", "Token", "Defl:#", "Def64#", "ImplDCL", "BZip2",
++ "LZMA", "Terse", "IBMLZ77", "WavPack", "PPMd", "Unk:###"};
++
++
++
++/*---------------------------------------------------------------------------
++ Unlike extract_or_test_files(), this routine confines itself to the cen-
++ tral directory. Thus its structure is somewhat simpler, since we can do
++ just a single loop through the entire directory, listing files as we go.
++
++ So to start off, print the heading line and then begin main loop through
++ the central directory. The results will look vaguely like the following:
++
++ Length Method Size Ratio Date Time CRC-32 Name ("^" ==> case
++-------- ------ ------- ----- ---- ---- ------ ---- conversion)
++ 44004 Implode 13041 71% 11-02-89 19:34 8b4207f7 Makefile.UNIX
++ 3438 Shrunk 2209 36% 09-15-90 14:07 a2394fd8 ^dos-file.ext
++ 16717 Defl:X 5252 69% 11-03-97 06:40 1ce0f189 WHERE
++-------- ------- --- -------
++ 64159 20502 68% 3 files
++ ---------------------------------------------------------------------------*/
++
++ G.pInfo = &info;
++ date_format = DATE_FORMAT;
++ dt_sepchar = DATE_SEPCHAR;
++
++#ifndef WINDLL
++ if (uO.qflag < 2) {
++ if (uO.L_flag)
++ Info(slide, 0, ((char *)slide, LoadFarString(CaseConversion),
++ LoadFarStringSmall(Headers[longhdr][0]),
++ LoadFarStringSmall2(Headers[longhdr][1])));
++ else
++ Info(slide, 0, ((char *)slide, "%s\n%s\n",
++ LoadFarString(Headers[longhdr][0]),
++ LoadFarStringSmall(Headers[longhdr][1])));
++ }
++#endif /* !WINDLL */
++
++ for (j = 1L;;j++) {
++
++ if (readbuf(__G__ G.sig, 4) == 0)
++ return PK_EOF;
++ if (memcmp(G.sig, central_hdr_sig, 4)) { /* is it a CentDir entry? */
++ /* no new central directory entry
++ * -> is the number of processed entries compatible with the
++ * number of entries as stored in the end_central record?
++ */
++ if (((j - 1) &
++ (ulg)(G.ecrec.have_ecr64 ? MASK_ZUCN64 : MASK_ZUCN16))
++ == (ulg)G.ecrec.total_entries_central_dir)
++ {
++ /* "j modulus 4T/64k" matches the reported 64/16-bit-unsigned
++ * number of directory entries -> probably, the regular
++ * end of the central directory has been reached
++ */
++ break;
++ } else {
++ Info(slide, 0x401,
++ ((char *)slide, LoadFarString(CentSigMsg), j));
++ Info(slide, 0x401,
++ ((char *)slide,"%s", LoadFarString(ReportMsg)));
++ return PK_BADERR; /* sig not found */
++ }
++ }
++ /* process_cdir_file_hdr() sets pInfo->hostnum, pInfo->lcflag, ...: */
++ if ((error = process_cdir_file_hdr(__G)) != PK_COOL)
++ return error; /* only PK_EOF defined */
++
++ /*
++ * We could DISPLAY the filename instead of storing (and possibly trun-
++ * cating, in the case of a very long name) and printing it, but that
++ * has the disadvantage of not allowing case conversion--and it's nice
++ * to be able to see in the listing precisely how you have to type each
++ * filename in order for unzip to consider it a match. Speaking of
++ * which, if member names were specified on the command line, check in
++ * with match() to see if the current file is one of them, and make a
++ * note of it if it is.
++ */
++
++ if ((error = do_string(__G__ G.crec.filename_length, DS_FN)) !=
++ PK_COOL) /* ^--(uses pInfo->lcflag) */
++ {
++ error_in_archive = error;
++ if (error > PK_WARN) /* fatal: can't continue */
++ return error;
++ }
++ if (G.extra_field != (uch *)NULL) {
++ free(G.extra_field);
++ G.extra_field = (uch *)NULL;
++ }
++ if ((error = do_string(__G__ G.crec.extra_field_length, EXTRA_FIELD))
++ != 0)
++ {
++ error_in_archive = error;
++ if (error > PK_WARN) /* fatal */
++ return error;
++ }
++ if (!G.process_all_files) { /* check if specified on command line */
++ unsigned i;
++
++ if (G.filespecs == 0)
++ do_this_file = TRUE;
++ else { /* check if this entry matches an `include' argument */
++ do_this_file = FALSE;
++ for (i = 0; i < G.filespecs; i++)
++ if (match(G.filename, G.pfnames[i], uO.C_flag WISEP)) {
++ do_this_file = TRUE;
++ break; /* found match, so stop looping */
++ }
++ }
++ if (do_this_file) { /* check if this is an excluded file */
++ for (i = 0; i < G.xfilespecs; i++)
++ if (match(G.filename, G.pxnames[i], uO.C_flag WISEP)) {
++ do_this_file = FALSE; /* ^-- ignore case in match */
++ break;
++ }
++ }
++ }
++ /*
++ * If current file was specified on command line, or if no names were
++ * specified, do the listing for this file. Otherwise, get rid of the
++ * file comment and go back for the next file.
++ */
++
++ if (G.process_all_files || do_this_file) {
++
++#ifdef OS2DLL
++ /* this is used by UzpFileTree() to allow easy processing of lists
++ * of zip directory contents */
++ if (G.processExternally) {
++ if ((G.processExternally)(G.filename, &G.crec))
++ break;
++ ++members;
++ } else {
++#endif
++#ifdef OS2_EAS
++ {
++ uch *ef_ptr = G.extra_field;
++ int ef_size, ef_len = G.crec.extra_field_length;
++ ea_size = acl_size = 0;
++
++ while (ef_len >= EB_HEADSIZE) {
++ ef_size = makeword(&ef_ptr[EB_LEN]);
++ switch (makeword(&ef_ptr[EB_ID])) {
++ case EF_OS2:
++ ea_size = makelong(&ef_ptr[EB_HEADSIZE]);
++ break;
++ case EF_ACL:
++ acl_size = makelong(&ef_ptr[EB_HEADSIZE]);
++ break;
++ }
++ ef_ptr += (ef_size + EB_HEADSIZE);
++ ef_len -= (ef_size + EB_HEADSIZE);
++ }
++ }
++#endif
++#ifdef USE_EF_UT_TIME
++ if (G.extra_field &&
++#ifdef IZ_CHECK_TZ
++ G.tz_is_valid &&
++#endif
++ (ef_scan_for_izux(G.extra_field, G.crec.extra_field_length, 1,
++ G.crec.last_mod_dos_datetime, &z_utime, NULL)
++ & EB_UT_FL_MTIME))
++ {
++ TIMET_TO_NATIVE(z_utime.mtime) /* NOP unless MSC 7.0, Mac */
++ t = localtime(&(z_utime.mtime));
++ } else
++ t = (struct tm *)NULL;
++ if (t != (struct tm *)NULL) {
++ mo = (unsigned)(t->tm_mon + 1);
++ dy = (unsigned)(t->tm_mday);
++ yr = (unsigned)(t->tm_year + 1900);
++ hh = (unsigned)(t->tm_hour);
++ mm = (unsigned)(t->tm_min);
++ } else
++#endif /* USE_EF_UT_TIME */
++ {
++ yr = ((((unsigned)(G.crec.last_mod_dos_datetime >> 25) & 0x7f)
++ + 1980));
++ mo = ((unsigned)(G.crec.last_mod_dos_datetime >> 21) & 0x0f);
++ dy = ((unsigned)(G.crec.last_mod_dos_datetime >> 16) & 0x1f);
++ hh = (((unsigned)G.crec.last_mod_dos_datetime >> 11) & 0x1f);
++ mm = (((unsigned)G.crec.last_mod_dos_datetime >> 5) & 0x3f);
++ }
++ /* permute date so it displays according to nat'l convention
++ * ('methnum' is not yet set, it is used as temporary buffer) */
++ switch (date_format) {
++ case DF_YMD:
++ methnum = mo;
++ mo = yr; yr = dy; dy = methnum;
++ break;
++ case DF_DMY:
++ methnum = mo;
++ mo = dy; dy = methnum;
++ }
++
++ csiz = G.crec.csize;
++ if (G.crec.general_purpose_bit_flag & 1)
++ csiz -= 12; /* if encrypted, don't count encryption header */
++ if ((cfactor = ratio(G.crec.ucsize, csiz)) < 0) {
++#ifndef WINDLL
++ sgn = '-';
++#endif
++ cfactor = (-cfactor + 5) / 10;
++ } else {
++#ifndef WINDLL
++ sgn = ' ';
++#endif
++ cfactor = (cfactor + 5) / 10;
++ }
++
++ methnum = find_compr_idx(G.crec.compression_method);
++ zfstrcpy(methbuf, method[methnum]);
++ if (G.crec.compression_method == DEFLATED ||
++ G.crec.compression_method == ENHDEFLATED) {
++ methbuf[5] = dtype[(G.crec.general_purpose_bit_flag>>1) & 3];
++ } else if (methnum >= NUM_METHODS) {
++ /* 2013-02-26 SMS.
++ * http://sourceforge.net/tracker/?func=detail
++ * &aid=2861648&group_id=118012&atid=679786
++ * Unexpectedly large compression methods overflow
++ * &methbuf[]. Use the old, three-digit decimal format
++ * for values which fit. Otherwise, sacrifice the
++ * colon, and use four-digit hexadecimal.
++ */
++ if (G.crec.compression_method <= 999) {
++ sprintf( &methbuf[ 4], "%03u", G.crec.compression_method);
++ } else {
++ sprintf( &methbuf[ 3], "%04X", G.crec.compression_method);
++ }
++ }
++
++#if 0 /* GRR/Euro: add this? */
++#if defined(DOS_FLX_NLM_OS2_W32) || defined(THEOS) || defined(UNIX)
++ for (p = G.filename; *p; ++p)
++ if (!isprint(*p))
++ *p = '?'; /* change non-printable chars to '?' */
++#endif /* DOS_FLX_NLM_OS2_W32 || THEOS || UNIX */
++#endif /* 0 */
++
++#ifdef WINDLL
++ /* send data to application for formatting and printing */
++ if (G.lpUserFunctions->SendApplicationMessage != NULL)
++ (*G.lpUserFunctions->SendApplicationMessage)(G.crec.ucsize,
++ csiz, (unsigned)cfactor, mo, dy, yr, hh, mm,
++ (char)(G.pInfo->lcflag ? '^' : ' '),
++ (LPCSTR)fnfilter(G.filename, slide, (WSIZE>>1)),
++ (LPCSTR)methbuf, G.crec.crc32,
++ (char)((G.crec.general_purpose_bit_flag & 1) ? 'E' : ' '));
++ else if (G.lpUserFunctions->SendApplicationMessage_i32 != NULL) {
++ unsigned long ucsize_lo, csiz_lo;
++ unsigned long ucsize_hi=0L, csiz_hi=0L;
++ ucsize_lo = (unsigned long)(G.crec.ucsize);
++ csiz_lo = (unsigned long)(csiz);
++#ifdef ZIP64_SUPPORT
++ ucsize_hi = (unsigned long)(G.crec.ucsize >> 32);
++ csiz_hi = (unsigned long)(csiz >> 32);
++#endif /* ZIP64_SUPPORT */
++ (*G.lpUserFunctions->SendApplicationMessage_i32)(ucsize_lo,
++ ucsize_hi, csiz_lo, csiz_hi, (unsigned)cfactor,
++ mo, dy, yr, hh, mm,
++ (char)(G.pInfo->lcflag ? '^' : ' '),
++ (LPCSTR)fnfilter(G.filename, slide, (WSIZE>>1)),
++ (LPCSTR)methbuf, G.crec.crc32,
++ (char)((G.crec.general_purpose_bit_flag & 1) ? 'E' : ' '));
++ }
++#else /* !WINDLL */
++ if (cfactor == 100)
++ sprintf(cfactorstr, LoadFarString(CompFactor100));
++ else
++ sprintf(cfactorstr, LoadFarString(CompFactorStr), sgn, cfactor);
++ if (longhdr)
++ Info(slide, 0, ((char *)slide, LoadFarString(LongHdrStats),
++ FmZofft(G.crec.ucsize, "8", "u"), methbuf,
++ FmZofft(csiz, "8", "u"), cfactorstr,
++ mo, dt_sepchar, dy, dt_sepchar, yr, hh, mm,
++ G.crec.crc32, (G.pInfo->lcflag? '^':' ')));
++ else
++#ifdef OS2_EAS
++ Info(slide, 0, ((char *)slide, LoadFarString(ShortHdrStats),
++ FmZofft(G.crec.ucsize, "9", "u"), ea_size, acl_size,
++ mo, dt_sepchar, dy, dt_sepchar, yr, hh, mm,
++ (G.pInfo->lcflag? '^':' ')));
++#else
++ Info(slide, 0, ((char *)slide, LoadFarString(ShortHdrStats),
++ FmZofft(G.crec.ucsize, "9", "u"),
++ mo, dt_sepchar, dy, dt_sepchar, yr, hh, mm,
++ (G.pInfo->lcflag? '^':' ')));
++#endif
++ fnprint(__G);
++#endif /* ?WINDLL */
++
++ if ((error = do_string(__G__ G.crec.file_comment_length,
++ QCOND? DISPL_8 : SKIP)) != 0)
++ {
++ error_in_archive = error; /* might be just warning */
++ if (error > PK_WARN) /* fatal */
++ return error;
++ }
++ tot_ucsize += G.crec.ucsize;
++ tot_csize += csiz;
++ ++members;
++#ifdef OS2_EAS
++ if (ea_size) {
++ tot_easize += ea_size;
++ ++tot_eafiles;
++ }
++ if (acl_size) {
++ tot_aclsize += acl_size;
++ ++tot_aclfiles;
++ }
++#endif
++#ifdef OS2DLL
++ } /* end of "if (G.processExternally) {...} else {..." */
++#endif
++ } else { /* not listing this file */
++ SKIP_(G.crec.file_comment_length)
++ }
++ } /* end for-loop (j: files in central directory) */
++
++/*---------------------------------------------------------------------------
++ Print footer line and totals (compressed size, uncompressed size, number
++ of members in zipfile).
++ ---------------------------------------------------------------------------*/
++
++ if (uO.qflag < 2
++#ifdef OS2DLL
++ && !G.processExternally
++#endif
++ ) {
++ if ((cfactor = ratio(tot_ucsize, tot_csize)) < 0) {
++#ifndef WINDLL
++ sgn = '-';
++#endif
++ cfactor = (-cfactor + 5) / 10;
++ } else {
++#ifndef WINDLL
++ sgn = ' ';
++#endif
++ cfactor = (cfactor + 5) / 10;
++ }
++#ifdef WINDLL
++ /* pass the totals back to the calling application */
++ G.lpUserFunctions->TotalSizeComp = tot_csize;
++ G.lpUserFunctions->TotalSize = tot_ucsize;
++ G.lpUserFunctions->CompFactor = (ulg)cfactor;
++ G.lpUserFunctions->NumMembers = members;
++
++#else /* !WINDLL */
++ if (cfactor == 100)
++ sprintf(cfactorstr, LoadFarString(CompFactor100));
++ else
++ sprintf(cfactorstr, LoadFarString(CompFactorStr), sgn, cfactor);
++ if (longhdr) {
++ Info(slide, 0, ((char *)slide, LoadFarString(LongFileTrailer),
++ FmZofft(tot_ucsize, "8", "u"), FmZofft(tot_csize, "8", "u"),
++ cfactorstr, members, members==1? "":"s"));
++#ifdef OS2_EAS
++ if (tot_easize || tot_aclsize)
++ Info(slide, 0, ((char *)slide, "\n"));
++ if (tot_eafiles && tot_easize)
++ Info(slide, 0, ((char *)slide, LoadFarString(OS2ExtAttrTrailer),
++ tot_eafiles, tot_eafiles == 1? " has" : "s have a total of",
++ tot_easize));
++ if (tot_aclfiles && tot_aclsize)
++ Info(slide, 0, ((char *)slide, LoadFarString(OS2ACLTrailer),
++ tot_aclfiles,
++ tot_aclfiles == 1 ? " has" : "s have a total of",
++ tot_aclsize));
++#endif /* OS2_EAS */
++ } else
++#ifdef OS2_EAS
++ Info(slide, 0, ((char *)slide, LoadFarString(ShortFileTrailer),
++ FmZofft(tot_ucsize, "9", "u"), tot_easize, tot_aclsize,
++ members, members == 1 ? "" : "s"));
++#else
++ Info(slide, 0, ((char *)slide, LoadFarString(ShortFileTrailer),
++ FmZofft(tot_ucsize, "9", "u"),
++ members, members == 1 ? "" : "s"));
++#endif /* OS2_EAS */
++#endif /* ?WINDLL */
++ }
++
++ /* Skip the following checks in case of a premature listing break. */
++ if (error_in_archive <= PK_WARN) {
++
++/*---------------------------------------------------------------------------
++ Double check that we're back at the end-of-central-directory record.
++ ---------------------------------------------------------------------------*/
++
++ if ( (memcmp(G.sig,
++ (G.ecrec.have_ecr64 ?
++ end_central64_sig : end_central_sig),
++ 4) != 0)
++ && (!G.ecrec.is_zip64_archive)
++ && (memcmp(G.sig, end_central_sig, 4) != 0)
++ ) { /* just to make sure again */
++ Info(slide, 0x401,
++ ((char *)slide,"%s", LoadFarString(EndSigMsg)));
++ error_in_archive = PK_WARN; /* didn't find sig */
++ }
++
++ /* Set specific return code when no files have been found. */
++ if (members == 0L && error_in_archive <= PK_WARN)
++ error_in_archive = PK_FIND;
++
++ }
++
++ return error_in_archive;
++
++} /* end function list_files() */
++
++
++
++
++
++#ifdef TIMESTAMP
++
++/************************/
++/* Function fn_is_dir() */
++/************************/
++
++static int fn_is_dir(__G) /* returns TRUE if G.filename is directory */
++ __GDEF
++{
++ extent fn_len = strlen(G.filename);
++ register char endc;
++
++ return fn_len > 0 &&
++ ((endc = lastchar(G.filename, fn_len)) == '/' ||
++ (G.pInfo->hostnum == FS_FAT_ && !MBSCHR(G.filename, '/') &&
++ endc == '\\'));
++}
++
++
++
++
++
++/*****************************/
++/* Function get_time_stamp() */
++/*****************************/
++
++int get_time_stamp(__G__ last_modtime, nmember) /* return PK-type error code */
++ __GDEF
++ time_t *last_modtime;
++ ulg *nmember;
++{
++ int do_this_file=FALSE, error, error_in_archive=PK_COOL;
++ ulg j;
++#ifdef USE_EF_UT_TIME
++ iztimes z_utime;
++#endif
++ min_info info;
++
++
++/*---------------------------------------------------------------------------
++ Unlike extract_or_test_files() but like list_files(), this function works
++ on information in the central directory alone. Thus we have a single,
++ large loop through the entire directory, searching for the latest time
++ stamp.
++ ---------------------------------------------------------------------------*/
++
++ *last_modtime = 0L; /* assuming no zipfile data older than 1970 */
++ *nmember = 0L;
++ G.pInfo = &info;
++
++ for (j = 1L;; j++) {
++
++ if (readbuf(__G__ G.sig, 4) == 0)
++ return PK_EOF;
++ if (memcmp(G.sig, central_hdr_sig, 4)) { /* is it a CentDir entry? */
++ if (((unsigned)(j - 1) & (unsigned)0xFFFF) ==
++ (unsigned)G.ecrec.total_entries_central_dir) {
++ /* "j modulus 64k" matches the reported 16-bit-unsigned
++ * number of directory entries -> probably, the regular
++ * end of the central directory has been reached
++ */
++ break;
++ } else {
++ Info(slide, 0x401,
++ ((char *)slide, LoadFarString(CentSigMsg), j));
++ Info(slide, 0x401,
++ ((char *)slide,"%s", LoadFarString(ReportMsg)));
++ return PK_BADERR; /* sig not found */
++ }
++ }
++ /* process_cdir_file_hdr() sets pInfo->lcflag: */
++ if ((error = process_cdir_file_hdr(__G)) != PK_COOL)
++ return error; /* only PK_EOF defined */
++ if ((error = do_string(__G__ G.crec.filename_length, DS_FN)) != PK_OK)
++ { /* ^-- (uses pInfo->lcflag) */
++ error_in_archive = error;
++ if (error > PK_WARN) /* fatal: can't continue */
++ return error;
++ }
++ if (G.extra_field != (uch *)NULL) {
++ free(G.extra_field);
++ G.extra_field = (uch *)NULL;
++ }
++ if ((error = do_string(__G__ G.crec.extra_field_length, EXTRA_FIELD))
++ != 0)
++ {
++ error_in_archive = error;
++ if (error > PK_WARN) /* fatal */
++ return error;
++ }
++ if (!G.process_all_files) { /* check if specified on command line */
++ unsigned i;
++
++ if (G.filespecs == 0)
++ do_this_file = TRUE;
++ else { /* check if this entry matches an `include' argument */
++ do_this_file = FALSE;
++ for (i = 0; i < G.filespecs; i++)
++ if (match(G.filename, G.pfnames[i], uO.C_flag WISEP)) {
++ do_this_file = TRUE;
++ break; /* found match, so stop looping */
++ }
++ }
++ if (do_this_file) { /* check if this is an excluded file */
++ for (i = 0; i < G.xfilespecs; i++)
++ if (match(G.filename, G.pxnames[i], uO.C_flag WISEP)) {
++ do_this_file = FALSE; /* ^-- ignore case in match */
++ break;
++ }
++ }
++ }
++
++ /* If current file was specified on command line, or if no names were
++ * specified, check the time for this file. Either way, get rid of the
++ * file comment and go back for the next file.
++ * Directory entries are always ignored, to stay compatible with both
++ * Zip and PKZIP.
++ */
++ if ((G.process_all_files || do_this_file) && !fn_is_dir(__G)) {
++#ifdef USE_EF_UT_TIME
++ if (G.extra_field &&
++#ifdef IZ_CHECK_TZ
++ G.tz_is_valid &&
++#endif
++ (ef_scan_for_izux(G.extra_field, G.crec.extra_field_length, 1,
++ G.crec.last_mod_dos_datetime, &z_utime, NULL)
++ & EB_UT_FL_MTIME))
++ {
++ if (*last_modtime < z_utime.mtime)
++ *last_modtime = z_utime.mtime;
++ } else
++#endif /* USE_EF_UT_TIME */
++ {
++ time_t modtime = dos_to_unix_time(G.crec.last_mod_dos_datetime);
++
++ if (*last_modtime < modtime)
++ *last_modtime = modtime;
++ }
++ ++*nmember;
++ }
++ SKIP_(G.crec.file_comment_length)
++
++ } /* end for-loop (j: files in central directory) */
++
++/*---------------------------------------------------------------------------
++ Double check that we're back at the end-of-central-directory record.
++ ---------------------------------------------------------------------------*/
++
++ if (memcmp(G.sig, end_central_sig, 4)) { /* just to make sure again */
++ Info(slide, 0x401, ((char *)slide,"%s", LoadFarString(EndSigMsg)));
++ error_in_archive = PK_WARN;
++ }
++ if (*nmember == 0L && error_in_archive <= PK_WARN)
++ error_in_archive = PK_FIND;
++
++ return error_in_archive;
++
++} /* end function get_time_stamp() */
++
++#endif /* TIMESTAMP */
++
++
++
++
++
++/********************/
++/* Function ratio() */ /* also used by ZipInfo routines */
++/********************/
++
++int ratio(uc, c)
++ zusz_t uc, c;
++{
++ zusz_t denom;
++
++ if (uc == 0)
++ return 0;
++ if (uc > 2000000L) { /* risk signed overflow if multiply numerator */
++ denom = uc / 1000L;
++ return ((uc >= c) ?
++ (int) ((uc-c + (denom>>1)) / denom) :
++ -((int) ((c-uc + (denom>>1)) / denom)));
++ } else { /* ^^^^^^^^ rounding */
++ denom = uc;
++ return ((uc >= c) ?
++ (int) ((1000L*(uc-c) + (denom>>1)) / denom) :
++ -((int) ((1000L*(c-uc) + (denom>>1)) / denom)));
++ } /* ^^^^^^^^ rounding */
++}
++
++
++
++
++
++/************************/
++/* Function fnprint() */ /* also used by ZipInfo routines */
++/************************/
++
++void fnprint(__G) /* print filename (after filtering) and newline */
++ __GDEF
++{
++ char *name = fnfilter(G.filename, slide, (extent)(WSIZE>>1));
++
++ (*G.message)((zvoid *)&G, (uch *)name, (ulg)strlen(name), 0);
++ (*G.message)((zvoid *)&G, (uch *)"\n", 1L, 0);
++
++} /* end function fnprint() */
+diff -Naur a/match.c b/match.c
+--- a/match.c 2005-08-14 18:00:36.000000000 +0100
++++ b/match.c 2019-12-02 00:19:45.331163073 +0000
+@@ -27,16 +27,14 @@
+
+ ---------------------------------------------------------------------------
+
+- Copyright on recmatch() from Zip's util.c (although recmatch() was almost
+- certainly written by Mark Adler...ask me how I can tell :-) ):
++ Copyright on recmatch() from Zip's util.c
++ Copyright (c) 1990-2005 Info-ZIP. All rights reserved.
+
+- Copyright (C) 1990-1992 Mark Adler, Richard B. Wales, Jean-loup Gailly,
+- Kai Uwe Rommel and Igor Mandrichenko.
++ See the accompanying file LICENSE, version 2004-May-22 or later
++ for terms of use.
++ If, for some reason, both of these files are missing, the Info-ZIP license
++ also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html
+
+- Permission is granted to any individual or institution to use, copy,
+- or redistribute this software so long as all of the original files are
+- included unmodified, that it is not sold for profit, and that this copy-
+- right notice is retained.
+
+ ---------------------------------------------------------------------------
+
+@@ -53,7 +51,7 @@
+
+ A set is composed of characters or ranges; a range looks like ``character
+ hyphen character'' (as in 0-9 or A-Z). [0-9a-zA-Z_] is the minimal set of
+- characters allowed in the [..] pattern construct. Other characters are
++ characters ALlowed in the [..] pattern construct. Other characters are
+ allowed (i.e., 8-bit characters) if your system will support them.
+
+ To suppress the special syntactic significance of any of ``[]*?!^-\'', in-
+@@ -101,8 +99,32 @@
+ # define WILDCHAR '?'
+ # define BEG_RANGE '['
+ # define END_RANGE ']'
++# define WILDCHR_SINGLE '?'
++# define DIRSEP_CHR '/'
++# define WILDCHR_MULTI '*'
+ #endif
+
++#ifdef WILD_STOP_AT_DIR
++ int wild_stop_at_dir = 1; /* default wildcards do not include / in matches */
++#else
++ int wild_stop_at_dir = 0; /* default wildcards do include / in matches */
++#endif
++
++
++
++/*
++ * case mapping functions. case_map is used to ignore case in comparisons,
++ * to_up is used to force upper case even on Unix (for dosify option).
++ */
++#ifdef USE_CASE_MAP
++# define case_map(c) upper[(c) & 0xff]
++# define to_up(c) upper[(c) & 0xff]
++#else
++# define case_map(c) (c)
++# define to_up(c) ((c) >= 'a' && (c) <= 'z' ? (c)-'a'+'A' : (c))
++#endif /* USE_CASE_MAP */
++
++
+ #if 0 /* GRR: add this to unzip.h someday... */
+ #if !(defined(MSDOS) && defined(DOSWILD))
+ #ifdef WILD_STOP_AT_DIR
+@@ -114,8 +136,8 @@
+ int ignore_case __WDLPRO));
+ #endif
+ #endif /* 0 */
+-static int recmatch OF((ZCONST uch *pattern, ZCONST uch *string,
+- int ignore_case __WDLPRO));
++static int recmatch OF((ZCONST char *, ZCONST char *,
++ int));
+ static char *isshexp OF((ZCONST char *p));
+ static int namecmp OF((ZCONST char *s1, ZCONST char *s2));
+
+@@ -154,192 +176,240 @@
+ }
+ dospattern[j-1] = '\0'; /* nuke the end "." */
+ }
+- j = recmatch((uch *)dospattern, (uch *)string, ignore_case __WDL);
++ j = recmatch(dospattern, string, ignore_case);
+ free(dospattern);
+ return j == 1;
+ } else
+ #endif /* MSDOS && DOSWILD */
+- return recmatch((uch *)pattern, (uch *)string, ignore_case __WDL) == 1;
++ return recmatch(pattern, string, ignore_case) == 1;
+ }
+
++#ifdef _MBCS
++
++char *___tmp_ptr;
+
++#endif
+
+-static int recmatch(p, s, ic __WDL)
+- ZCONST uch *p; /* sh pattern to match */
+- ZCONST uch *s; /* string to which to match it */
+- int ic; /* true for case insensitivity */
+- __WDLDEF /* directory sepchar for WildStopAtDir mode, or 0 */
++static int recmatch(p, s, ci)
++ZCONST char *p; /* sh pattern to match */
++ZCONST char *s; /* string to match it to */
++int ci; /* flag: force case-insensitive matching */
+ /* Recursively compare the sh pattern p with the string s and return 1 if
+- * they match, and 0 or 2 if they don't or if there is a syntax error in the
+- * pattern. This routine recurses on itself no more deeply than the number
+- * of characters in the pattern. */
++ they match, and 0 or 2 if they don't or if there is a syntax error in the
++ pattern. This routine recurses on itself no deeper than the number of
++ characters in the pattern. */
+ {
+- unsigned int c; /* pattern char or start of range in [-] loop */
++ int c; /* pattern char or start of range in [-] loop */
++ /* Get first character, the pattern for new recmatch calls follows */
++ /* borrowed from Zip's global.c */
++ int no_wild = 0;
++ int allow_regex=1;
++ /* This fix provided by akt@m5.dion.ne.jp for Japanese.
++ See 21 July 2006 mail.
++ It only applies when p is pointing to a doublebyte character and
++ things like / and wildcards are not doublebyte. This probably
++ should not be needed. */
+
+- /* Get first character, the pattern for new recmatch calls follows */
+- c = *p; INCSTR(p);
++#ifdef _MBCS
++ if (CLEN(p) == 2) {
++ if (CLEN(s) == 2) {
++ return (*p == *s && *(p+1) == *(s+1)) ?
++ recmatch(p + 2, s + 2, ci) : 0;
++ } else {
++ return 0;
++ }
++ }
++#endif /* ?_MBCS */
+
+- /* If that was the end of the pattern, match if string empty too */
+- if (c == 0)
+- return *s == 0;
++ c = *POSTINCSTR(p);
+
+- /* '?' (or '%') matches any character (but not an empty string). */
+- if (c == WILDCHAR)
+-#ifdef WILD_STOP_AT_DIR
+- /* If uO.W_flag is non-zero, it won't match '/' */
+- return (*s && (!sepc || *s != (uch)sepc))
+- ? recmatch(p, s + CLEN(s), ic, sepc) : 0;
+-#else
+- return *s ? recmatch(p, s + CLEN(s), ic) : 0;
+-#endif
++ /* If that was the end of the pattern, match if string empty too */
++ if (c == 0)
++ return *s == 0;
++
++ /* '?' (or '%' or '#') matches any character (but not an empty string) */
++ if (c == WILDCHR_SINGLE) {
++ if (wild_stop_at_dir)
++ return (*s && *s != DIRSEP_CHR) ? recmatch(p, s + CLEN(s), ci) : 0;
++ else
++ return *s ? recmatch(p, s + CLEN(s), ci) : 0;
++ }
+
+- /* '*' matches any number of characters, including zero */
++ /* WILDCHR_MULTI ('*') matches any number of characters, including zero */
+ #ifdef AMIGA
+- if (c == '#' && *p == '?') /* "#?" is Amiga-ese for "*" */
+- c = '*', p++;
++ if (!no_wild && c == '#' && *p == '?') /* "#?" is Amiga-ese for "*" */
++ c = WILDCHR_MULTI, p++;
+ #endif /* AMIGA */
+- if (c == '*') {
+-#ifdef WILD_STOP_AT_DIR
+- if (sepc) {
+- /* check for single "*" or double "**" */
+-# ifdef AMIGA
+- if ((c = p[0]) == '#' && p[1] == '?') /* "#?" is Amiga-ese for "*" */
+- c = '*', p++;
+- if (c != '*') {
+-# else /* !AMIGA */
+- if (*p != '*') {
+-# endif /* ?AMIGA */
+- /* single "*": this doesn't match the dirsep character */
+- for (; *s && *s != (uch)sepc; INCSTR(s))
+- if ((c = recmatch(p, s, ic, sepc)) != 0)
+- return (int)c;
+- /* end of pattern: matched if at end of string, else continue */
+- if (*p == '\0')
+- return (*s == 0);
+- /* continue to match if at sepc in pattern, else give up */
+- return (*p == (uch)sepc || (*p == '\\' && p[1] == (uch)sepc))
+- ? recmatch(p, s, ic, sepc) : 2;
+- }
+- /* "**": this matches slashes */
+- ++p; /* move p behind the second '*' */
+- /* and continue with the non-W_flag code variant */
+- }
+-#endif /* WILD_STOP_AT_DIR */
++ if (!no_wild && c == WILDCHR_MULTI)
++ {
++ if (wild_stop_at_dir) {
++ /* Check for an immediately following WILDCHR_MULTI */
++# ifdef AMIGA
++ if ((c = p[0]) == '#' && p[1] == '?') /* "#?" is Amiga-ese for "*" */
++ c = WILDCHR_MULTI, p++;
++ if (c != WILDCHR_MULTI) {
++# else /* !AMIGA */
++ if (*p != WILDCHR_MULTI) {
++# endif /* ?AMIGA */
++ /* Single WILDCHR_MULTI ('*'): this doesn't match slashes */
++ for (; *s && *s != DIRSEP_CHR; INCSTR(s))
++ if ((c = recmatch(p, s, ci)) != 0)
++ return c;
++ /* end of pattern: matched if at end of string, else continue */
+ if (*p == 0)
+- return 1;
+- if (isshexp((ZCONST char *)p) == NULL) {
+- /* Optimization for rest of pattern being a literal string:
+- * If there are no other shell expression chars in the rest
+- * of the pattern behind the multi-char wildcard, then just
+- * compare the literal string tail.
+- */
+- ZCONST uch *srest;
+-
+- srest = s + (strlen((ZCONST char *)s) - strlen((ZCONST char *)p));
+- if (srest - s < 0)
+- /* remaining literal string from pattern is longer than rest
+- * of test string, there can't be a match
+- */
+- return 0;
+- else
+- /* compare the remaining literal pattern string with the last
+- * bytes of the test string to check for a match
+- */
++ return (*s == 0);
++ /* continue to match if at DIRSEP_CHR in pattern, else give up */
++ return (*p == DIRSEP_CHR || (*p == '\\' && p[1] == DIRSEP_CHR))
++ ? recmatch(p, s, ci) : 2;
++ }
++ /* Two consecutive WILDCHR_MULTI ("**"): this matches DIRSEP_CHR ('/') */
++ p++; /* move p past the second WILDCHR_MULTI */
++ /* continue with the normal non-WILD_STOP_AT_DIR code */
++ } /* wild_stop_at_dir */
++
++ /* Not wild_stop_at_dir */
++ if (*p == 0)
++ return 1;
++ if (!isshexp((char *)p))
++ {
++ /* optimization for rest of pattern being a literal string */
++
++ /* optimization to handle patterns like *.txt */
++ /* if the first char in the pattern is '*' and there */
++ /* are no other shell expression chars, i.e. a literal string */
++ /* then just compare the literal string at the end */
++
++ ZCONST char *srest;
++
++ srest = s + (strlen(s) - strlen(p));
++ if (srest - s < 0)
++ /* remaining literal string from pattern is longer than rest of
++ test string, there can't be a match
++ */
++ return 0;
++ else
++ /* compare the remaining literal pattern string with the last bytes
++ of the test string to check for a match */
+ #ifdef _MBCS
+- {
+- ZCONST uch *q = s;
++ {
++ ZCONST char *q = s;
+
+- /* MBCS-aware code must not scan backwards into a string from
+- * the end.
+- * So, we have to move forward by character from our well-known
+- * character position s in the test string until we have
+- * advanced to the srest position.
+- */
+- while (q < srest)
+- INCSTR(q);
+- /* In case the byte *srest is a trailing byte of a multibyte
+- * character in the test string s, we have actually advanced
+- * past the position (srest).
+- * For this case, the match has failed!
+- */
+- if (q != srest)
+- return 0;
+- return ((ic
+- ? namecmp((ZCONST char *)p, (ZCONST char *)q)
+- : strcmp((ZCONST char *)p, (ZCONST char *)q)
+- ) == 0);
+- }
++ /* MBCS-aware code must not scan backwards into a string from
++ * the end.
++ * So, we have to move forward by character from our well-known
++ * character position s in the test string until we have advanced
++ * to the srest position.
++ */
++ while (q < srest)
++ INCSTR(q);
++ /* In case the byte *srest is a trailing byte of a multibyte
++ * character, we have actually advanced past the position (srest).
++ * For this case, the match has failed!
++ */
++ if (q != srest)
++ return 0;
++ return ((!ci ? strcmp(p, q) : namecmp(p, q)) == 0);
++ }
+ #else /* !_MBCS */
+- return ((ic
+- ? namecmp((ZCONST char *)p, (ZCONST char *)srest)
+- : strcmp((ZCONST char *)p, (ZCONST char *)srest)
+- ) == 0);
++ return ((!ci ? strcmp(p, srest) : namecmp(p, srest)) == 0);
+ #endif /* ?_MBCS */
+- } else {
+- /* pattern contains more wildcards, continue with recursion... */
+- for (; *s; INCSTR(s))
+- if ((c = recmatch(p, s, ic __WDL)) != 0)
+- return (int)c;
+- return 2; /* 2 means give up--match will return false */
+- }
+ }
+-
+- /* Parse and process the list of characters and ranges in brackets */
+- if (c == BEG_RANGE) {
+- int e; /* flag true if next char to be taken literally */
+- ZCONST uch *q; /* pointer to end of [-] group */
+- int r; /* flag true to match anything but the range */
+-
+- if (*s == 0) /* need a character to match */
+- return 0;
+- p += (r = (*p == '!' || *p == '^')); /* see if reverse */
+- for (q = p, e = 0; *q; INCSTR(q)) /* find closing bracket */
+- if (e)
+- e = 0;
+- else
+- if (*q == '\\') /* GRR: change to ^ for MS-DOS, OS/2? */
+- e = 1;
+- else if (*q == END_RANGE)
+- break;
+- if (*q != END_RANGE) /* nothing matches if bad syntax */
+- return 0;
+- for (c = 0, e = (*p == '-'); p < q; INCSTR(p)) {
+- /* go through the list */
+- if (!e && *p == '\\') /* set escape flag if \ */
+- e = 1;
+- else if (!e && *p == '-') /* set start of range if - */
+- c = *(p-1);
+- else {
+- unsigned int cc = Case(*s);
+-
+- if (*(p+1) != '-')
+- for (c = c ? c : *p; c <= *p; c++) /* compare range */
+- if ((unsigned)Case(c) == cc) /* typecast for MSC bug */
+- return r ? 0 : recmatch(q + 1, s + 1, ic __WDL);
+- c = e = 0; /* clear range, escape flags */
+- }
+- }
+- return r ? recmatch(q + CLEN(q), s + CLEN(s), ic __WDL) : 0;
+- /* bracket match failed */
++ else
++ {
++ /* pattern contains more wildcards, continue with recursion... */
++ for (; *s; INCSTR(s))
++ if ((c = recmatch(p, s, ci)) != 0)
++ return c;
++ return 2; /* 2 means give up--shmatch will return false */
+ }
++ }
+
+- /* if escape ('\\'), just compare next character */
+- if (c == '\\' && (c = *p++) == 0) /* if \ at end, then syntax error */
+- return 0;
++#ifndef VMS /* No bracket matching in VMS */
++ /* Parse and process the list of characters and ranges in brackets */
++ if (!no_wild && allow_regex && c == '[')
++ {
++ int e; /* flag true if next char to be taken literally */
++ ZCONST char *q; /* pointer to end of [-] group */
++ int r; /* flag true to match anything but the range */
++
++ if (*s == 0) /* need a character to match */
++ return 0;
++ p += (r = (*p == '!' || *p == '^')); /* see if reverse */
++ for (q = p, e = 0; *q; q++) /* find closing bracket */
++ if (e)
++ e = 0;
++ else
++ if (*q == '\\')
++ e = 1;
++ else if (*q == ']')
++ break;
++ if (*q != ']') /* nothing matches if bad syntax */
++ return 0;
++ for (c = 0, e = *p == '-'; p < q; p++) /* go through the list */
++ {
++ if (e == 0 && *p == '\\') /* set escape flag if \ */
++ e = 1;
++ else if (e == 0 && *p == '-') /* set start of range if - */
++ c = *(p-1);
++ else
++ {
++ uch cc = (!ci ? (uch)*s : to_up((uch)*s));
++ uch uc = (uch) c;
++ if (*(p+1) != '-')
++ for (uc = uc ? uc : (uch)*p; uc <= (uch)*p; uc++)
++ /* compare range */
++ if ((!ci ? uc : to_up(uc)) == cc)
++ return r ? 0 : recmatch(q + CLEN(q), s + CLEN(s), ci);
++ c = e = 0; /* clear range, escape flags */
++ }
++ }
++ return r ? recmatch(q + CLEN(q), s + CLEN(s), ci) : 0;
++ /* bracket match failed */
++ }
++#endif /* !VMS */
+
+- /* just a character--compare it */
+-#ifdef QDOS
+- return QMatch(Case((uch)c), Case(*s)) ?
+- recmatch(p, s + CLEN(s), ic __WDL) : 0;
+-#else
+- return Case((uch)c) == Case(*s) ?
+- recmatch(p, s + CLEN(s), ic __WDL) : 0;
+-#endif
++ /* If escape ('\'), just compare next character */
++ if (!no_wild && c == '\\')
++ if ((c = *p++) == '\0') /* if \ at end, then syntax error */
++ return 0;
++
++#ifdef VMS
++ /* 2005-11-06 SMS.
++ Handle "..." wildcard in p with "." or "]" in s.
++ */
++ if ((c == '.') && (*p == '.') && (*(p+ CLEN( p)) == '.') &&
++ ((*s == '.') || (*s == ']')))
++ {
++ /* Match "...]" with "]". Continue after "]" in both. */
++ if ((*(p+ 2* CLEN( p)) == ']') && (*s == ']'))
++ return recmatch( (p+ 3* CLEN( p)), (s+ CLEN( s)), ci);
++
++ /* Else, look for a reduced match in s, until "]" in or end of s. */
++ for (; *s && (*s != ']'); INCSTR(s))
++ if (*s == '.')
++ /* If reduced match, then continue after "..." in p, "." in s. */
++ if ((c = recmatch( (p+ CLEN( p)), s, ci)) != 0)
++ return (int)c;
++
++ /* Match "...]" with "]". Continue after "]" in both. */
++ if ((*(p+ 2* CLEN( p)) == ']') && (*s == ']'))
++ return recmatch( (p+ 3* CLEN( p)), (s+ CLEN( s)), ci);
++
++ /* No reduced match. Quit. */
++ return 2;
++ }
++
++#endif /* def VMS */
++
++ /* Just a character--compare it */
++ return (!ci ? c == *s : to_up((uch)c) == to_up((uch)*s)) ?
++ recmatch(p, s + CLEN(s), ci) : 0;
++}
+
+-} /* end function recmatch() */
+
+
+
++/*************************************************************************************************/
+ static char *isshexp(p)
+ ZCONST char *p;
+ /* If p is a sh expression, a pointer to the first special character is
+diff -Naur a/process.c b/process.c
+--- a/process.c 2009-03-06 01:25:10.000000000 +0000
++++ b/process.c 2019-12-02 01:44:23.792587920 +0000
+@@ -1,5 +1,5 @@
+ /*
+- Copyright (c) 1990-2009 Info-ZIP. All rights reserved.
++ Copyright (c) 1990-2014 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.
+@@ -1888,48 +1888,83 @@
+ 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.
++
++ 2014-12-05 SMS.
++ Added checks to ensure that enough data are available before calling
++ makeint64() or makelong(). Replaced various sizeof() values with
++ simple ("4" or "8") constants. (The Zip64 structures do not depend
++ on our variable sizes.) Error handling is crude, but we should now
++ stay within the buffer.
+ ---------------------------------------------------------------------------*/
+
++#define Z64FLGS 0xffff
++#define Z64FLGL 0xffffffff
++
+ 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) {
++ 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! */
++ if (eb_len > (ef_len - EB_HEADSIZE))
++ {
++ /* Extra block length exceeds remaining extra field length. */
+ Trace((stderr,
+ "getZip64Data: block length %u > rest ef_size %u\n", eb_len,
+ ef_len - EB_HEADSIZE));
+ break;
+ }
+- if (eb_id == EF_PKSZ64) {
+
++ 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.ucsize == Z64FLGL) || (G.lrec.ucsize == Z64FLGL))
++ {
++ if (offset+ 8 > ef_len)
++ return PK_ERR;
++
++ G.crec.ucsize = G.lrec.ucsize = makeint64(offset + ef_buf);
++ offset += 8;
+ }
+- 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.csize == Z64FLGL) || (G.lrec.csize == Z64FLGL))
++ {
++ if (offset+ 8 > ef_len)
++ return PK_ERR;
++
++ G.csize = G.crec.csize = G.lrec.csize = makeint64(offset + ef_buf);
++ offset += 8;
+ }
+- if (G.crec.relative_offset_local_header == 0xffffffff){
++
++ if (G.crec.relative_offset_local_header == Z64FLGL)
++ {
++ if (offset+ 8 > ef_len)
++ return PK_ERR;
++
+ G.crec.relative_offset_local_header = makeint64(offset + ef_buf);
+- offset += sizeof(G.crec.relative_offset_local_header);
++ offset += 8;
+ }
+- if (G.crec.disk_number_start == 0xffff){
++
++ if (G.crec.disk_number_start == Z64FLGS)
++ {
++ if (offset+ 4 > ef_len)
++ return PK_ERR;
++
+ G.crec.disk_number_start = (zuvl_t)makelong(offset + ef_buf);
+- offset += sizeof(G.crec.disk_number_start);
++ offset += 4;
+ }
++#if 0
++ break; /* Expect only one EF_PKSZ64 block. */
++#endif /* 0 */
+ }
+
+- /* Skip this extra field block */
++ /* Skip this extra field block. */
+ ef_buf += (eb_len + EB_HEADSIZE);
+ ef_len -= (eb_len + EB_HEADSIZE);
+ }
+@@ -2867,10 +2902,13 @@
+ break;
+
+ case EF_IZUNIX2:
+- if (have_new_type_eb == 0) {
+- flags &= ~0x0ff; /* ignore any previous IZUNIX field */
++ if (have_new_type_eb == 0) { /* (< 1) */
+ have_new_type_eb = 1;
+ }
++ if (have_new_type_eb <= 1) {
++ /* Ignore any prior (EF_IZUNIX/EF_PKUNIX) UID/GID. */
++ flags &= 0x0ff;
++ }
+ #ifdef IZ_HAVE_UXUIDGID
+ if (have_new_type_eb > 1)
+ break; /* IZUNIX3 overrides IZUNIX2 e.f. block ! */
+@@ -2886,6 +2924,8 @@
+ /* new 3rd generation Unix ef */
+ have_new_type_eb = 2;
+
++ /* Ignore any prior EF_IZUNIX/EF_PKUNIX/EF_IZUNIX2 UID/GID. */
++ flags &= 0x0ff;
+ /*
+ Version 1 byte version of this extra field, currently 1
+ UIDSize 1 byte Size of UID field
+@@ -2895,9 +2935,9 @@
+ */
+
+ #ifdef IZ_HAVE_UXUIDGID
+- if (eb_len >= EB_UX3_MINLEN
+- && z_uidgid != NULL
+- && (*((EB_HEADSIZE + 0) + ef_buf) == 1)
++ if ((eb_len >= EB_UX3_MINLEN)
++ && (z_uidgid != NULL)
++ && ((*((EB_HEADSIZE + 0) + ef_buf) == 1)))
+ /* only know about version 1 */
+ {
+ uch uid_size;
+@@ -2906,13 +2946,11 @@
+ 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])
++ uid_size, &z_uidgid[0])
+ &&
+ read_ux3_value((EB_HEADSIZE + uid_size + 3) + ef_buf,
+- gid_size, z_uidgid[1]) )
++ gid_size, &z_uidgid[1]) )
+ {
+ flags |= EB_UX2_VALID; /* signal success */
+ }
+diff -Naur a/process.c.orig b/process.c.orig
+--- a/process.c.orig 1970-01-01 01:00:00.000000000 +0100
++++ b/process.c.orig 2019-12-02 00:32:11.309743527 +0000
+@@ -0,0 +1,3123 @@
++/*
++ Copyright (c) 1990-2014 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.
++
++ 2014-12-05 SMS.
++ Added checks to ensure that enough data are available before calling
++ makeint64() or makelong(). Replaced various sizeof() values with
++ simple ("4" or "8") constants. (The Zip64 structures do not depend
++ on our variable sizes.) Error handling is crude, but we should now
++ stay within the buffer.
++ ---------------------------------------------------------------------------*/
++
++#define Z64FLGS 0xffff
++#define Z64FLGL 0xffffffff
++
++ 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))
++ {
++ /* Extra block length exceeds remaining extra field length. */
++ 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 == Z64FLGL) || (G.lrec.ucsize == Z64FLGL))
++ {
++ if (offset+ 8 > ef_len)
++ return PK_ERR;
++
++ G.crec.ucsize = G.lrec.ucsize = makeint64(offset + ef_buf);
++ offset += 8;
++ }
++
++ if ((G.crec.csize == Z64FLGL) || (G.lrec.csize == Z64FLGL))
++ {
++ if (offset+ 8 > ef_len)
++ return PK_ERR;
++
++ G.csize = G.crec.csize = G.lrec.csize = makeint64(offset + ef_buf);
++ offset += 8;
++ }
++
++ if (G.crec.relative_offset_local_header == Z64FLGL)
++ {
++ if (offset+ 8 > ef_len)
++ return PK_ERR;
++
++ G.crec.relative_offset_local_header = makeint64(offset + ef_buf);
++ offset += 8;
++ }
++
++ if (G.crec.disk_number_start == Z64FLGS)
++ {
++ if (offset+ 4 > ef_len)
++ return PK_ERR;
++
++ G.crec.disk_number_start = (zuvl_t)makelong(offset + ef_buf);
++ offset += 4;
++ }
++#if 0
++ break; /* Expect only one EF_PKSZ64 block. */
++#endif /* 0 */
++ }
++
++ /* 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 ! */
++