Splux Linux

source-based Linux using SPS

sps-core · log · files · refs · tags · releases · people · languages · GitHub · commit

7f6949f


core: add unzip, zip, parser tools, sqlite, pcre2, GnuPG, and OpenSSH

Date
Commit7f6949f950263b2a3ad9aa9fe3285a9d9db05e57
GitHubmirror

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 ! */
++