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 ! */
+                }
+                if (z_utim != NULL) {
+                    flags |= (EB_UT_FL_MTIME | EB_UT_FL_ATIME);
+                    i_time = (long)makelong((EB_HEADSIZE+EB_UX_MTIME)+ef_buf);
+                    TTrace((stderr,"  Unix EF modtime = %ld\n", i_time));
+#ifdef TIME_T_TYPE_DOUBLE
+                    if ((ulg)(i_time) & (ulg)(0x80000000L)) {
+                        if (dos_mdatetime == DOSTIME_MINIMUM) {
+                            ut_in_archive_sgn = -1;
+                            z_utim->mtime =
+                              (time_t)((long)i_time | (~(long)0x7fffffffL));
+                        } else if (dos_mdatetime >= DOSTIME_2038_01_18) {
+                            ut_in_archive_sgn = 1;
+                            z_utim->mtime =
+                              (time_t)((ulg)i_time & (ulg)0xffffffffL);
+                        } else {
+                            ut_in_archive_sgn = 0;
+                            /* cannot determine sign of mtime;
+                               without modtime: ignore complete UT field */
+                            flags &= ~0x0ff;    /* no time_t times available */
+                            TTrace((stderr,
+                                  "  UX modtime range error: ignore e.f.!\n"));
+                        }
+                    } else {
+                        /* cannot determine, safe assumption is FALSE */
+                        ut_in_archive_sgn = 0;
+                        z_utim->mtime = (time_t)i_time;
+                    }
+#else /* !TIME_T_TYPE_DOUBLE */
+                    if ((ulg)(i_time) & (ulg)(0x80000000L)) {
+                        ut_zip_unzip_compatible =
+                          ((time_t)0x80000000L < (time_t)0L)
+                          ? (dos_mdatetime == DOSTIME_MINIMUM)
+                          : (dos_mdatetime >= DOSTIME_2038_01_18);
+                        if (!ut_zip_unzip_compatible) {
+                            /* UnZip interpretes mtime differently than Zip;
+                               without modtime: ignore complete UT field */
+                            flags &= ~0x0ff;    /* no time_t times available */
+                            TTrace((stderr,
+                                  "  UX modtime range error: ignore e.f.!\n"));
+                        }
+                    } else {
+                        /* cannot determine, safe assumption is FALSE */
+                        ut_zip_unzip_compatible = FALSE;
+                    }
+                    z_utim->mtime = (time_t)i_time;
+#endif /* ?TIME_T_TYPE_DOUBLE */
+                    i_time = (long)makelong((EB_HEADSIZE+EB_UX_ATIME)+ef_buf);
+                    TTrace((stderr,"  Unix EF actime = %ld\n", i_time));
+#ifdef TIME_T_TYPE_DOUBLE
+                    if ((ulg)(i_time) & (ulg)(0x80000000L)) {
+                        if (ut_in_archive_sgn == -1)
+                            z_utim->atime =
+                              (time_t)((long)i_time | (~(long)0x7fffffffL));
+                        } else if (ut_in_archive_sgn == 1) {
+                            z_utim->atime =
+                              (time_t)((ulg)i_time & (ulg)0xffffffffL);
+                        } else if (flags & 0x0ff) {
+                            /* sign of 32-bit time is unknown -> ignore it */
+                            flags &= ~EB_UT_FL_ATIME;
+                            TTrace((stderr,
+                                "  UX access time range error: skip time!\n"));
+                        }
+                    } else {
+                        z_utim->atime = (time_t)i_time;
+                    }
+#else /* !TIME_T_TYPE_DOUBLE */
+                    if (((ulg)(i_time) & (ulg)(0x80000000L)) &&
+                        !ut_zip_unzip_compatible && (flags & 0x0ff)) {
+                        /* atime not in range of UnZip's time_t */
+                        flags &= ~EB_UT_FL_ATIME;
+                        TTrace((stderr,
+                                "  UX access time range error: skip time!\n"));
+                    } else {
+                        z_utim->atime = (time_t)i_time;
+                    }
+#endif /* ?TIME_T_TYPE_DOUBLE */
+                }
+#ifdef IZ_HAVE_UXUIDGID
+                if (eb_len >= EB_UX_FULLSIZE && z_uidgid != NULL) {
+                    z_uidgid[0] = makeword((EB_HEADSIZE+EB_UX_UID) + ef_buf);
+                    z_uidgid[1] = makeword((EB_HEADSIZE+EB_UX_GID) + ef_buf);
+                    flags |= EB_UX2_VALID;
+                }
+#endif /* IZ_HAVE_UXUIDGID */
+            }
+            break;
+
+          default:
+            break;
+        }
+
+        /* Skip this extra field block */
+        ef_buf += (eb_len + EB_HEADSIZE);
+        ef_len -= (eb_len + EB_HEADSIZE);
+    }
+
+    return flags;
+}
+
+#endif /* USE_EF_UT_TIME */
+
+
+#if (defined(RISCOS) || defined(ACORN_FTYPE_NFS))
+
+#define SPARKID_2 0x30435241    /* = "ARC0" */
+
+/*******************************/
+/* Function getRISCOSexfield() */
+/*******************************/
+
+zvoid *getRISCOSexfield(ef_buf, ef_len)
+    ZCONST uch *ef_buf; /* buffer containing extra field */
+    unsigned ef_len;    /* total length of extra field */
+{
+    unsigned eb_id;
+    unsigned eb_len;
+
+/*---------------------------------------------------------------------------
+    This function scans the extra field for a Acorn SPARK filetype ef-block.
+    If a valid block is found, the function returns a pointer to the start
+    of the SPARK_EF block in the extra field buffer.  Otherwise, a NULL
+    pointer is returned.
+  ---------------------------------------------------------------------------*/
+
+    if (ef_len == 0 || ef_buf == NULL)
+        return NULL;
+
+    Trace((stderr,"\ngetRISCOSexfield: scanning extra field of length %u\n",
+      ef_len));
+
+    while (ef_len >= EB_HEADSIZE) {
+        eb_id = makeword(EB_ID + ef_buf);
+        eb_len = makeword(EB_LEN + ef_buf);
+
+        if (eb_len > (ef_len - EB_HEADSIZE)) {
+            /* discovered some extra field inconsistency! */
+            Trace((stderr,
+              "getRISCOSexfield: block length %u > rest ef_size %u\n", eb_len,
+              ef_len - EB_HEADSIZE));
+            break;
+        }
+
+        if (eb_id == EF_SPARK && (eb_len == 24 || eb_len == 20)) {
+            if (makelong(EB_HEADSIZE + ef_buf) == SPARKID_2) {
+                /* Return a pointer to the valid SPARK filetype ef block */
+                return (zvoid *)ef_buf;
+            }
+        }
+
+        /* Skip this extra field block */
+        ef_buf += (eb_len + EB_HEADSIZE);
+        ef_len -= (eb_len + EB_HEADSIZE);
+    }
+
+    return NULL;
+}
+
+#endif /* (RISCOS || ACORN_FTYPE_NFS) */
diff -Naur a/unix/configure b/unix/configure
--- a/unix/configure	2009-04-16 20:25:12.000000000 +0100
+++ b/unix/configure	2019-12-01 23:54:04.330597267 +0000
@@ -17,7 +17,7 @@
 IZ_BZIP2=${3}
 CFLAGS="${CFLAGS} -I. -DUNIX"
 LFLAGS1=""
-LFLAGS2="-s"
+LFLAGS2="${LFLAGS2}"
 LN="ln -s"
 
 CFLAGS_OPT=''
@@ -640,7 +640,24 @@
     D_USE_BZ2="-DUSE_BZIP2"
     L_BZ2="${BZLF} -lbz2"
   else
-    echo "-- bzip2 sources not found - no bzip2 support"
+    echo "  Check if OS already has bzip2 library installed"
+    cat > conftest.c << _EOF_
+#include "bzlib.h"
+int main()
+{
+  bz_stream strm;
+  BZ2_bzCompressEnd(&strm);
+  return 0;
+}
+_EOF_
+    $CC $CFLAGS -o conftest conftest.c -lbz2 > /dev/null 2>/dev/null
+    if test $? -eq 0; then
+      echo "-- OS supports bzip2 - linking in bzip2"
+      D_USE_BZ2="-DUSE_BZIP2"
+      L_BZ2="${BZLF} -lbz2"
+    else
+      echo "-- Either bzlib.h or libbz2.a not found - no bzip2"
+    fi
   fi
 fi
 
diff -Naur a/unix/unix.c b/unix/unix.c
--- a/unix/unix.c	2009-01-23 23:31:26.000000000 +0000
+++ b/unix/unix.c	2019-12-02 01:49:39.894641004 +0000
@@ -30,6 +30,9 @@
 #define UNZIP_INTERNAL
 #include "unzip.h"
 
+#include <iconv.h>
+#include <langinfo.h>
+
 #ifdef SCO_XENIX
 #  define SYSNDIR
 #else  /* SCO Unix, AIX, DNIX, TI SysV, Coherent 4.x, ... */
@@ -1096,10 +1099,41 @@
 #ifndef MTS
 
 /****************************/
+/* Function CloseError()    */
+/***************************/
+
+int CloseError(__G)
+    __GDEF
+{
+    int errval = PK_OK;
+    
+    if (fclose(G.outfile) < 0) {
+          switch (errno) {
+                case ENOSPC:
+                    /* Do we need this on fileio.c? */
+                    Info(slide, 0x4a1, ((char *)slide, "%s: write error (disk full?).   Continue? (y/n/^C) ",
+                          FnFilter1(G.filename)));
+                    fgets(G.answerbuf, 9, stdin);
+                    if (*G.answerbuf == 'y')     /* stop writing to this file */
+                        G.disk_full = 1;         /* pass to next */
+                    else
+                        G.disk_full = 2;         /* no: exit program */
+          
+                    errval = PK_DISK;
+                    break;
+
+                default:
+                    errval = PK_WARN;
+          }
+     }
+     return errval;
+} /* End of CloseError() */
+
+/****************************/
 /* Function close_outfile() */
 /****************************/
 
-void close_outfile(__G)    /* GRR: change to return PK-style warning level */
+int close_outfile(__G) 
     __GDEF
 {
     union {
@@ -1108,6 +1142,7 @@
     } zt;
     ulg z_uidgid[2];
     int have_uidgid_flg;
+    int errval = PK_OK;
 
     have_uidgid_flg = get_extattribs(__G__ &(zt.t3), z_uidgid);
 
@@ -1141,16 +1176,16 @@
             Info(slide, 0x201, ((char *)slide,
               "warning:  symbolic link (%s) failed: mem alloc overflow\n",
               FnFilter1(G.filename)));
-            fclose(G.outfile);
-            return;
+            errval = CloseError(G.outfile, G.filename);
+            return errval ? errval : PK_WARN;
         }
 
         if ((slnk_entry = (slinkentry *)malloc(slnk_entrysize)) == NULL) {
             Info(slide, 0x201, ((char *)slide,
               "warning:  symbolic link (%s) failed: no mem\n",
               FnFilter1(G.filename)));
-            fclose(G.outfile);
-            return;
+            errval = CloseError(G.outfile, G.filename);
+            return errval ? errval : PK_WARN;
         }
         slnk_entry->next = NULL;
         slnk_entry->targetlen = ucsize;
@@ -1174,10 +1209,10 @@
               "warning:  symbolic link (%s) failed\n",
               FnFilter1(G.filename)));
             free(slnk_entry);
-            fclose(G.outfile);
-            return;
+            errval = CloseError(G.outfile, G.filename);
+            return errval ? errval : PK_WARN;
         }
-        fclose(G.outfile);                  /* close "link" file for good... */
+        errval = CloseError(G.outfile, G.filename); /* close "link" file for good... */
         slnk_entry->target[ucsize] = '\0';
         if (QCOND2)
             Info(slide, 0, ((char *)slide, "-> %s ",
@@ -1188,7 +1223,7 @@
         else
             G.slink_head = slnk_entry;
         G.slink_last = slnk_entry;
-        return;
+        return errval;
     }
 #endif /* SYMLINKS */
 
@@ -1201,7 +1236,7 @@
 #endif
 
 #if (defined(NO_FCHOWN))
-    fclose(G.outfile);
+    errval = CloseError(G.outfile, G.filename);
 #endif
 
     /* if -X option was specified and we have UID/GID info, restore it */
@@ -1227,7 +1262,7 @@
     }
 
 #if (!defined(NO_FCHOWN) && defined(NO_FCHMOD))
-    fclose(G.outfile);
+    errval = CloseError(G.outfile, G.filename);
 #endif
 
 #if (!defined(NO_FCHOWN) && !defined(NO_FCHMOD))
@@ -1239,7 +1274,7 @@
     if (fchmod(fileno(G.outfile), filtattr(__G__ G.pInfo->file_attr)))
         perror("fchmod (file attributes) error");
 
-    fclose(G.outfile);
+    errval = CloseError(G.outfile, G.filename);
 #endif /* !NO_FCHOWN && !NO_FCHMOD */
 
     /* skip restoring time stamps on user's request */
@@ -1267,6 +1302,7 @@
 #endif
 #endif /* NO_FCHOWN || NO_FCHMOD */
 
+    return errval;
 } /* end function close_outfile() */
 
 #endif /* !MTS */
@@ -1874,3 +1910,104 @@
     }
 }
 #endif /* QLZIP */
+
+
+typedef struct {
+    char *local_charset;
+    char *archive_charset;
+} CHARSET_MAP;
+
+/* A mapping of local <-> archive charsets used by default to convert filenames
+ * of DOS/Windows Zip archives. Currently very basic. */
+static CHARSET_MAP dos_charset_map[] = {
+    { "ANSI_X3.4-1968", "CP850" },
+    { "ISO-8859-1", "CP850" },
+    { "CP1252", "CP850" },
+    { "UTF-8", "CP866" },
+    { "KOI8-R", "CP866" },
+    { "KOI8-U", "CP866" },
+    { "ISO-8859-5", "CP866" }
+};
+
+char OEM_CP[MAX_CP_NAME] = "";
+char ISO_CP[MAX_CP_NAME] = "";
+
+/* Try to guess the default value of OEM_CP based on the current locale.
+ * ISO_CP is left alone for now. */
+void init_conversion_charsets()
+{
+    const char *local_charset;
+    int i;
+
+    /* Make a guess only if OEM_CP not already set. */ 
+    if(*OEM_CP == '\0') {
+    	local_charset = nl_langinfo(CODESET);
+    	for(i = 0; i < sizeof(dos_charset_map)/sizeof(CHARSET_MAP); i++)
+    		if(!strcasecmp(local_charset, dos_charset_map[i].local_charset)) {
+    			strncpy(OEM_CP, dos_charset_map[i].archive_charset,
+    					MAX_CP_NAME - 1);
+
+			OEM_CP[MAX_CP_NAME - 1] = '\0';
+    			break;
+    		}
+    }
+}
+
+/* Convert a string from one encoding to the current locale using iconv().
+ * Be as non-intrusive as possible. If error is encountered during covertion
+ * just leave the string intact. */
+static void charset_to_intern(char *string, char *from_charset)
+{
+    iconv_t cd;
+    char *s,*d, *buf;
+    size_t slen, dlen, buflen;
+    const char *local_charset;
+
+    if(*from_charset == '\0')
+    	return;
+
+    buf = NULL;
+    local_charset = nl_langinfo(CODESET);
+
+    if((cd = iconv_open(local_charset, from_charset)) == (iconv_t)-1)
+        return;
+
+    slen = strlen(string);
+    s = string;
+
+    /*  Make sure OUTBUFSIZ + 1 never ends up smaller than FILNAMSIZ
+     *  as this function also gets called with G.outbuf in fileio.c
+     */
+    buflen = FILNAMSIZ;
+    if (OUTBUFSIZ + 1 < FILNAMSIZ)
+    {
+        buflen = OUTBUFSIZ + 1;
+    }
+
+    d = buf = malloc(buflen);
+    if(!d)
+    	goto cleanup;
+
+    bzero(buf,buflen);
+    dlen = buflen - 1;
+
+    if(iconv(cd, &s, &slen, &d, &dlen) == (size_t)-1)
+    	goto cleanup;
+    strncpy(string, buf, buflen);
+
+    cleanup:
+    free(buf);
+    iconv_close(cd);
+}
+
+/* Convert a string from OEM_CP to the current locale charset. */
+inline void oem_intern(char *string)
+{
+    charset_to_intern(string, OEM_CP);
+}
+
+/* Convert a string from ISO_CP to the current locale charset. */
+inline void iso_intern(char *string)
+{
+    charset_to_intern(string, ISO_CP);
+}
diff -Naur a/unix/unix.c.orig b/unix/unix.c.orig
--- a/unix/unix.c.orig	1970-01-01 01:00:00.000000000 +0100
+++ b/unix/unix.c.orig	2019-12-01 23:53:08.675401658 +0000
@@ -0,0 +1,1909 @@
+/*
+  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
+*/
+/*---------------------------------------------------------------------------
+
+  unix.c
+
+  Unix-specific routines for use with Info-ZIP's UnZip 5.41 and later.
+
+  Contains:  readdir()
+             do_wild()           <-- generic enough to put in fileio.c?
+             mapattr()
+             mapname()
+             checkdir()
+             mkdir()
+             close_outfile()
+             defer_dir_attribs()
+             set_direc_attribs()
+             stamp_file()
+             version()
+
+  ---------------------------------------------------------------------------*/
+
+
+#define UNZIP_INTERNAL
+#include "unzip.h"
+
+#ifdef SCO_XENIX
+#  define SYSNDIR
+#else  /* SCO Unix, AIX, DNIX, TI SysV, Coherent 4.x, ... */
+#  if defined(__convexc__) || defined(SYSV) || defined(CRAY) || defined(BSD4_4)
+#    define DIRENT
+#  endif
+#endif
+#if defined(_AIX) || defined(__mpexl)
+#  define DIRENT
+#endif
+#ifdef COHERENT
+#  if defined(_I386) || (defined(__COHERENT__) && (__COHERENT__ >= 0x420))
+#    define DIRENT
+#  endif
+#endif
+
+#ifdef _POSIX_VERSION
+#  ifndef DIRENT
+#    define DIRENT
+#  endif
+#endif
+
+#ifdef DIRENT
+#  include <dirent.h>
+#else
+#  ifdef SYSV
+#    ifdef SYSNDIR
+#      include <sys/ndir.h>
+#    else
+#      include <ndir.h>
+#    endif
+#  else /* !SYSV */
+#    ifndef NO_SYSDIR
+#      include <sys/dir.h>
+#    endif
+#  endif /* ?SYSV */
+#  ifndef dirent
+#    define dirent direct
+#  endif
+#endif /* ?DIRENT */
+
+#ifdef SET_DIR_ATTRIB
+typedef struct uxdirattr {      /* struct for holding unix style directory */
+    struct uxdirattr *next;     /*  info until can be sorted and set at end */
+    char *fn;                   /* filename of directory */
+    union {
+        iztimes t3;             /* mtime, atime, ctime */
+        ztimbuf t2;             /* modtime, actime */
+    } u;
+    unsigned perms;             /* same as min_info.file_attr */
+    int have_uidgid;            /* flag */
+    ulg uidgid[2];
+    char fnbuf[1];              /* buffer stub for directory name */
+} uxdirattr;
+#define UxAtt(d)  ((uxdirattr *)d)    /* typecast shortcut */
+#endif /* SET_DIR_ATTRIB */
+
+#ifdef ACORN_FTYPE_NFS
+/* Acorn bits for NFS filetyping */
+typedef struct {
+  uch ID[2];
+  uch size[2];
+  uch ID_2[4];
+  uch loadaddr[4];
+  uch execaddr[4];
+  uch attr[4];
+} RO_extra_block;
+
+#endif /* ACORN_FTYPE_NFS */
+
+/* static int created_dir;      */      /* used in mapname(), checkdir() */
+/* static int renamed_fullpath; */      /* ditto */
+
+static unsigned filtattr OF((__GPRO__ unsigned perms));
+
+
+/*****************************/
+/* Strings used multiple     */
+/* times in unix.c           */
+/*****************************/
+
+#ifndef MTS
+/* messages of code for setting file/directory attributes */
+static ZCONST char CannotSetItemUidGid[] =
+  "warning:  cannot set UID %lu and/or GID %lu for %s\n          %s\n";
+static ZCONST char CannotSetUidGid[] =
+  " (warning) cannot set UID %lu and/or GID %lu\n          %s";
+static ZCONST char CannotSetItemTimestamps[] =
+  "warning:  cannot set modif./access times for %s\n          %s\n";
+static ZCONST char CannotSetTimestamps[] =
+  " (warning) cannot set modif./access times\n          %s";
+#endif /* !MTS */
+
+
+#ifndef SFX
+#ifdef NO_DIR                  /* for AT&T 3B1 */
+
+#define opendir(path) fopen(path,"r")
+#define closedir(dir) fclose(dir)
+typedef FILE DIR;
+typedef struct zdir {
+    FILE *dirhandle;
+    struct dirent *entry;
+} DIR
+DIR *opendir OF((ZCONST char *dirspec));
+void closedir OF((DIR *dirp));
+struct dirent *readdir OF((DIR *dirp));
+
+DIR *opendir(dirspec)
+    ZCONST char *dirspec;
+{
+    DIR *dirp;
+
+    if ((dirp = malloc(sizeof(DIR)) != NULL) {
+        if ((dirp->dirhandle = fopen(dirspec, "r")) == NULL) {
+            free(dirp);
+            dirp = NULL;
+        }
+    }
+    return dirp;
+}
+
+void closedir(dirp)
+    DIR *dirp;
+{
+    fclose(dirp->dirhandle);
+    free(dirp);
+}
+
+/*
+ *  Apparently originally by Rich Salz.
+ *  Cleaned up and modified by James W. Birdsall.
+ */
+struct dirent *readdir(dirp)
+    DIR *dirp;
+{
+
+    if (dirp == NULL)
+        return NULL;
+
+    for (;;)
+        if (fread(&(dirp->entry), sizeof (struct dirent), 1,
+                  dirp->dirhandle) == 0)
+            return (struct dirent *)NULL;
+        else if ((dirp->entry).d_ino)
+            return &(dirp->entry);
+
+} /* end function readdir() */
+
+#endif /* NO_DIR */
+
+
+/**********************/
+/* Function do_wild() */   /* for porting: dir separator; match(ignore_case) */
+/**********************/
+
+char *do_wild(__G__ wildspec)
+    __GDEF
+    ZCONST char *wildspec;  /* only used first time on a given dir */
+{
+/* these statics are now declared in SYSTEM_SPECIFIC_GLOBALS in unxcfg.h:
+    static DIR *wild_dir = (DIR *)NULL;
+    static ZCONST char *wildname;
+    static char *dirname, matchname[FILNAMSIZ];
+    static int notfirstcall=FALSE, have_dirname, dirnamelen;
+*/
+    struct dirent *file;
+
+    /* Even when we're just returning wildspec, we *always* do so in
+     * matchname[]--calling routine is allowed to append four characters
+     * to the returned string, and wildspec may be a pointer to argv[].
+     */
+    if (!G.notfirstcall) {  /* first call:  must initialize everything */
+        G.notfirstcall = TRUE;
+
+        if (!iswild(wildspec)) {
+            strncpy(G.matchname, wildspec, FILNAMSIZ);
+            G.matchname[FILNAMSIZ-1] = '\0';
+            G.have_dirname = FALSE;
+            G.wild_dir = NULL;
+            return G.matchname;
+        }
+
+        /* break the wildspec into a directory part and a wildcard filename */
+        if ((G.wildname = (ZCONST char *)strrchr(wildspec, '/')) == NULL) {
+            G.dirname = ".";
+            G.dirnamelen = 1;
+            G.have_dirname = FALSE;
+            G.wildname = wildspec;
+        } else {
+            ++G.wildname;     /* point at character after '/' */
+            G.dirnamelen = G.wildname - wildspec;
+            if ((G.dirname = (char *)malloc(G.dirnamelen+1)) == (char *)NULL) {
+                Info(slide, 0x201, ((char *)slide,
+                  "warning:  cannot allocate wildcard buffers\n"));
+                strncpy(G.matchname, wildspec, FILNAMSIZ);
+                G.matchname[FILNAMSIZ-1] = '\0';
+                return G.matchname; /* but maybe filespec was not a wildcard */
+            }
+            strncpy(G.dirname, wildspec, G.dirnamelen);
+            G.dirname[G.dirnamelen] = '\0';   /* terminate for strcpy below */
+            G.have_dirname = TRUE;
+        }
+
+        if ((G.wild_dir = (zvoid *)opendir(G.dirname)) != (zvoid *)NULL) {
+            while ((file = readdir((DIR *)G.wild_dir)) !=
+                   (struct dirent *)NULL) {
+                Trace((stderr, "do_wild:  readdir returns %s\n",
+                  FnFilter1(file->d_name)));
+                if (file->d_name[0] == '.' && G.wildname[0] != '.')
+                    continue; /* Unix:  '*' and '?' do not match leading dot */
+                if (match(file->d_name, G.wildname, 0 WISEP) &&/*0=case sens.*/
+                    /* skip "." and ".." directory entries */
+                    strcmp(file->d_name, ".") && strcmp(file->d_name, "..")) {
+                    Trace((stderr, "do_wild:  match() succeeds\n"));
+                    if (G.have_dirname) {
+                        strcpy(G.matchname, G.dirname);
+                        strcpy(G.matchname+G.dirnamelen, file->d_name);
+                    } else
+                        strcpy(G.matchname, file->d_name);
+                    return G.matchname;
+                }
+            }
+            /* if we get to here directory is exhausted, so close it */
+            closedir((DIR *)G.wild_dir);
+            G.wild_dir = (zvoid *)NULL;
+        }
+        Trace((stderr, "do_wild:  opendir(%s) returns NULL\n",
+          FnFilter1(G.dirname)));
+
+        /* return the raw wildspec in case that works (e.g., directory not
+         * searchable, but filespec was not wild and file is readable) */
+        strncpy(G.matchname, wildspec, FILNAMSIZ);
+        G.matchname[FILNAMSIZ-1] = '\0';
+        return G.matchname;
+    }
+
+    /* last time through, might have failed opendir but returned raw wildspec */
+    if ((DIR *)G.wild_dir == (DIR *)NULL) {
+        G.notfirstcall = FALSE; /* nothing left--reset for new wildspec */
+        if (G.have_dirname)
+            free(G.dirname);
+        return (char *)NULL;
+    }
+
+    /* If we've gotten this far, we've read and matched at least one entry
+     * successfully (in a previous call), so dirname has been copied into
+     * matchname already.
+     */
+    while ((file = readdir((DIR *)G.wild_dir)) != (struct dirent *)NULL) {
+        Trace((stderr, "do_wild:  readdir returns %s\n",
+          FnFilter1(file->d_name)));
+        if (file->d_name[0] == '.' && G.wildname[0] != '.')
+            continue;   /* Unix:  '*' and '?' do not match leading dot */
+        if (match(file->d_name, G.wildname, 0 WISEP)) { /* 0 == case sens. */
+            Trace((stderr, "do_wild:  match() succeeds\n"));
+            if (G.have_dirname) {
+                /* strcpy(G.matchname, G.dirname); */
+                strcpy(G.matchname+G.dirnamelen, file->d_name);
+            } else
+                strcpy(G.matchname, file->d_name);
+            return G.matchname;
+        }
+    }
+
+    closedir((DIR *)G.wild_dir);  /* at least one entry read; nothing left */
+    G.wild_dir = (zvoid *)NULL;
+    G.notfirstcall = FALSE;       /* reset for new wildspec */
+    if (G.have_dirname)
+        free(G.dirname);
+    return (char *)NULL;
+
+} /* end function do_wild() */
+
+#endif /* !SFX */
+
+
+
+
+#ifndef S_ISUID
+# define S_ISUID        0004000 /* set user id on execution */
+#endif
+#ifndef S_ISGID
+# define S_ISGID        0002000 /* set group id on execution */
+#endif
+#ifndef S_ISVTX
+# define S_ISVTX        0001000 /* save swapped text even after use */
+#endif
+
+/************************/
+/*  Function filtattr() */
+/************************/
+/* This is used to clear or keep the SUID and SGID bits on file permissions.
+ * It's possible that a file in an archive could have one of these bits set
+ * and, unknown to the person unzipping, could allow others to execute the
+ * file as the user or group.  The new option -K bypasses this check.
+ */
+
+static unsigned filtattr(__G__ perms)
+    __GDEF
+    unsigned perms;
+{
+    /* keep setuid/setgid/tacky perms? */
+    if (!uO.K_flag)
+        perms &= ~(S_ISUID | S_ISGID | S_ISVTX);
+
+    return (0xffff & perms);
+} /* end function filtattr() */
+
+
+
+
+
+/**********************/
+/* Function mapattr() */
+/**********************/
+
+int mapattr(__G)
+    __GDEF
+{
+    int r;
+    ulg tmp = G.crec.external_file_attributes;
+
+    G.pInfo->file_attr = 0;
+    /* initialized to 0 for check in "default" branch below... */
+
+    switch (G.pInfo->hostnum) {
+        case AMIGA_:
+            tmp = (unsigned)(tmp>>17 & 7);   /* Amiga RWE bits */
+            G.pInfo->file_attr = (unsigned)(tmp<<6 | tmp<<3 | tmp);
+            break;
+        case THEOS_:
+            tmp &= 0xF1FFFFFFL;
+            if ((tmp & 0xF0000000L) != 0x40000000L)
+                tmp &= 0x01FFFFFFL;     /* not a dir, mask all ftype bits */
+            else
+                tmp &= 0x41FFFFFFL;     /* leave directory bit as set */
+            /* fall through! */
+        case UNIX_:
+        case VMS_:
+        case ACORN_:
+        case ATARI_:
+        case ATHEOS_:
+        case BEOS_:
+        case QDOS_:
+        case TANDEM_:
+            r = FALSE;
+            G.pInfo->file_attr = (unsigned)(tmp >> 16);
+            if (G.pInfo->file_attr == 0 && G.extra_field) {
+                /* Some (non-Info-ZIP) implementations of Zip for Unix and
+                 * VMS (and probably others ??) leave 0 in the upper 16-bit
+                 * part of the external_file_attributes field. Instead, they
+                 * store file permission attributes in some extra field.
+                 * As a work-around, we search for the presence of one of
+                 * these extra fields and fall back to the MSDOS compatible
+                 * part of external_file_attributes if one of the known
+                 * e.f. types has been detected.
+                 * Later, we might implement extraction of the permission
+                 * bits from the VMS extra field. But for now, the work-around
+                 * should be sufficient to provide "readable" extracted files.
+                 * (For ASI Unix e.f., an experimental remap of the e.f.
+                 * mode value IS already provided!)
+                 */
+                ush ebID;
+                unsigned ebLen;
+                uch *ef = G.extra_field;
+                unsigned ef_len = G.crec.extra_field_length;
+
+                while (!r && ef_len >= EB_HEADSIZE) {
+                    ebID = makeword(ef);
+                    ebLen = (unsigned)makeword(ef+EB_LEN);
+                    if (ebLen > (ef_len - EB_HEADSIZE))
+                        /* discoverd some e.f. inconsistency! */
+                        break;
+                    switch (ebID) {
+                      case EF_ASIUNIX:
+                        if (ebLen >= (EB_ASI_MODE+2)) {
+                            G.pInfo->file_attr =
+                              (unsigned)makeword(ef+(EB_HEADSIZE+EB_ASI_MODE));
+                            /* force stop of loop: */
+                            ef_len = (ebLen + EB_HEADSIZE);
+                            break;
+                        }
+                        /* else: fall through! */
+                      case EF_PKVMS:
+                        /* "found nondecypherable e.f. with perm. attr" */
+                        r = TRUE;
+                      default:
+                        break;
+                    }
+                    ef_len -= (ebLen + EB_HEADSIZE);
+                    ef += (ebLen + EB_HEADSIZE);
+                }
+            }
+            if (!r) {
+#ifdef SYMLINKS
+                /* Check if the file is a (POSIX-compatible) symbolic link.
+                 * We restrict symlink support to those "made-by" hosts that
+                 * are known to support symbolic links.
+                 */
+                G.pInfo->symlink = S_ISLNK(G.pInfo->file_attr) &&
+                                   SYMLINK_HOST(G.pInfo->hostnum);
+#endif
+                return 0;
+            }
+            /* fall through! */
+        /* all remaining cases:  expand MSDOS read-only bit into write perms */
+        case FS_FAT_:
+            /* PKWARE's PKZip for Unix marks entries as FS_FAT_, but stores the
+             * Unix attributes in the upper 16 bits of the external attributes
+             * field, just like Info-ZIP's Zip for Unix.  We try to use that
+             * value, after a check for consistency with the MSDOS attribute
+             * bits (see below).
+             */
+            G.pInfo->file_attr = (unsigned)(tmp >> 16);
+            /* fall through! */
+        case FS_HPFS_:
+        case FS_NTFS_:
+        case MAC_:
+        case TOPS20_:
+        default:
+            /* Ensure that DOS subdir bit is set when the entry's name ends
+             * in a '/'.  Some third-party Zip programs fail to set the subdir
+             * bit for directory entries.
+             */
+            if ((tmp & 0x10) == 0) {
+                extent fnlen = strlen(G.filename);
+                if (fnlen > 0 && G.filename[fnlen-1] == '/')
+                    tmp |= 0x10;
+            }
+            /* read-only bit --> write perms; subdir bit --> dir exec bit */
+            tmp = !(tmp & 1) << 1  |  (tmp & 0x10) >> 4;
+            if ((G.pInfo->file_attr & 0700) == (unsigned)(0400 | tmp<<6)) {
+                /* keep previous G.pInfo->file_attr setting, when its "owner"
+                 * part appears to be consistent with DOS attribute flags!
+                 */
+#ifdef SYMLINKS
+                /* Entries "made by FS_FAT_" could have been zipped on a
+                 * system that supports POSIX-style symbolic links.
+                 */
+                G.pInfo->symlink = S_ISLNK(G.pInfo->file_attr) &&
+                                   (G.pInfo->hostnum == FS_FAT_);
+#endif
+                return 0;
+            }
+            G.pInfo->file_attr = (unsigned)(0444 | tmp<<6 | tmp<<3 | tmp);
+            break;
+    } /* end switch (host-OS-created-by) */
+
+    /* for originating systems with no concept of "group," "other," "system": */
+    umask( (int)(tmp=umask(0)) );    /* apply mask to expanded r/w(/x) perms */
+    G.pInfo->file_attr &= ~tmp;
+
+    return 0;
+
+} /* end function mapattr() */
+
+
+
+
+
+/************************/
+/*  Function mapname()  */
+/************************/
+
+int mapname(__G__ renamed)
+    __GDEF
+    int renamed;
+/*
+ * returns:
+ *  MPN_OK          - no problem detected
+ *  MPN_INF_TRUNC   - caution (truncated filename)
+ *  MPN_INF_SKIP    - info "skip entry" (dir doesn't exist)
+ *  MPN_ERR_SKIP    - error -> skip entry
+ *  MPN_ERR_TOOLONG - error -> path is too long
+ *  MPN_NOMEM       - error (memory allocation failed) -> skip entry
+ *  [also MPN_VOL_LABEL, MPN_CREATED_DIR]
+ */
+{
+    char pathcomp[FILNAMSIZ];      /* path-component buffer */
+    char *pp, *cp=(char *)NULL;    /* character pointers */
+    char *lastsemi=(char *)NULL;   /* pointer to last semi-colon in pathcomp */
+#ifdef ACORN_FTYPE_NFS
+    char *lastcomma=(char *)NULL;  /* pointer to last comma in pathcomp */
+    RO_extra_block *ef_spark;      /* pointer Acorn FTYPE ef block */
+#endif
+    int killed_ddot = FALSE;       /* is set when skipping "../" pathcomp */
+    int error = MPN_OK;
+    register unsigned workch;      /* hold the character being tested */
+
+
+/*---------------------------------------------------------------------------
+    Initialize various pointers and counters and stuff.
+  ---------------------------------------------------------------------------*/
+
+    if (G.pInfo->vollabel)
+        return MPN_VOL_LABEL;   /* can't set disk volume labels in Unix */
+
+    /* can create path as long as not just freshening, or if user told us */
+    G.create_dirs = (!uO.fflag || renamed);
+
+    G.created_dir = FALSE;      /* not yet */
+
+    /* user gave full pathname:  don't prepend rootpath */
+    G.renamed_fullpath = (renamed && (*G.filename == '/'));
+
+    if (checkdir(__G__ (char *)NULL, INIT) == MPN_NOMEM)
+        return MPN_NOMEM;       /* initialize path buffer, unless no memory */
+
+    *pathcomp = '\0';           /* initialize translation buffer */
+    pp = pathcomp;              /* point to translation buffer */
+    if (uO.jflag)               /* junking directories */
+        cp = (char *)strrchr(G.filename, '/');
+    if (cp == (char *)NULL)     /* no '/' or not junking dirs */
+        cp = G.filename;        /* point to internal zipfile-member pathname */
+    else
+        ++cp;                   /* point to start of last component of path */
+
+/*---------------------------------------------------------------------------
+    Begin main loop through characters in filename.
+  ---------------------------------------------------------------------------*/
+
+    while ((workch = (uch)*cp++) != 0) {
+
+        switch (workch) {
+            case '/':             /* can assume -j flag not given */
+                *pp = '\0';
+                if (strcmp(pathcomp, ".") == 0) {
+                    /* don't bother appending "./" to the path */
+                    *pathcomp = '\0';
+                } else if (!uO.ddotflag && strcmp(pathcomp, "..") == 0) {
+                    /* "../" dir traversal detected, skip over it */
+                    *pathcomp = '\0';
+                    killed_ddot = TRUE;     /* set "show message" flag */
+                }
+                /* when path component is not empty, append it now */
+                if (*pathcomp != '\0' &&
+                    ((error = checkdir(__G__ pathcomp, APPEND_DIR))
+                     & MPN_MASK) > MPN_INF_TRUNC)
+                    return error;
+                pp = pathcomp;    /* reset conversion buffer for next piece */
+                lastsemi = (char *)NULL; /* leave direct. semi-colons alone */
+                break;
+
+#ifdef __CYGWIN__   /* Cygwin runs on Win32, apply FAT/NTFS filename rules */
+            case ':':         /* drive spec not stored, so no colon allowed */
+            case '\\':        /* '\\' may come as normal filename char (not */
+            case '<':         /*  dir sep char!) from unix-like file system */
+            case '>':         /* no redirection symbols allowed either */
+            case '|':         /* no pipe signs allowed */
+            case '"':         /* no double quotes allowed */
+            case '?':         /* no wildcards allowed */
+            case '*':
+                *pp++ = '_';  /* these rules apply equally to FAT and NTFS */
+                break;
+#endif
+
+            case ';':             /* VMS version (or DEC-20 attrib?) */
+                lastsemi = pp;
+                *pp++ = ';';      /* keep for now; remove VMS ";##" */
+                break;            /*  later, if requested */
+
+#ifdef ACORN_FTYPE_NFS
+            case ',':             /* NFS filetype extension */
+                lastcomma = pp;
+                *pp++ = ',';      /* keep for now; may need to remove */
+                break;            /*  later, if requested */
+#endif
+
+#ifdef MTS
+            case ' ':             /* change spaces to underscore under */
+                *pp++ = '_';      /*  MTS; leave as spaces under Unix */
+                break;
+#endif
+
+            default:
+                /* disable control character filter when requested,
+                 * else allow 8-bit characters (e.g. UTF-8) in filenames:
+                 */
+                if (uO.cflxflag ||
+                    (isprint(workch) || (128 <= workch && workch <= 254)))
+                    *pp++ = (char)workch;
+        } /* end switch */
+
+    } /* end while loop */
+
+    /* Show warning when stripping insecure "parent dir" path components */
+    if (killed_ddot && QCOND2) {
+        Info(slide, 0, ((char *)slide,
+          "warning:  skipped \"../\" path component(s) in %s\n",
+          FnFilter1(G.filename)));
+        if (!(error & ~MPN_MASK))
+            error = (error & MPN_MASK) | PK_WARN;
+    }
+
+/*---------------------------------------------------------------------------
+    Report if directory was created (and no file to create:  filename ended
+    in '/'), check name to be sure it exists, and combine path and name be-
+    fore exiting.
+  ---------------------------------------------------------------------------*/
+
+    if (G.filename[strlen(G.filename) - 1] == '/') {
+        checkdir(__G__ G.filename, GETPATH);
+        if (G.created_dir) {
+            if (QCOND2) {
+                Info(slide, 0, ((char *)slide, "   creating: %s\n",
+                  FnFilter1(G.filename)));
+            }
+#ifndef NO_CHMOD
+            /* Filter out security-relevant attributes bits. */
+            G.pInfo->file_attr = filtattr(__G__ G.pInfo->file_attr);
+            /* When extracting non-UNIX directories or when extracting
+             * without UID/GID restoration or SGID preservation, any
+             * SGID flag inherited from the parent directory should be
+             * maintained to allow files extracted into this new folder
+             * to inherit the GID setting from the parent directory.
+             */
+            if (G.pInfo->hostnum != UNIX_ || !(uO.X_flag || uO.K_flag)) {
+                /* preserve SGID bit when inherited from parent dir */
+                if (!SSTAT(G.filename, &G.statbuf)) {
+                    G.pInfo->file_attr |= G.statbuf.st_mode & S_ISGID;
+                } else {
+                    perror("Could not read directory attributes");
+                }
+            }
+
+            /* set approx. dir perms (make sure can still read/write in dir) */
+            if (chmod(G.filename, G.pInfo->file_attr | 0700))
+                perror("chmod (directory attributes) error");
+#endif
+            /* set dir time (note trailing '/') */
+            return (error & ~MPN_MASK) | MPN_CREATED_DIR;
+        }
+        /* dir existed already; don't look for data to extract */
+        return (error & ~MPN_MASK) | MPN_INF_SKIP;
+    }
+
+    *pp = '\0';                   /* done with pathcomp:  terminate it */
+
+    /* if not saving them, remove VMS version numbers (appended ";###") */
+    if (!uO.V_flag && lastsemi) {
+        pp = lastsemi + 1;
+        while (isdigit((uch)(*pp)))
+            ++pp;
+        if (*pp == '\0')          /* only digits between ';' and end:  nuke */
+            *lastsemi = '\0';
+    }
+
+    /* On UNIX (and compatible systems), "." and ".." are reserved for
+     * directory navigation and cannot be used as regular file names.
+     * These reserved one-dot and two-dot names are mapped to "_" and "__".
+     */
+    if (strcmp(pathcomp, ".") == 0)
+        *pathcomp = '_';
+    else if (strcmp(pathcomp, "..") == 0)
+        strcpy(pathcomp, "__");
+
+#ifdef ACORN_FTYPE_NFS
+    /* translate Acorn filetype information if asked to do so */
+    if (uO.acorn_nfs_ext &&
+        (ef_spark = (RO_extra_block *)
+                    getRISCOSexfield(G.extra_field, G.lrec.extra_field_length))
+        != (RO_extra_block *)NULL)
+    {
+        /* file *must* have a RISC OS extra field */
+        long ft = (long)makelong(ef_spark->loadaddr);
+        /*32-bit*/
+        if (lastcomma) {
+            pp = lastcomma + 1;
+            while (isxdigit((uch)(*pp))) ++pp;
+            if (pp == lastcomma+4 && *pp == '\0') *lastcomma='\0'; /* nuke */
+        }
+        if ((ft & 1<<31)==0) ft=0x000FFD00;
+        sprintf(pathcomp+strlen(pathcomp), ",%03x", (int)(ft>>8) & 0xFFF);
+    }
+#endif /* ACORN_FTYPE_NFS */
+
+    if (*pathcomp == '\0') {
+        Info(slide, 1, ((char *)slide, "mapname:  conversion of %s failed\n",
+          FnFilter1(G.filename)));
+        return (error & ~MPN_MASK) | MPN_ERR_SKIP;
+    }
+
+    checkdir(__G__ pathcomp, APPEND_NAME);  /* returns 1 if truncated: care? */
+    checkdir(__G__ G.filename, GETPATH);
+
+    return error;
+
+} /* end function mapname() */
+
+
+
+
+#if 0  /*========== NOTES ==========*/
+
+  extract-to dir:      a:path/
+  buildpath:           path1/path2/ ...   (NULL-terminated)
+  pathcomp:                filename
+
+  mapname():
+    loop over chars in zipfile member name
+      checkdir(path component, COMPONENT | CREATEDIR) --> map as required?
+        (d:/tmp/unzip/)                    (disk:[tmp.unzip.)
+        (d:/tmp/unzip/jj/)                 (disk:[tmp.unzip.jj.)
+        (d:/tmp/unzip/jj/temp/)            (disk:[tmp.unzip.jj.temp.)
+    finally add filename itself and check for existence? (could use with rename)
+        (d:/tmp/unzip/jj/temp/msg.outdir)  (disk:[tmp.unzip.jj.temp]msg.outdir)
+    checkdir(name, GETPATH)     -->  copy path to name and free space
+
+#endif /* 0 */
+
+
+
+
+/***********************/
+/* Function checkdir() */
+/***********************/
+
+int checkdir(__G__ pathcomp, flag)
+    __GDEF
+    char *pathcomp;
+    int flag;
+/*
+ * returns:
+ *  MPN_OK          - no problem detected
+ *  MPN_INF_TRUNC   - (on APPEND_NAME) truncated filename
+ *  MPN_INF_SKIP    - path doesn't exist, not allowed to create
+ *  MPN_ERR_SKIP    - path doesn't exist, tried to create and failed; or path
+ *                    exists and is not a directory, but is supposed to be
+ *  MPN_ERR_TOOLONG - path is too long
+ *  MPN_NOMEM       - can't allocate memory for filename buffers
+ */
+{
+ /* static int rootlen = 0; */  /* length of rootpath */
+ /* static char *rootpath;  */  /* user's "extract-to" directory */
+ /* static char *buildpath; */  /* full path (so far) to extracted file */
+ /* static char *end;       */  /* pointer to end of buildpath ('\0') */
+
+#   define FN_MASK   7
+#   define FUNCTION  (flag & FN_MASK)
+
+
+
+/*---------------------------------------------------------------------------
+    APPEND_DIR:  append the path component to the path being built and check
+    for its existence.  If doesn't exist and we are creating directories, do
+    so for this one; else signal success or error as appropriate.
+  ---------------------------------------------------------------------------*/
+
+    if (FUNCTION == APPEND_DIR) {
+        int too_long = FALSE;
+#ifdef SHORT_NAMES
+        char *old_end = end;
+#endif
+
+        Trace((stderr, "appending dir segment [%s]\n", FnFilter1(pathcomp)));
+        while ((*G.end = *pathcomp++) != '\0')
+            ++G.end;
+#ifdef SHORT_NAMES   /* path components restricted to 14 chars, typically */
+        if ((G.end-old_end) > FILENAME_MAX)  /* GRR:  proper constant? */
+            *(G.end = old_end + FILENAME_MAX) = '\0';
+#endif
+
+        /* GRR:  could do better check, see if overrunning buffer as we go:
+         * check end-buildpath after each append, set warning variable if
+         * within 20 of FILNAMSIZ; then if var set, do careful check when
+         * appending.  Clear variable when begin new path. */
+
+        /* next check: need to append '/', at least one-char name, '\0' */
+        if ((G.end-G.buildpath) > FILNAMSIZ-3)
+            too_long = TRUE;                    /* check if extracting dir? */
+        if (SSTAT(G.buildpath, &G.statbuf)) {   /* path doesn't exist */
+            if (!G.create_dirs) { /* told not to create (freshening) */
+                free(G.buildpath);
+                return MPN_INF_SKIP;    /* path doesn't exist: nothing to do */
+            }
+            if (too_long) {
+                Info(slide, 1, ((char *)slide,
+                  "checkdir error:  path too long: %s\n",
+                  FnFilter1(G.buildpath)));
+                free(G.buildpath);
+                /* no room for filenames:  fatal */
+                return MPN_ERR_TOOLONG;
+            }
+            if (mkdir(G.buildpath, 0777) == -1) {   /* create the directory */
+                Info(slide, 1, ((char *)slide,
+                  "checkdir error:  cannot create %s\n\
+                 %s\n\
+                 unable to process %s.\n",
+                  FnFilter2(G.buildpath),
+                  strerror(errno),
+                  FnFilter1(G.filename)));
+                free(G.buildpath);
+                /* path didn't exist, tried to create, failed */
+                return MPN_ERR_SKIP;
+            }
+            G.created_dir = TRUE;
+        } else if (!S_ISDIR(G.statbuf.st_mode)) {
+            Info(slide, 1, ((char *)slide,
+              "checkdir error:  %s exists but is not directory\n\
+                 unable to process %s.\n",
+              FnFilter2(G.buildpath), FnFilter1(G.filename)));
+            free(G.buildpath);
+            /* path existed but wasn't dir */
+            return MPN_ERR_SKIP;
+        }
+        if (too_long) {
+            Info(slide, 1, ((char *)slide,
+              "checkdir error:  path too long: %s\n", FnFilter1(G.buildpath)));
+            free(G.buildpath);
+            /* no room for filenames:  fatal */
+            return MPN_ERR_TOOLONG;
+        }
+        *G.end++ = '/';
+        *G.end = '\0';
+        Trace((stderr, "buildpath now = [%s]\n", FnFilter1(G.buildpath)));
+        return MPN_OK;
+
+    } /* end if (FUNCTION == APPEND_DIR) */
+
+/*---------------------------------------------------------------------------
+    GETPATH:  copy full path to the string pointed at by pathcomp, and free
+    G.buildpath.
+  ---------------------------------------------------------------------------*/
+
+    if (FUNCTION == GETPATH) {
+        strcpy(pathcomp, G.buildpath);
+        Trace((stderr, "getting and freeing path [%s]\n",
+          FnFilter1(pathcomp)));
+        free(G.buildpath);
+        G.buildpath = G.end = (char *)NULL;
+        return MPN_OK;
+    }
+
+/*---------------------------------------------------------------------------
+    APPEND_NAME:  assume the path component is the filename; append it and
+    return without checking for existence.
+  ---------------------------------------------------------------------------*/
+
+    if (FUNCTION == APPEND_NAME) {
+#ifdef SHORT_NAMES
+        char *old_end = end;
+#endif
+
+        Trace((stderr, "appending filename [%s]\n", FnFilter1(pathcomp)));
+        while ((*G.end = *pathcomp++) != '\0') {
+            ++G.end;
+#ifdef SHORT_NAMES  /* truncate name at 14 characters, typically */
+            if ((G.end-old_end) > FILENAME_MAX)    /* GRR:  proper constant? */
+                *(G.end = old_end + FILENAME_MAX) = '\0';
+#endif
+            if ((G.end-G.buildpath) >= FILNAMSIZ) {
+                *--G.end = '\0';
+                Info(slide, 0x201, ((char *)slide,
+                  "checkdir warning:  path too long; truncating\n\
+                   %s\n                -> %s\n",
+                  FnFilter1(G.filename), FnFilter2(G.buildpath)));
+                return MPN_INF_TRUNC;   /* filename truncated */
+            }
+        }
+        Trace((stderr, "buildpath now = [%s]\n", FnFilter1(G.buildpath)));
+        /* could check for existence here, prompt for new name... */
+        return MPN_OK;
+    }
+
+/*---------------------------------------------------------------------------
+    INIT:  allocate and initialize buffer space for the file currently being
+    extracted.  If file was renamed with an absolute path, don't prepend the
+    extract-to path.
+  ---------------------------------------------------------------------------*/
+
+/* GRR:  for VMS and TOPS-20, add up to 13 to strlen */
+
+    if (FUNCTION == INIT) {
+        Trace((stderr, "initializing buildpath to "));
+#ifdef ACORN_FTYPE_NFS
+        if ((G.buildpath = (char *)malloc(strlen(G.filename)+G.rootlen+
+                                          (uO.acorn_nfs_ext ? 5 : 1)))
+#else
+        if ((G.buildpath = (char *)malloc(strlen(G.filename)+G.rootlen+1))
+#endif
+            == (char *)NULL)
+            return MPN_NOMEM;
+        if ((G.rootlen > 0) && !G.renamed_fullpath) {
+            strcpy(G.buildpath, G.rootpath);
+            G.end = G.buildpath + G.rootlen;
+        } else {
+            *G.buildpath = '\0';
+            G.end = G.buildpath;
+        }
+        Trace((stderr, "[%s]\n", FnFilter1(G.buildpath)));
+        return MPN_OK;
+    }
+
+/*---------------------------------------------------------------------------
+    ROOT:  if appropriate, store the path in rootpath and create it if
+    necessary; else assume it's a zipfile member and return.  This path
+    segment gets used in extracting all members from every zipfile specified
+    on the command line.
+  ---------------------------------------------------------------------------*/
+
+#if (!defined(SFX) || defined(SFX_EXDIR))
+    if (FUNCTION == ROOT) {
+        Trace((stderr, "initializing root path to [%s]\n",
+          FnFilter1(pathcomp)));
+        if (pathcomp == (char *)NULL) {
+            G.rootlen = 0;
+            return MPN_OK;
+        }
+        if (G.rootlen > 0)      /* rootpath was already set, nothing to do */
+            return MPN_OK;
+        if ((G.rootlen = strlen(pathcomp)) > 0) {
+            char *tmproot;
+
+            if ((tmproot = (char *)malloc(G.rootlen+2)) == (char *)NULL) {
+                G.rootlen = 0;
+                return MPN_NOMEM;
+            }
+            strcpy(tmproot, pathcomp);
+            if (tmproot[G.rootlen-1] == '/') {
+                tmproot[--G.rootlen] = '\0';
+            }
+            if (G.rootlen > 0 && (SSTAT(tmproot, &G.statbuf) ||
+                                  !S_ISDIR(G.statbuf.st_mode)))
+            {   /* path does not exist */
+                if (!G.create_dirs /* || iswild(tmproot) */ ) {
+                    free(tmproot);
+                    G.rootlen = 0;
+                    /* skip (or treat as stored file) */
+                    return MPN_INF_SKIP;
+                }
+                /* create the directory (could add loop here scanning tmproot
+                 * to create more than one level, but why really necessary?) */
+                if (mkdir(tmproot, 0777) == -1) {
+                    Info(slide, 1, ((char *)slide,
+                      "checkdir:  cannot create extraction directory: %s\n\
+           %s\n",
+                      FnFilter1(tmproot), strerror(errno)));
+                    free(tmproot);
+                    G.rootlen = 0;
+                    /* path didn't exist, tried to create, and failed: */
+                    /* file exists, or 2+ subdir levels required */
+                    return MPN_ERR_SKIP;
+                }
+            }
+            tmproot[G.rootlen++] = '/';
+            tmproot[G.rootlen] = '\0';
+            if ((G.rootpath = (char *)realloc(tmproot, G.rootlen+1)) == NULL) {
+                free(tmproot);
+                G.rootlen = 0;
+                return MPN_NOMEM;
+            }
+            Trace((stderr, "rootpath now = [%s]\n", FnFilter1(G.rootpath)));
+        }
+        return MPN_OK;
+    }
+#endif /* !SFX || SFX_EXDIR */
+
+/*---------------------------------------------------------------------------
+    END:  free rootpath, immediately prior to program exit.
+  ---------------------------------------------------------------------------*/
+
+    if (FUNCTION == END) {
+        Trace((stderr, "freeing rootpath\n"));
+        if (G.rootlen > 0) {
+            free(G.rootpath);
+            G.rootlen = 0;
+        }
+        return MPN_OK;
+    }
+
+    return MPN_INVALID; /* should never reach */
+
+} /* end function checkdir() */
+
+
+
+
+
+#ifdef NO_MKDIR
+
+/********************/
+/* Function mkdir() */
+/********************/
+
+int mkdir(path, mode)
+    ZCONST char *path;
+    int mode;   /* ignored */
+/*
+ * returns:   0 - successful
+ *           -1 - failed (errno not set, however)
+ */
+{
+    char command[FILNAMSIZ+40]; /* buffer for system() call */
+
+    /* GRR 930416:  added single quotes around path to avoid bug with
+     * creating directories with ampersands in name; not yet tested */
+    sprintf(command, "IFS=\" \t\n\" /bin/mkdir '%s' 2>/dev/null", path);
+    if (system(command))
+        return -1;
+    return 0;
+}
+
+#endif /* NO_MKDIR */
+
+
+
+
+#if (!defined(MTS) || defined(SET_DIR_ATTRIB))
+static int get_extattribs OF((__GPRO__ iztimes *pzt, ulg z_uidgid[2]));
+
+static int get_extattribs(__G__ pzt, z_uidgid)
+    __GDEF
+    iztimes *pzt;
+    ulg z_uidgid[2];
+{
+/*---------------------------------------------------------------------------
+    Convert from MSDOS-format local time and date to Unix-format 32-bit GMT
+    time:  adjust base year from 1980 to 1970, do usual conversions from
+    yy/mm/dd hh:mm:ss to elapsed seconds, and account for timezone and day-
+    light savings time differences.  If we have a Unix extra field, however,
+    we're laughing:  both mtime and atime are ours.  On the other hand, we
+    then have to check for restoration of UID/GID.
+  ---------------------------------------------------------------------------*/
+    int have_uidgid_flg;
+    unsigned eb_izux_flg;
+
+    eb_izux_flg = (G.extra_field ? ef_scan_for_izux(G.extra_field,
+                   G.lrec.extra_field_length, 0, G.lrec.last_mod_dos_datetime,
+#ifdef IZ_CHECK_TZ
+                   (G.tz_is_valid ? pzt : NULL),
+#else
+                   pzt,
+#endif
+                   z_uidgid) : 0);
+    if (eb_izux_flg & EB_UT_FL_MTIME) {
+        TTrace((stderr, "\nget_extattribs:  Unix e.f. modif. time = %ld\n",
+          pzt->mtime));
+    } else {
+        pzt->mtime = dos_to_unix_time(G.lrec.last_mod_dos_datetime);
+    }
+    if (eb_izux_flg & EB_UT_FL_ATIME) {
+        TTrace((stderr, "get_extattribs:  Unix e.f. access time = %ld\n",
+          pzt->atime));
+    } else {
+        pzt->atime = pzt->mtime;
+        TTrace((stderr, "\nget_extattribs:  modification/access times = %ld\n",
+          pzt->mtime));
+    }
+
+    /* if -X option was specified and we have UID/GID info, restore it */
+    have_uidgid_flg =
+#ifdef RESTORE_UIDGID
+            (uO.X_flag && (eb_izux_flg & EB_UX2_VALID));
+#else
+            0;
+#endif
+    return have_uidgid_flg;
+}
+#endif /* !MTS || SET_DIR_ATTRIB */
+
+
+
+#ifndef MTS
+
+/****************************/
+/* Function CloseError()    */
+/***************************/
+
+int CloseError(__G)
+    __GDEF
+{
+    int errval = PK_OK;
+    
+    if (fclose(G.outfile) < 0) {
+          switch (errno) {
+                case ENOSPC:
+                    /* Do we need this on fileio.c? */
+                    Info(slide, 0x4a1, ((char *)slide, "%s: write error (disk full?).   Continue? (y/n/^C) ",
+                          FnFilter1(G.filename)));
+                    fgets(G.answerbuf, 9, stdin);
+                    if (*G.answerbuf == 'y')     /* stop writing to this file */
+                        G.disk_full = 1;         /* pass to next */
+                    else
+                        G.disk_full = 2;         /* no: exit program */
+          
+                    errval = PK_DISK;
+                    break;
+
+                default:
+                    errval = PK_WARN;
+          }
+     }
+     return errval;
+} /* End of CloseError() */
+
+/****************************/
+/* Function close_outfile() */
+/****************************/
+
+int close_outfile(__G) 
+    __GDEF
+{
+    union {
+        iztimes t3;             /* mtime, atime, ctime */
+        ztimbuf t2;             /* modtime, actime */
+    } zt;
+    ulg z_uidgid[2];
+    int have_uidgid_flg;
+    int errval = PK_OK;
+
+    have_uidgid_flg = get_extattribs(__G__ &(zt.t3), z_uidgid);
+
+/*---------------------------------------------------------------------------
+    If symbolic links are supported, allocate storage for a symlink control
+    structure, put the uncompressed "data" and other required info in it, and
+    add the structure to the "deferred symlinks" chain.  Since we know it's a
+    symbolic link to start with, we shouldn't have to worry about overflowing
+    unsigned ints with unsigned longs.
+  ---------------------------------------------------------------------------*/
+
+#ifdef SYMLINKS
+    if (G.symlnk) {
+        extent ucsize = (extent)G.lrec.ucsize;
+# ifdef SET_SYMLINK_ATTRIBS
+        extent attribsize = sizeof(unsigned) +
+                            (have_uidgid_flg ? sizeof(z_uidgid) : 0);
+# else
+        extent attribsize = 0;
+# endif
+        /* size of the symlink entry is the sum of
+         *  (struct size (includes 1st '\0') + 1 additional trailing '\0'),
+         *  system specific attribute data size (might be 0),
+         *  and the lengths of name and link target.
+         */
+        extent slnk_entrysize = (sizeof(slinkentry) + 1) + attribsize +
+                                ucsize + strlen(G.filename);
+        slinkentry *slnk_entry;
+
+        if (slnk_entrysize < ucsize) {
+            Info(slide, 0x201, ((char *)slide,
+              "warning:  symbolic link (%s) failed: mem alloc overflow\n",
+              FnFilter1(G.filename)));
+            errval = CloseError(G.outfile, G.filename);
+            return errval ? errval : PK_WARN;
+        }
+
+        if ((slnk_entry = (slinkentry *)malloc(slnk_entrysize)) == NULL) {
+            Info(slide, 0x201, ((char *)slide,
+              "warning:  symbolic link (%s) failed: no mem\n",
+              FnFilter1(G.filename)));
+            errval = CloseError(G.outfile, G.filename);
+            return errval ? errval : PK_WARN;
+        }
+        slnk_entry->next = NULL;
+        slnk_entry->targetlen = ucsize;
+        slnk_entry->attriblen = attribsize;
+# ifdef SET_SYMLINK_ATTRIBS
+        memcpy(slnk_entry->buf, &(G.pInfo->file_attr),
+               sizeof(unsigned));
+        if (have_uidgid_flg)
+            memcpy(slnk_entry->buf + 4, z_uidgid, sizeof(z_uidgid));
+# endif
+        slnk_entry->target = slnk_entry->buf + slnk_entry->attriblen;
+        slnk_entry->fname = slnk_entry->target + ucsize + 1;
+        strcpy(slnk_entry->fname, G.filename);
+
+        /* move back to the start of the file to re-read the "link data" */
+        rewind(G.outfile);
+
+        if (fread(slnk_entry->target, 1, ucsize, G.outfile) != ucsize)
+        {
+            Info(slide, 0x201, ((char *)slide,
+              "warning:  symbolic link (%s) failed\n",
+              FnFilter1(G.filename)));
+            free(slnk_entry);
+            errval = CloseError(G.outfile, G.filename);
+            return errval ? errval : PK_WARN;
+        }
+        errval = CloseError(G.outfile, G.filename); /* close "link" file for good... */
+        slnk_entry->target[ucsize] = '\0';
+        if (QCOND2)
+            Info(slide, 0, ((char *)slide, "-> %s ",
+              FnFilter1(slnk_entry->target)));
+        /* add this symlink record to the list of deferred symlinks */
+        if (G.slink_last != NULL)
+            G.slink_last->next = slnk_entry;
+        else
+            G.slink_head = slnk_entry;
+        G.slink_last = slnk_entry;
+        return errval;
+    }
+#endif /* SYMLINKS */
+
+#ifdef QLZIP
+    if (G.extra_field) {
+        static void qlfix OF((__GPRO__ uch *ef_ptr, unsigned ef_len));
+
+        qlfix(__G__ G.extra_field, G.lrec.extra_field_length);
+    }
+#endif
+
+#if (defined(NO_FCHOWN))
+    errval = CloseError(G.outfile, G.filename);
+#endif
+
+    /* if -X option was specified and we have UID/GID info, restore it */
+    if (have_uidgid_flg
+        /* check that both uid and gid values fit into their data sizes */
+        && ((ulg)(uid_t)(z_uidgid[0]) == z_uidgid[0])
+        && ((ulg)(gid_t)(z_uidgid[1]) == z_uidgid[1])) {
+        TTrace((stderr, "close_outfile:  restoring Unix UID/GID info\n"));
+#if (defined(NO_FCHOWN))
+        if (chown(G.filename, (uid_t)z_uidgid[0], (gid_t)z_uidgid[1]))
+#else
+        if (fchown(fileno(G.outfile), (uid_t)z_uidgid[0], (gid_t)z_uidgid[1]))
+#endif
+        {
+            if (uO.qflag)
+                Info(slide, 0x201, ((char *)slide, CannotSetItemUidGid,
+                  z_uidgid[0], z_uidgid[1], FnFilter1(G.filename),
+                  strerror(errno)));
+            else
+                Info(slide, 0x201, ((char *)slide, CannotSetUidGid,
+                  z_uidgid[0], z_uidgid[1], strerror(errno)));
+        }
+    }
+
+#if (!defined(NO_FCHOWN) && defined(NO_FCHMOD))
+    errval = CloseError(G.outfile, G.filename);
+#endif
+
+#if (!defined(NO_FCHOWN) && !defined(NO_FCHMOD))
+/*---------------------------------------------------------------------------
+    Change the file permissions from default ones to those stored in the
+    zipfile.
+  ---------------------------------------------------------------------------*/
+
+    if (fchmod(fileno(G.outfile), filtattr(__G__ G.pInfo->file_attr)))
+        perror("fchmod (file attributes) error");
+
+    errval = CloseError(G.outfile, G.filename);
+#endif /* !NO_FCHOWN && !NO_FCHMOD */
+
+    /* skip restoring time stamps on user's request */
+    if (uO.D_flag <= 1) {
+        /* set the file's access and modification times */
+        if (utime(G.filename, &(zt.t2))) {
+            if (uO.qflag)
+                Info(slide, 0x201, ((char *)slide, CannotSetItemTimestamps,
+                  FnFilter1(G.filename), strerror(errno)));
+            else
+                Info(slide, 0x201, ((char *)slide, CannotSetTimestamps,
+                  strerror(errno)));
+        }
+    }
+
+#if (defined(NO_FCHOWN) || defined(NO_FCHMOD))
+/*---------------------------------------------------------------------------
+    Change the file permissions from default ones to those stored in the
+    zipfile.
+  ---------------------------------------------------------------------------*/
+
+#ifndef NO_CHMOD
+    if (chmod(G.filename, filtattr(__G__ G.pInfo->file_attr)))
+        perror("chmod (file attributes) error");
+#endif
+#endif /* NO_FCHOWN || NO_FCHMOD */
+
+    return errval;
+} /* end function close_outfile() */
+
+#endif /* !MTS */
+
+
+#if (defined(SYMLINKS) && defined(SET_SYMLINK_ATTRIBS))
+int set_symlnk_attribs(__G__ slnk_entry)
+    __GDEF
+    slinkentry *slnk_entry;
+{
+    if (slnk_entry->attriblen > 0) {
+# if (!defined(NO_LCHOWN))
+      if (slnk_entry->attriblen > sizeof(unsigned)) {
+        ulg *z_uidgid_p = (zvoid *)(slnk_entry->buf + sizeof(unsigned));
+        /* check that both uid and gid values fit into their data sizes */
+        if (((ulg)(uid_t)(z_uidgid_p[0]) == z_uidgid_p[0]) &&
+            ((ulg)(gid_t)(z_uidgid_p[1]) == z_uidgid_p[1])) {
+          TTrace((stderr,
+            "set_symlnk_attribs:  restoring Unix UID/GID info for\n\
+        %s\n",
+            FnFilter1(slnk_entry->fname)));
+          if (lchown(slnk_entry->fname,
+                     (uid_t)z_uidgid_p[0], (gid_t)z_uidgid_p[1]))
+          {
+            Info(slide, 0x201, ((char *)slide, CannotSetItemUidGid,
+              z_uidgid_p[0], z_uidgid_p[1], FnFilter1(slnk_entry->fname),
+              strerror(errno)));
+          }
+        }
+      }
+# endif /* !NO_LCHOWN */
+# if (!defined(NO_LCHMOD))
+      TTrace((stderr,
+        "set_symlnk_attribs:  restoring Unix attributes for\n        %s\n",
+        FnFilter1(slnk_entry->fname)));
+      if (lchmod(slnk_entry->fname,
+                 filtattr(__G__ *(unsigned *)(zvoid *)slnk_entry->buf)))
+          perror("lchmod (file attributes) error");
+# endif /* !NO_LCHMOD */
+    }
+    /* currently, no error propagation... */
+    return PK_OK;
+} /* end function set_symlnk_attribs() */
+#endif /* SYMLINKS && SET_SYMLINK_ATTRIBS */
+
+
+#ifdef SET_DIR_ATTRIB
+/* messages of code for setting directory attributes */
+#  ifndef NO_CHMOD
+  static ZCONST char DirlistChmodFailed[] =
+    "warning:  cannot set permissions for %s\n          %s\n";
+#  endif
+
+
+int defer_dir_attribs(__G__ pd)
+    __GDEF
+    direntry **pd;
+{
+    uxdirattr *d_entry;
+
+    d_entry = (uxdirattr *)malloc(sizeof(uxdirattr) + strlen(G.filename));
+    *pd = (direntry *)d_entry;
+    if (d_entry == (uxdirattr *)NULL) {
+        return PK_MEM;
+    }
+    d_entry->fn = d_entry->fnbuf;
+    strcpy(d_entry->fn, G.filename);
+
+    d_entry->perms = G.pInfo->file_attr;
+
+    d_entry->have_uidgid = get_extattribs(__G__ &(d_entry->u.t3),
+                                          d_entry->uidgid);
+    return PK_OK;
+} /* end function defer_dir_attribs() */
+
+
+int set_direc_attribs(__G__ d)
+    __GDEF
+    direntry *d;
+{
+    int errval = PK_OK;
+
+    if (UxAtt(d)->have_uidgid &&
+        /* check that both uid and gid values fit into their data sizes */
+        ((ulg)(uid_t)(UxAtt(d)->uidgid[0]) == UxAtt(d)->uidgid[0]) &&
+        ((ulg)(gid_t)(UxAtt(d)->uidgid[1]) == UxAtt(d)->uidgid[1]) &&
+        chown(UxAtt(d)->fn, (uid_t)UxAtt(d)->uidgid[0],
+              (gid_t)UxAtt(d)->uidgid[1]))
+    {
+        Info(slide, 0x201, ((char *)slide, CannotSetItemUidGid,
+          UxAtt(d)->uidgid[0], UxAtt(d)->uidgid[1], FnFilter1(d->fn),
+          strerror(errno)));
+        if (!errval)
+            errval = PK_WARN;
+    }
+    /* Skip restoring directory time stamps on user' request. */
+    if (uO.D_flag <= 0) {
+        /* restore directory timestamps */
+        if (utime(d->fn, &UxAtt(d)->u.t2)) {
+            Info(slide, 0x201, ((char *)slide, CannotSetItemTimestamps,
+              FnFilter1(d->fn), strerror(errno)));
+            if (!errval)
+                errval = PK_WARN;
+        }
+    }
+#ifndef NO_CHMOD
+    if (chmod(d->fn, UxAtt(d)->perms)) {
+        Info(slide, 0x201, ((char *)slide, DirlistChmodFailed,
+          FnFilter1(d->fn), strerror(errno)));
+        if (!errval)
+            errval = PK_WARN;
+    }
+#endif /* !NO_CHMOD */
+    return errval;
+} /* end function set_direc_attribs() */
+
+#endif /* SET_DIR_ATTRIB */
+
+
+
+
+#ifdef TIMESTAMP
+
+/***************************/
+/*  Function stamp_file()  */
+/***************************/
+
+int stamp_file(fname, modtime)
+    ZCONST char *fname;
+    time_t modtime;
+{
+    ztimbuf tp;
+
+    tp.modtime = tp.actime = modtime;
+    return (utime(fname, &tp));
+
+} /* end function stamp_file() */
+
+#endif /* TIMESTAMP */
+
+
+
+
+#ifndef SFX
+
+/************************/
+/*  Function version()  */
+/************************/
+
+void version(__G)
+    __GDEF
+{
+#if (defined(__GNUC__) && defined(NX_CURRENT_COMPILER_RELEASE))
+    char cc_namebuf[40];
+    char cc_versbuf[40];
+#else
+#if (defined(__SUNPRO_C))
+    char cc_versbuf[17];
+#else
+#if (defined(__HP_cc) || defined(__IBMC__))
+    char cc_versbuf[25];
+#else
+#if (defined(__DECC_VER))
+    char cc_versbuf[17];
+    int cc_verstyp;
+#else
+#if (defined(CRAY) && defined(_RELEASE))
+    char cc_versbuf[40];
+#endif /* (CRAY && _RELEASE) */
+#endif /* __DECC_VER */
+#endif /* __HP_cc || __IBMC__ */
+#endif /* __SUNPRO_C */
+#endif /* (__GNUC__ && NX_CURRENT_COMPILER_RELEASE) */
+
+#if ((defined(CRAY) || defined(cray)) && defined(_UNICOS))
+    char os_namebuf[40];
+#else
+#if defined(__NetBSD__)
+    char os_namebuf[40];
+#endif
+#endif
+
+    /* Pyramid, NeXT have problems with huge macro expansion, too:  no Info() */
+    sprintf((char *)slide, LoadFarString(CompiledWith),
+
+#ifdef __GNUC__
+#  ifdef NX_CURRENT_COMPILER_RELEASE
+      (sprintf(cc_namebuf, "NeXT DevKit %d.%02d ",
+        NX_CURRENT_COMPILER_RELEASE/100, NX_CURRENT_COMPILER_RELEASE%100),
+       cc_namebuf),
+      (strlen(__VERSION__) > 8)? "(gcc)" :
+        (sprintf(cc_versbuf, "(gcc %s)", __VERSION__), cc_versbuf),
+#  else
+      "gcc ", __VERSION__,
+#  endif
+#else
+#if defined(__SUNPRO_C)
+      "Sun C ", (sprintf(cc_versbuf, "version %x", __SUNPRO_C), cc_versbuf),
+#else
+#if (defined(__HP_cc))
+      "HP C ",
+      (((__HP_cc% 100) == 0) ?
+      (sprintf(cc_versbuf, "version A.%02d.%02d",
+      (__HP_cc/ 10000), ((__HP_cc% 10000)/ 100))) :
+      (sprintf(cc_versbuf, "version A.%02d.%02d.%02d",
+      (__HP_cc/ 10000), ((__HP_cc% 10000)/ 100), (__HP_cc% 100))),
+      cc_versbuf),
+#else
+#if (defined(__DECC_VER))
+      "DEC C ",
+      (sprintf(cc_versbuf, "%c%d.%d-%03d",
+               ((cc_verstyp = (__DECC_VER / 10000) % 10) == 6 ? 'T' :
+                (cc_verstyp == 8 ? 'S' : 'V')),
+               __DECC_VER / 10000000,
+               (__DECC_VER % 10000000) / 100000, __DECC_VER % 1000),
+               cc_versbuf),
+#else
+#if defined(CRAY) && defined(_RELEASE)
+      "cc ", (sprintf(cc_versbuf, "version %d", _RELEASE), cc_versbuf),
+#else
+#ifdef __IBMC__
+      "IBM C ",
+      (sprintf(cc_versbuf, "version %d.%d.%d",
+               (__IBMC__ / 100), ((__IBMC__ / 10) % 10), (__IBMC__ % 10)),
+               cc_versbuf),
+#else
+#ifdef __VERSION__
+#   ifndef IZ_CC_NAME
+#    define IZ_CC_NAME "cc "
+#   endif
+      IZ_CC_NAME, __VERSION__
+#else
+#   ifndef IZ_CC_NAME
+#    define IZ_CC_NAME "cc"
+#   endif
+      IZ_CC_NAME, "",
+#endif /* ?__VERSION__ */
+#endif /* ?__IBMC__ */
+#endif /* ?(CRAY && _RELEASE) */
+#endif /* ?__DECC_VER */
+#endif /* ?__HP_cc */
+#endif /* ?__SUNPRO_C */
+#endif /* ?__GNUC__ */
+
+#ifndef IZ_OS_NAME
+#  define IZ_OS_NAME "Unix"
+#endif
+      IZ_OS_NAME,
+
+#if defined(sgi) || defined(__sgi)
+      " (Silicon Graphics IRIX)",
+#else
+#ifdef sun
+#  ifdef sparc
+#    ifdef __SVR4
+      " (Sun SPARC/Solaris)",
+#    else /* may or may not be SunOS */
+      " (Sun SPARC)",
+#    endif
+#  else
+#  if defined(sun386) || defined(i386)
+      " (Sun 386i)",
+#  else
+#  if defined(mc68020) || defined(__mc68020__)
+      " (Sun 3)",
+#  else /* mc68010 or mc68000:  Sun 2 or earlier */
+      " (Sun 2)",
+#  endif
+#  endif
+#  endif
+#else
+#ifdef __hpux
+      " (HP-UX)",
+#else
+#ifdef __osf__
+      " (DEC OSF/1)",
+#else
+#ifdef _AIX
+      " (IBM AIX)",
+#else
+#ifdef aiws
+      " (IBM RT/AIX)",
+#else
+#if defined(CRAY) || defined(cray)
+#  ifdef _UNICOS
+      (sprintf(os_namebuf, " (Cray UNICOS release %d)", _UNICOS), os_namebuf),
+#  else
+      " (Cray UNICOS)",
+#  endif
+#else
+#if defined(uts) || defined(UTS)
+      " (Amdahl UTS)",
+#else
+#ifdef NeXT
+#  ifdef mc68000
+      " (NeXTStep/black)",
+#  else
+      " (NeXTStep for Intel)",
+#  endif
+#else              /* the next dozen or so are somewhat order-dependent */
+#ifdef LINUX
+#  ifdef __ELF__
+      " (Linux ELF)",
+#  else
+      " (Linux a.out)",
+#  endif
+#else
+#ifdef MINIX
+      " (Minix)",
+#else
+#ifdef M_UNIX
+      " (SCO Unix)",
+#else
+#ifdef M_XENIX
+      " (SCO Xenix)",
+#else
+#ifdef __NetBSD__
+#  ifdef NetBSD0_8
+      (sprintf(os_namebuf, " (NetBSD 0.8%c)", (char)(NetBSD0_8 - 1 + 'A')),
+       os_namebuf),
+#  else
+#  ifdef NetBSD0_9
+      (sprintf(os_namebuf, " (NetBSD 0.9%c)", (char)(NetBSD0_9 - 1 + 'A')),
+       os_namebuf),
+#  else
+#  ifdef NetBSD1_0
+      (sprintf(os_namebuf, " (NetBSD 1.0%c)", (char)(NetBSD1_0 - 1 + 'A')),
+       os_namebuf),
+#  else
+      (BSD4_4 == 0.5)? " (NetBSD before 0.9)" : " (NetBSD 1.1 or later)",
+#  endif
+#  endif
+#  endif
+#else
+#ifdef __FreeBSD__
+      (BSD4_4 == 0.5)? " (FreeBSD 1.x)" : " (FreeBSD 2.0 or later)",
+#else
+#ifdef __bsdi__
+      (BSD4_4 == 0.5)? " (BSD/386 1.0)" : " (BSD/386 1.1 or later)",
+#else
+#ifdef __386BSD__
+      (BSD4_4 == 1)? " (386BSD, post-4.4 release)" : " (386BSD)",
+#else
+#ifdef __CYGWIN__
+      " (Cygwin)",
+#else
+#if defined(i686) || defined(__i686) || defined(__i686__)
+      " (Intel 686)",
+#else
+#if defined(i586) || defined(__i586) || defined(__i586__)
+      " (Intel 586)",
+#else
+#if defined(i486) || defined(__i486) || defined(__i486__)
+      " (Intel 486)",
+#else
+#if defined(i386) || defined(__i386) || defined(__i386__)
+      " (Intel 386)",
+#else
+#ifdef pyr
+      " (Pyramid)",
+#else
+#ifdef ultrix
+#  ifdef mips
+      " (DEC/MIPS)",
+#  else
+#  ifdef vax
+      " (DEC/VAX)",
+#  else /* __alpha? */
+      " (DEC/Alpha)",
+#  endif
+#  endif
+#else
+#ifdef gould
+      " (Gould)",
+#else
+#ifdef MTS
+      " (MTS)",
+#else
+#ifdef __convexc__
+      " (Convex)",
+#else
+#ifdef __QNX__
+      " (QNX 4)",
+#else
+#ifdef __QNXNTO__
+      " (QNX Neutrino)",
+#else
+#ifdef Lynx
+      " (LynxOS)",
+#else
+#ifdef __APPLE__
+#  ifdef __i386__
+      " Mac OS X Intel i32",
+#  else
+#  ifdef __ppc__
+      " Mac OS X PowerPC",
+#  else
+#  ifdef __ppc64__
+      " Mac OS X PowerPC64",
+#  else
+      " Mac OS X",
+#  endif /* __ppc64__ */
+#  endif /* __ppc__ */
+#  endif /* __i386__ */
+#else
+      "",
+#endif /* Apple */
+#endif /* Lynx */
+#endif /* QNX Neutrino */
+#endif /* QNX 4 */
+#endif /* Convex */
+#endif /* MTS */
+#endif /* Gould */
+#endif /* DEC */
+#endif /* Pyramid */
+#endif /* 386 */
+#endif /* 486 */
+#endif /* 586 */
+#endif /* 686 */
+#endif /* Cygwin */
+#endif /* 386BSD */
+#endif /* BSDI BSD/386 */
+#endif /* NetBSD */
+#endif /* FreeBSD */
+#endif /* SCO Xenix */
+#endif /* SCO Unix */
+#endif /* Minix */
+#endif /* Linux */
+#endif /* NeXT */
+#endif /* Amdahl */
+#endif /* Cray */
+#endif /* RT/AIX */
+#endif /* AIX */
+#endif /* OSF/1 */
+#endif /* HP-UX */
+#endif /* Sun */
+#endif /* SGI */
+
+#ifdef __DATE__
+      " on ", __DATE__
+#else
+      "", ""
+#endif
+    );
+
+    (*G.message)((zvoid *)&G, slide, (ulg)strlen((char *)slide), 0);
+
+} /* end function version() */
+
+#endif /* !SFX */
+
+
+
+
+#ifdef QLZIP
+
+struct qdirect  {
+    long            d_length __attribute__ ((packed));  /* file length */
+    unsigned char   d_access __attribute__ ((packed));  /* file access type */
+    unsigned char   d_type __attribute__ ((packed));    /* file type */
+    long            d_datalen __attribute__ ((packed)); /* data length */
+    long            d_reserved __attribute__ ((packed));/* Unused */
+    short           d_szname __attribute__ ((packed));  /* size of name */
+    char            d_name[36] __attribute__ ((packed));/* name area */
+    long            d_update __attribute__ ((packed));  /* last update */
+    long            d_refdate __attribute__ ((packed));
+    long            d_backup __attribute__ ((packed));   /* EOD */
+};
+
+#define LONGID  "QDOS02"
+#define EXTRALEN (sizeof(struct qdirect) + 8)
+#define JBLONGID    "QZHD"
+#define JBEXTRALEN  (sizeof(jbextra)  - 4 * sizeof(char))
+
+typedef struct {
+    char        eb_header[4] __attribute__ ((packed));  /* place_holder */
+    char        longid[8] __attribute__ ((packed));
+    struct      qdirect     header __attribute__ ((packed));
+} qdosextra;
+
+typedef struct {
+    char        eb_header[4];                           /* place_holder */
+    char        longid[4];
+    struct      qdirect     header;
+} jbextra;
+
+
+
+/*  The following two functions SH() and LG() convert big-endian short
+ *  and long numbers into native byte order.  They are some kind of
+ *  counterpart to the generic UnZip's makeword() and makelong() functions.
+ */
+static ush SH(ush val)
+{
+    uch swapbuf[2];
+
+    swapbuf[1] = (uch)(val & 0xff);
+    swapbuf[0] = (uch)(val >> 8);
+    return (*(ush *)swapbuf);
+}
+
+
+
+static ulg LG(ulg val)
+{
+    /*  convert the big-endian unsigned long number `val' to the machine
+     *  dependent representation
+     */
+    ush swapbuf[2];
+
+    swapbuf[1] = SH((ush)(val & 0xffff));
+    swapbuf[0] = SH((ush)(val >> 16));
+    return (*(ulg *)swapbuf);
+}
+
+
+
+static void qlfix(__G__ ef_ptr, ef_len)
+    __GDEF
+    uch *ef_ptr;
+    unsigned ef_len;
+{
+    while (ef_len >= EB_HEADSIZE)
+    {
+        unsigned    eb_id  = makeword(EB_ID + ef_ptr);
+        unsigned    eb_len = makeword(EB_LEN + ef_ptr);
+
+        if (eb_len > (ef_len - EB_HEADSIZE)) {
+            /* discovered some extra field inconsistency! */
+            Trace((stderr,
+              "qlfix: block length %u > rest ef_size %u\n", eb_len,
+              ef_len - EB_HEADSIZE));
+            break;
+        }
+
+        switch (eb_id) {
+          case EF_QDOS:
+          {
+            struct _ntc_
+            {
+                long id;
+                long dlen;
+            } ntc;
+            long dlen = 0;
+
+            qdosextra   *extra = (qdosextra *)ef_ptr;
+            jbextra     *jbp   = (jbextra   *)ef_ptr;
+
+            if (!strncmp(extra->longid, LONGID, strlen(LONGID)))
+            {
+                if (eb_len != EXTRALEN)
+                    if (uO.qflag)
+                        Info(slide, 0x201, ((char *)slide,
+                          "warning:  invalid length in Qdos field for %s\n",
+                          FnFilter1(G.filename)));
+                    else
+                        Info(slide, 0x201, ((char *)slide,
+                          "warning:  invalid length in Qdos field"));
+
+                if (extra->header.d_type)
+                {
+                    dlen = extra->header.d_datalen;
+                }
+            }
+
+            if (!strncmp(jbp->longid, JBLONGID, strlen(JBLONGID)))
+            {
+                if (eb_len != JBEXTRALEN)
+                    if (uO.qflag)
+                        Info(slide, 0x201, ((char *)slide,
+                          "warning:  invalid length in QZ field for %s\n",
+                          FnFilter1(G.filename)));
+                    else
+                        Info(slide, 0x201, ((char *)slide,
+                          "warning:  invalid length in QZ field"));
+                if (jbp->header.d_type)
+                {
+                    dlen = jbp->header.d_datalen;
+                }
+            }
+
+            if ((long)LG(dlen) > 0)
+            {
+                zfseeko(G.outfile, -8, SEEK_END);
+                fread(&ntc, 8, 1, G.outfile);
+                if (ntc.id != *(long *)"XTcc")
+                {
+                    ntc.id = *(long *)"XTcc";
+                    ntc.dlen = dlen;
+                    fwrite (&ntc, 8, 1, G.outfile);
+                }
+                Info(slide, 0x201, ((char *)slide, "QData = %d", LG(dlen)));
+            }
+            return;     /* finished, cancel further extra field scanning */
+          }
+
+          default:
+            Trace((stderr,"qlfix: unknown extra field block, ID=%d\n",
+               eb_id));
+        }
+
+        /* Skip this extra field block */
+        ef_ptr += (eb_len + EB_HEADSIZE);
+        ef_len -= (eb_len + EB_HEADSIZE);
+    }
+}
+#endif /* QLZIP */
diff -Naur a/unix/unxcfg.h b/unix/unxcfg.h
--- a/unix/unxcfg.h	2009-04-16 19:36:12.000000000 +0100
+++ b/unix/unxcfg.h	2019-12-02 01:49:39.894641004 +0000
@@ -227,4 +227,30 @@
 /* wild_dir, dirname, wildname, matchname[], dirnamelen, have_dirname, */
 /*    and notfirstcall are used by do_wild().                          */
 
+
+#define MAX_CP_NAME 25 + 1 
+   
+#ifdef SETLOCALE
+#  undef SETLOCALE
+#endif
+#define SETLOCALE(category, locale) setlocale(category, locale)
+#include <locale.h>
+   
+#ifdef _ISO_INTERN
+#  undef _ISO_INTERN
+#endif
+#define _ISO_INTERN(str1) iso_intern(str1)
+
+#ifdef _OEM_INTERN
+#  undef _OEM_INTERN
+#endif
+#ifndef IZ_OEM2ISO_ARRAY
+#  define IZ_OEM2ISO_ARRAY
+#endif
+#define _OEM_INTERN(str1) oem_intern(str1)
+
+void iso_intern(char *);
+void oem_intern(char *);
+void init_conversion_charsets(void);
+   
 #endif /* !__unxcfg_h */
diff -Naur a/unix/unxcfg.h.orig b/unix/unxcfg.h.orig
--- a/unix/unxcfg.h.orig	1970-01-01 01:00:00.000000000 +0100
+++ b/unix/unxcfg.h.orig	2009-04-16 19:36:12.000000000 +0100
@@ -0,0 +1,230 @@
+/*
+  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
+*/
+/*---------------------------------------------------------------------------
+    Unix specific configuration section:
+  ---------------------------------------------------------------------------*/
+
+#ifndef __unxcfg_h
+#define __unxcfg_h
+
+
+/* LARGE FILE SUPPORT - 10/6/04 EG */
+/* This needs to be set before the includes so they set the right sizes */
+
+#if (defined(NO_LARGE_FILE_SUPPORT) && defined(LARGE_FILE_SUPPORT))
+#  undef LARGE_FILE_SUPPORT
+#endif
+
+/* Automatically set ZIP64_SUPPORT if LFS */
+#ifdef LARGE_FILE_SUPPORT
+# if (!defined(NO_ZIP64_SUPPORT) && !defined(ZIP64_SUPPORT))
+#   define ZIP64_SUPPORT
+# endif
+#endif
+
+/* NO_ZIP64_SUPPORT takes preceedence over ZIP64_SUPPORT */
+#if defined(NO_ZIP64_SUPPORT) && defined(ZIP64_SUPPORT)
+#  undef ZIP64_SUPPORT
+#endif
+
+#ifdef LARGE_FILE_SUPPORT
+  /* 64-bit Large File Support */
+
+  /* The following Large File Summit (LFS) defines turn on large file support
+     on Linux (probably 2.4 or later kernel) and many other unixen */
+
+  /* These have to be before any include that sets types so the large file
+     versions of the types are set in the includes */
+
+# define _LARGEFILE_SOURCE      /* some OSes need this for fseeko */
+# define _LARGEFILE64_SOURCE
+# define _FILE_OFFSET_BITS 64   /* select default interface as 64 bit */
+# define _LARGE_FILES           /* some OSes need this for 64-bit off_t */
+# define __USE_LARGEFILE64
+#endif /* LARGE_FILE_SUPPORT */
+
+
+#include <sys/types.h>          /* off_t, time_t, dev_t, ... */
+#include <sys/stat.h>
+
+#ifdef NO_OFF_T
+  typedef long zoff_t;
+#else
+  typedef off_t zoff_t;
+#endif
+#define ZOFF_T_DEFINED
+typedef struct stat z_stat;
+#define Z_STAT_DEFINED
+
+#ifndef COHERENT
+#  include <fcntl.h>            /* O_BINARY for open() w/o CR/LF translation */
+#else /* COHERENT */
+#  ifdef _I386
+#    include <fcntl.h>          /* Coherent 4.0.x, Mark Williams C */
+#  else
+#    include <sys/fcntl.h>      /* Coherent 3.10, Mark Williams C */
+#  endif
+#  define SHORT_SYMS
+#  ifndef __COHERENT__          /* Coherent 4.2 has tzset() */
+#    define tzset  settz
+#  endif
+#endif /* ?COHERENT */
+
+#ifndef NO_PARAM_H
+#  ifdef NGROUPS_MAX
+#    undef NGROUPS_MAX      /* SCO bug:  defined again in <sys/param.h> */
+#  endif
+#  ifdef BSD
+#    define TEMP_BSD        /* may be defined again in <sys/param.h> */
+#    undef BSD
+#  endif
+#  include <sys/param.h>    /* conflict with <sys/types.h>, some systems? */
+#  ifdef TEMP_BSD
+#    undef TEMP_BSD
+#    ifndef BSD
+#      define BSD
+#    endif
+#  endif
+#endif /* !NO_PARAM_H */
+
+#ifdef __osf__
+#  define DIRENT
+#  ifdef BSD
+#    undef BSD
+#  endif
+#endif /* __osf__ */
+
+#ifdef __CYGWIN__
+#  include <unistd.h>
+#  define DIRENT
+#  define HAVE_TERMIOS_H
+#  ifndef timezone
+#    define timezone _timezone
+#  endif
+#endif
+
+#ifdef BSD
+#  include <sys/time.h>
+#  include <sys/timeb.h>
+#  if (defined(_AIX) || defined(__GLIBC__) || defined(__GNU__))
+#    include <time.h>
+#  endif
+#else
+#  include <time.h>
+   struct tm *gmtime(), *localtime();
+#endif
+
+#if (defined(BSD4_4) || (defined(SYSV) && defined(MODERN)))
+#  include <unistd.h>           /* this includes utime.h on SGIs */
+#  if (defined(BSD4_4) || defined(linux) || defined(__GLIBC__))
+#    include <utime.h>
+#    define GOT_UTIMBUF
+#  endif
+#  if (!defined(GOT_UTIMBUF) && (defined(__hpux) || defined(__SUNPRO_C)))
+#    include <utime.h>
+#    define GOT_UTIMBUF
+#  endif
+#  if (!defined(GOT_UTIMBUF) && defined(__GNU__))
+#    include <utime.h>
+#    define GOT_UTIMBUF
+#  endif
+#endif
+#if (defined(__DGUX__) && !defined(GOT_UTIMBUF))
+   /* DG/UX requires this because of a non-standard struct utimebuf */
+#  include <utime.h>
+#  define GOT_UTIMBUF
+#endif
+
+#if (defined(V7) || defined(pyr_bsd))
+#  define strchr   index
+#  define strrchr  rindex
+#endif
+#ifdef V7
+#  define O_RDONLY 0
+#  define O_WRONLY 1
+#  define O_RDWR   2
+#endif
+
+#if defined(NO_UNICODE_SUPPORT) && defined(UNICODE_SUPPORT)
+   /* disable Unicode (UTF-8) support when requested */
+#  undef UNICODE_SUPPORT
+#endif
+
+#if (defined(_MBCS) && defined(NO_MBCS))
+   /* disable MBCS support when requested */
+#  undef _MBCS
+#endif
+
+#if (!defined(NO_SETLOCALE) && !defined(_MBCS))
+# if (!defined(UNICODE_SUPPORT) || !defined(UTF8_MAYBE_NATIVE))
+   /* enable setlocale here, unless this happens later for UTF-8 and/or
+    * MBCS support */
+#  include <locale.h>
+#  ifndef SETLOCALE
+#    define SETLOCALE(category, locale) setlocale(category, locale)
+#  endif
+# endif
+#endif
+#ifndef NO_SETLOCALE
+# if (!defined(NO_WORKING_ISPRINT) && !defined(HAVE_WORKING_ISPRINT))
+   /* enable "enhanced" unprintable chars detection in fnfilter() */
+#  define HAVE_WORKING_ISPRINT
+# endif
+#endif
+
+#ifdef MINIX
+#  include <stdio.h>
+#endif
+#if (!defined(HAVE_STRNICMP) & !defined(NO_STRNICMP))
+#  define NO_STRNICMP
+#endif
+#ifndef DATE_FORMAT
+#  define DATE_FORMAT DF_MDY    /* GRR:  customize with locale.h somehow? */
+#endif
+#define lenEOL          1
+#ifdef EBCDIC
+#  define PutNativeEOL  *q++ = '\n';
+#else
+#  define PutNativeEOL  *q++ = native(LF);
+#endif
+#define SCREENSIZE(ttrows, ttcols)  screensize(ttrows, ttcols)
+#define SCREENWIDTH     80
+#define SCREENLWRAP     1
+#define USE_EF_UT_TIME
+#if (!defined(NO_LCHOWN) || !defined(NO_LCHMOD))
+#  define SET_SYMLINK_ATTRIBS
+#endif
+#ifdef MTS
+#  ifdef SET_DIR_ATTRIB
+#    undef SET_DIR_ATTRIB
+#  endif
+#else /* !MTS */
+#  define SET_DIR_ATTRIB
+#  if (!defined(NOTIMESTAMP) && !defined(TIMESTAMP))   /* GRR 970513 */
+#    define TIMESTAMP
+#  endif
+#  define RESTORE_UIDGID
+#endif /* ?MTS */
+
+/* Static variables that we have to add to Uz_Globs: */
+#define SYSTEM_SPECIFIC_GLOBALS \
+    int created_dir, renamed_fullpath;\
+    char *rootpath, *buildpath, *end;\
+    ZCONST char *wildname;\
+    char *dirname, matchname[FILNAMSIZ];\
+    int rootlen, have_dirname, dirnamelen, notfirstcall;\
+    zvoid *wild_dir;
+
+/* created_dir, and renamed_fullpath are used by both mapname() and    */
+/*    checkdir().                                                      */
+/* rootlen, rootpath, buildpath and end are used by checkdir().        */
+/* wild_dir, dirname, wildname, matchname[], dirnamelen, have_dirname, */
+/*    and notfirstcall are used by do_wild().                          */
+
+#endif /* !__unxcfg_h */
diff -Naur a/unzip.c b/unzip.c
--- a/unzip.c	2009-04-16 19:26:52.000000000 +0100
+++ b/unzip.c	2019-12-02 01:49:39.895641007 +0000
@@ -327,11 +327,21 @@
   -2  just filenames but allow -h/-t/-z  -l  long Unix \"ls -l\" format\n\
                                          -v  verbose, multi-page format\n";
 
+#ifndef UNIX
 static ZCONST char Far ZipInfoUsageLine3[] = "miscellaneous options:\n\
   -h  print header line       -t  print totals for listed files or for all\n\
   -z  print zipfile comment   -T  print file times in sortable decimal format\
 \n  -C  be case-insensitive   %s\
   -x  exclude filenames that follow from listing\n";
+#else /* UNIX */
+static ZCONST char Far ZipInfoUsageLine3[] = "miscellaneous options:\n\
+  -h  print header line       -t  print totals for listed files or for all\n\
+  -z  print zipfile comment  %c-T%c print file times in sortable decimal format\
+\n %c-C%c be case-insensitive   %s\
+  -x  exclude filenames that follow from listing\n\
+  -O CHARSET  specify a character encoding for DOS, Windows and OS/2 archives\n\
+  -I CHARSET  specify a character encoding for UNIX and other archives\n";
+#endif /* !UNIX */
 #ifdef MORE
    static ZCONST char Far ZipInfoUsageLine4[] =
      "  -M  page output through built-in \"more\"\n";
@@ -665,6 +675,17 @@
   -U  use escapes for all non-ASCII Unicode  -UU ignore any Unicode fields\n\
   -C  match filenames case-insensitively     -L  make (some) names \
 lowercase\n %-42s  -V  retain VMS version numbers\n%s";
+#elif (defined UNIX)
+static ZCONST char Far UnzipUsageLine4[] = "\
+modifiers:\n\
+  -n  never overwrite existing files         -q  quiet mode (-qq => quieter)\n\
+  -o  overwrite files WITHOUT prompting      -a  auto-convert any text files\n\
+  -j  junk paths (do not make directories)   -aa treat ALL files as text\n\
+  -U  use escapes for all non-ASCII Unicode  -UU ignore any Unicode fields\n\
+  -C  match filenames case-insensitively     -L  make (some) names \
+lowercase\n %-42s  -V  retain VMS version numbers\n%s\
+  -O CHARSET  specify a character encoding for DOS, Windows and OS/2 archives\n\
+  -I CHARSET  specify a character encoding for UNIX and other archives\n\n";
 #else /* !VMS */
 static ZCONST char Far UnzipUsageLine4[] = "\
 modifiers:\n\
@@ -803,6 +824,10 @@
 #endif /* UNICODE_SUPPORT */
 
 
+#ifdef UNIX
+    init_conversion_charsets();
+#endif
+
 #if (defined(__IBMC__) && defined(__DEBUG_ALLOC__))
     extern void DebugMalloc(void);
 
@@ -1336,6 +1361,11 @@
     argc = *pargc;
     argv = *pargv;
 
+#ifdef UNIX
+    extern char OEM_CP[MAX_CP_NAME];
+    extern char ISO_CP[MAX_CP_NAME];
+#endif
+    
     while (++argv, (--argc > 0 && *argv != NULL && **argv == '-')) {
         s = *argv + 1;
         while ((c = *s++) != 0) {    /* "!= 0":  prevent Turbo C warning */
@@ -1517,6 +1547,37 @@
                     }
                     break;
 #endif  /* MACOS */
+#ifdef UNIX
+    			case ('I'):
+                    if (negative) {
+                        Info(slide, 0x401, ((char *)slide,
+                          "error:  encodings can't be negated"));
+                        return(PK_PARAM);
+    				} else {
+    					if(*s) { /* Handle the -Icharset case */
+    						/* Assume that charsets can't start with a dash to spot arguments misuse */
+    						if(*s == '-') { 
+    	                        Info(slide, 0x401, ((char *)slide,
+        		                  "error:  a valid character encoding should follow the -I argument"));
+    	                        return(PK_PARAM); 
+    						}
+    						strncpy(ISO_CP, s, MAX_CP_NAME - 1);
+                ISO_CP[MAX_CP_NAME - 1] = '\0';
+    					} else { /* -I charset */
+    						++argv;
+    						if(!(--argc > 0 && *argv != NULL && **argv != '-')) {
+    	                        Info(slide, 0x401, ((char *)slide,
+        		                  "error:  a valid character encoding should follow the -I argument"));
+    	                        return(PK_PARAM); 
+    						}
+    						s = *argv;
+    						strncpy(ISO_CP, s, MAX_CP_NAME - 1);
+                ISO_CP[MAX_CP_NAME - 1] = '\0';
+    					}
+    					while(*(++s)); /* No params straight after charset name */
+    				}
+    				break;
+#endif /* ?UNIX */
                 case ('j'):    /* junk pathnames/directory structure */
                     if (negative)
                         uO.jflag = FALSE, negative = 0;
@@ -1592,6 +1653,37 @@
                     } else
                         ++uO.overwrite_all;
                     break;
+#ifdef UNIX
+    			case ('O'):
+                    if (negative) {
+                        Info(slide, 0x401, ((char *)slide,
+                          "error:  encodings can't be negated"));
+                        return(PK_PARAM);
+    				} else {
+    					if(*s) { /* Handle the -Ocharset case */
+    						/* Assume that charsets can't start with a dash to spot arguments misuse */
+    						if(*s == '-') { 
+    	                        Info(slide, 0x401, ((char *)slide,
+        		                  "error:  a valid character encoding should follow the -I argument"));
+    	                        return(PK_PARAM); 
+    						}
+    						strncpy(OEM_CP, s, MAX_CP_NAME - 1);
+                OEM_CP[MAX_CP_NAME - 1] = '\0';
+    					} else { /* -O charset */
+    						++argv;
+    						if(!(--argc > 0 && *argv != NULL && **argv != '-')) {
+    	                        Info(slide, 0x401, ((char *)slide,
+        		                  "error:  a valid character encoding should follow the -O argument"));
+    	                        return(PK_PARAM); 
+    						}
+    						s = *argv;
+    						strncpy(OEM_CP, s, MAX_CP_NAME - 1);
+                OEM_CP[MAX_CP_NAME - 1] = '\0';
+    					}
+    					while(*(++s)); /* No params straight after charset name */
+    				}
+    				break;
+#endif /* ?UNIX */
                 case ('p'):    /* pipes:  extract to stdout, no messages */
                     if (negative) {
                         uO.cflag = FALSE;
diff -Naur a/unzip.c.orig b/unzip.c.orig
--- a/unzip.c.orig	1970-01-01 01:00:00.000000000 +0100
+++ b/unzip.c.orig	2009-04-16 19:26:52.000000000 +0100
@@ -0,0 +1,2655 @@
+/*
+  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
+*/
+/*---------------------------------------------------------------------------
+
+  unzip.c
+
+  UnZip - a zipfile extraction utility.  See below for make instructions, or
+  read the comments in Makefile and the various Contents files for more de-
+  tailed explanations.  To report a bug, submit a *complete* description via
+  //www.info-zip.org/zip-bug.html; include machine type, operating system and
+  version, compiler and version, and reasonably detailed error messages or
+  problem report.  To join Info-ZIP, see the instructions in README.
+
+  UnZip 5.x is a greatly expanded and partially rewritten successor to 4.x,
+  which in turn was almost a complete rewrite of version 3.x.  For a detailed
+  revision history, see UnzpHist.zip at quest.jpl.nasa.gov.  For a list of
+  the many (near infinite) contributors, see "CONTRIBS" in the UnZip source
+  distribution.
+
+  UnZip 6.0 adds support for archives larger than 4 GiB using the Zip64
+  extensions as well as support for Unicode information embedded per the
+  latest zip standard additions.
+
+  ---------------------------------------------------------------------------
+
+  [from original zipinfo.c]
+
+  This program reads great gobs of totally nifty information, including the
+  central directory stuff, from ZIP archives ("zipfiles" for short).  It
+  started as just a testbed for fooling with zipfiles, but at this point it
+  is actually a useful utility.  It also became the basis for the rewrite of
+  UnZip (3.16 -> 4.0), using the central directory for processing rather than
+  the individual (local) file headers.
+
+  As of ZipInfo v2.0 and UnZip v5.1, the two programs are combined into one.
+  If the executable is named "unzip" (or "unzip.exe", depending), it behaves
+  like UnZip by default; if it is named "zipinfo" or "ii", it behaves like
+  ZipInfo.  The ZipInfo behavior may also be triggered by use of unzip's -Z
+  option; for example, "unzip -Z [zipinfo_options] archive.zip".
+
+  Another dandy product from your buddies at Newtware!
+
+  Author:  Greg Roelofs, newt@pobox.com, http://pobox.com/~newt/
+           23 August 1990 -> April 1997
+
+  ---------------------------------------------------------------------------
+
+  Version:  unzip5??.{tar.Z | tar.gz | zip} for Unix, VMS, OS/2, MS-DOS, Amiga,
+              Atari, Windows 3.x/95/NT/CE, Macintosh, Human68K, Acorn RISC OS,
+              AtheOS, BeOS, SMS/QDOS, VM/CMS, MVS, AOS/VS, Tandem NSK, Theos
+              and TOPS-20.
+
+  Copyrights:  see accompanying file "LICENSE" in UnZip source distribution.
+               (This software is free but NOT IN THE PUBLIC DOMAIN.)
+
+  ---------------------------------------------------------------------------*/
+
+
+
+#define __UNZIP_C       /* identifies this source module */
+#define UNZIP_INTERNAL
+#include "unzip.h"      /* includes, typedefs, macros, prototypes, etc. */
+#include "crypt.h"
+#include "unzvers.h"
+
+#ifndef WINDLL          /* The WINDLL port uses windll/windll.c instead... */
+
+/***************************/
+/* Local type declarations */
+/***************************/
+
+#if (defined(REENTRANT) && !defined(NO_EXCEPT_SIGNALS))
+typedef struct _sign_info
+    {
+        struct _sign_info *previous;
+        void (*sighandler)(int);
+        int sigtype;
+    } savsigs_info;
+#endif
+
+/*******************/
+/* Local Functions */
+/*******************/
+
+#if (defined(REENTRANT) && !defined(NO_EXCEPT_SIGNALS))
+static int setsignalhandler OF((__GPRO__ savsigs_info **p_savedhandler_chain,
+                                int signal_type, void (*newhandler)(int)));
+#endif
+#ifndef SFX
+static void  help_extended      OF((__GPRO));
+static void  show_version_info  OF((__GPRO));
+#endif
+
+
+/*************/
+/* Constants */
+/*************/
+
+#include "consts.h"  /* all constant global variables are in here */
+                     /* (non-constant globals were moved to globals.c) */
+
+/* constant local variables: */
+
+#ifndef SFX
+#ifndef _WIN32_WCE /* Win CE does not support environment variables */
+   static ZCONST char Far EnvUnZip[] = ENV_UNZIP;
+   static ZCONST char Far EnvUnZip2[] = ENV_UNZIP2;
+   static ZCONST char Far EnvZipInfo[] = ENV_ZIPINFO;
+   static ZCONST char Far EnvZipInfo2[] = ENV_ZIPINFO2;
+#ifdef RISCOS
+   static ZCONST char Far EnvUnZipExts[] = ENV_UNZIPEXTS;
+#endif /* RISCOS */
+  static ZCONST char Far NoMemEnvArguments[] =
+    "envargs:  cannot get memory for arguments";
+#endif /* !_WIN32_WCE */
+  static ZCONST char Far CmdLineParamTooLong[] =
+    "error:  command line parameter #%d exceeds internal size limit\n";
+#endif /* !SFX */
+
+#if (defined(REENTRANT) && !defined(NO_EXCEPT_SIGNALS))
+  static ZCONST char Far CantSaveSigHandler[] =
+    "error:  cannot save signal handler settings\n";
+#endif
+
+#if (!defined(SFX) || defined(SFX_EXDIR))
+   static ZCONST char Far NotExtracting[] =
+     "caution:  not extracting; -d ignored\n";
+   static ZCONST char Far MustGiveExdir[] =
+     "error:  must specify directory to which to extract with -d option\n";
+   static ZCONST char Far OnlyOneExdir[] =
+     "error:  -d option used more than once (only one exdir allowed)\n";
+#endif
+#if (defined(UNICODE_SUPPORT) && !defined(UNICODE_WCHAR))
+  static ZCONST char Far UTF8EscapeUnSupp[] =
+    "warning:  -U \"escape all non-ASCII UTF-8 chars\" is not supported\n";
+#endif
+
+#if CRYPT
+   static ZCONST char Far MustGivePasswd[] =
+     "error:  must give decryption password with -P option\n";
+#endif
+
+#ifndef SFX
+   static ZCONST char Far Zfirst[] =
+   "error:  -Z must be first option for ZipInfo mode (check UNZIP variable?)\n";
+#endif
+static ZCONST char Far InvalidOptionsMsg[] = "error:\
+  -fn or any combination of -c, -l, -p, -t, -u and -v options invalid\n";
+static ZCONST char Far IgnoreOOptionMsg[] =
+  "caution:  both -n and -o specified; ignoring -o\n";
+
+/* usage() strings */
+#ifndef SFX
+#ifdef VMS
+   static ZCONST char Far Example3[] = "vms.c";
+   static ZCONST char Far Example2[] = "  unzip \"-V\" foo \"Bar\"\
+ (Quote names to preserve case, unless SET PROC/PARS=EXT)\n";
+#else /* !VMS */
+   static ZCONST char Far Example3[] = "ReadMe";
+#ifdef RISCOS
+   static ZCONST char Far Example2[] =
+"  unzip foo -d RAM:$   => extract all files from foo into RAMDisc\n";
+#else /* !RISCOS */
+#if (defined(OS2) || (defined(DOS_FLX_OS2_W32) && defined(MORE)))
+   static ZCONST char Far Example2[] =
+     "";                /* no room:  too many local3[] items */
+#else /* !OS2 */
+#ifdef MACOS
+   static ZCONST char Far Example2[] = ""; /* not needed */
+#else /* !MACOS */
+   static ZCONST char Far Example2[] = " \
+ unzip -p foo | more  => send contents of foo.zip via pipe into program more\n";
+#endif /* ?MACOS */
+#endif /* ?OS2 */
+#endif /* ?RISCOS */
+#endif /* ?VMS */
+
+/* local1[]:  command options */
+#if defined(TIMESTAMP)
+   static ZCONST char Far local1[] =
+     "  -T  timestamp archive to latest";
+#else /* !TIMESTAMP */
+   static ZCONST char Far local1[] = "";
+#endif /* ?TIMESTAMP */
+
+/* local2[] and local3[]:  modifier options */
+#ifdef DOS_FLX_H68_OS2_W32
+#ifdef FLEXOS
+   static ZCONST char Far local2[] = "";
+#else
+   static ZCONST char Far local2[] =
+     " -$  label removables (-$$ => fixed disks)";
+#endif
+#ifdef OS2
+#ifdef MORE
+   static ZCONST char Far local3[] = "\
+  -X  restore ACLs if supported              -s  spaces in filenames => '_'\n\
+                                             -M  pipe through \"more\" pager\n";
+#else
+   static ZCONST char Far local3[] = " \
+ -X  restore ACLs if supported              -s  spaces in filenames => '_'\n\n";
+#endif /* ?MORE */
+#else /* !OS2 */
+#ifdef WIN32
+#ifdef NTSD_EAS
+#ifdef MORE
+   static ZCONST char Far local3[] = "\
+  -X  restore ACLs (-XX => use privileges)   -s  spaces in filenames => '_'\n\
+                                             -M  pipe through \"more\" pager\n";
+#else
+   static ZCONST char Far local3[] = " \
+ -X  restore ACLs (-XX => use privileges)   -s  spaces in filenames => '_'\n\n";
+#endif /* ?MORE */
+#else /* !NTSD_EAS */
+#ifdef MORE
+   static ZCONST char Far local3[] = "\
+  -M  pipe through \"more\" pager            \
+  -s  spaces in filenames => '_'\n\n";
+#else
+   static ZCONST char Far local3[] = " \
+                                            -s  spaces in filenames => '_'\n\n";
+#endif /* ?MORE */
+#endif /* ?NTSD_EAS */
+#else /* !WIN32 */
+#ifdef MORE
+   static ZCONST char Far local3[] = "  -\
+M  pipe through \"more\" pager              -s  spaces in filenames => '_'\n\n";
+#else
+   static ZCONST char Far local3[] = "\
+                                             -s  spaces in filenames => '_'\n";
+#endif
+#endif /* ?WIN32 */
+#endif /* ?OS2 || ?WIN32 */
+#else /* !DOS_FLX_OS2_W32 */
+#ifdef VMS
+   static ZCONST char Far local2[] = " -X  restore owner/ACL protection info";
+#ifdef MORE
+   static ZCONST char Far local3[] = "\
+  -Y  treat \".nnn\" as \";nnn\" version         -2  force ODS2 names\n\
+  --D restore dir (-D: no) timestamps        -M  pipe through \"more\" pager\n\
+  (Must quote upper-case options, like \"-V\", unless SET PROC/PARSE=EXTEND.)\
+\n\n";
+#else
+   static ZCONST char Far local3[] = "\n\
+  -Y  treat \".nnn\" as \";nnn\" version         -2  force ODS2 names\n\
+  --D restore dir (-D: no) timestamps\n\
+  (Must quote upper-case options, like \"-V\", unless SET PROC/PARSE=EXTEND.)\
+\n\n";
+#endif
+#else /* !VMS */
+#ifdef ATH_BEO_UNX
+   static ZCONST char Far local2[] = " -X  restore UID/GID info";
+#ifdef MORE
+   static ZCONST char Far local3[] = "\
+  -K  keep setuid/setgid/tacky permissions   -M  pipe through \"more\" pager\n";
+#else
+   static ZCONST char Far local3[] = "\
+  -K  keep setuid/setgid/tacky permissions\n";
+#endif
+#else /* !ATH_BEO_UNX */
+#ifdef TANDEM
+   static ZCONST char Far local2[] = "\
+ -X  restore Tandem User ID                 -r  remove file extensions\n\
+  -b  create 'C' (180) text files          ";
+#ifdef MORE
+   static ZCONST char Far local3[] = " \
+                                            -M  pipe through \"more\" pager\n";
+#else
+   static ZCONST char Far local3[] = "\n";
+#endif
+#else /* !TANDEM */
+#ifdef AMIGA
+   static ZCONST char Far local2[] = " -N  restore comments as filenotes";
+#ifdef MORE
+   static ZCONST char Far local3[] = " \
+                                            -M  pipe through \"more\" pager\n";
+#else
+   static ZCONST char Far local3[] = "\n";
+#endif
+#else /* !AMIGA */
+#ifdef MACOS
+   static ZCONST char Far local2[] = " -E  show Mac info during extraction";
+   static ZCONST char Far local3[] = " \
+ -i  ignore filenames in mac extra info     -J  junk (ignore) Mac extra info\n\
+\n";
+#else /* !MACOS */
+#ifdef MORE
+   static ZCONST char Far local2[] = " -M  pipe through \"more\" pager";
+   static ZCONST char Far local3[] = "\n";
+#else
+   static ZCONST char Far local2[] = "";   /* Atari, Mac, CMS/MVS etc. */
+   static ZCONST char Far local3[] = "";
+#endif
+#endif /* ?MACOS */
+#endif /* ?AMIGA */
+#endif /* ?TANDEM */
+#endif /* ?ATH_BEO_UNX */
+#endif /* ?VMS */
+#endif /* ?DOS_FLX_OS2_W32 */
+#endif /* !SFX */
+
+#ifndef NO_ZIPINFO
+#ifdef VMS
+   static ZCONST char Far ZipInfoExample[] = "* or % (e.g., \"*font-%.zip\")";
+#else
+   static ZCONST char Far ZipInfoExample[] = "*, ?, [] (e.g., \"[a-j]*.zip\")";
+#endif
+
+static ZCONST char Far ZipInfoUsageLine1[] = "\
+ZipInfo %d.%d%d%s of %s, by Greg Roelofs and the Info-ZIP group.\n\
+\n\
+List name, date/time, attribute, size, compression method, etc., about files\n\
+in list (excluding those in xlist) contained in the specified .zip archive(s).\
+\n\"file[.zip]\" may be a wildcard name containing %s.\n\n\
+   usage:  zipinfo [-12smlvChMtTz] file[.zip] [list...] [-x xlist...]\n\
+      or:  unzip %s-Z%s [-12smlvChMtTz] file[.zip] [list...] [-x xlist...]\n";
+
+static ZCONST char Far ZipInfoUsageLine2[] = "\nmain\
+ listing-format options:             -s  short Unix \"ls -l\" format (def.)\n\
+  -1  filenames ONLY, one per line       -m  medium Unix \"ls -l\" format\n\
+  -2  just filenames but allow -h/-t/-z  -l  long Unix \"ls -l\" format\n\
+                                         -v  verbose, multi-page format\n";
+
+static ZCONST char Far ZipInfoUsageLine3[] = "miscellaneous options:\n\
+  -h  print header line       -t  print totals for listed files or for all\n\
+  -z  print zipfile comment   -T  print file times in sortable decimal format\
+\n  -C  be case-insensitive   %s\
+  -x  exclude filenames that follow from listing\n";
+#ifdef MORE
+   static ZCONST char Far ZipInfoUsageLine4[] =
+     "  -M  page output through built-in \"more\"\n";
+#else /* !MORE */
+   static ZCONST char Far ZipInfoUsageLine4[] = "";
+#endif /* ?MORE */
+#endif /* !NO_ZIPINFO */
+
+#ifdef BETA
+#  ifdef VMSCLI
+   /* BetaVersion[] is also used in vms/cmdline.c:  do not make it static */
+     ZCONST char Far BetaVersion[] = "%s\
+        THIS IS STILL A BETA VERSION OF UNZIP%s -- DO NOT DISTRIBUTE.\n\n";
+#  else
+     static ZCONST char Far BetaVersion[] = "%s\
+        THIS IS STILL A BETA VERSION OF UNZIP%s -- DO NOT DISTRIBUTE.\n\n";
+#  endif
+#endif
+
+#ifdef SFX
+#  ifdef VMSCLI
+   /* UnzipSFXBanner[] is also used in vms/cmdline.c:  do not make it static */
+     ZCONST char Far UnzipSFXBanner[] =
+#  else
+     static ZCONST char Far UnzipSFXBanner[] =
+#  endif
+     "UnZipSFX %d.%d%d%s of %s, by Info-ZIP (http://www.info-zip.org).\n";
+#  ifdef SFX_EXDIR
+     static ZCONST char Far UnzipSFXOpts[] =
+    "Valid options are -tfupcz and -d <exdir>; modifiers are -abjnoqCL%sV%s.\n";
+#  else
+     static ZCONST char Far UnzipSFXOpts[] =
+       "Valid options are -tfupcz; modifiers are -abjnoqCL%sV%s.\n";
+#  endif
+#else /* !SFX */
+   static ZCONST char Far CompileOptions[] =
+     "UnZip special compilation options:\n";
+   static ZCONST char Far CompileOptFormat[] = "        %s\n";
+#ifndef _WIN32_WCE /* Win CE does not support environment variables */
+   static ZCONST char Far EnvOptions[] =
+     "\nUnZip and ZipInfo environment options:\n";
+   static ZCONST char Far EnvOptFormat[] = "%16s:  %.1024s\n";
+#endif
+   static ZCONST char Far None[] = "[none]";
+#  ifdef ACORN_FTYPE_NFS
+     static ZCONST char Far AcornFtypeNFS[] = "ACORN_FTYPE_NFS";
+#  endif
+#  ifdef ASM_CRC
+     static ZCONST char Far AsmCRC[] = "ASM_CRC";
+#  endif
+#  ifdef ASM_INFLATECODES
+     static ZCONST char Far AsmInflateCodes[] = "ASM_INFLATECODES";
+#  endif
+#  ifdef CHECK_VERSIONS
+     static ZCONST char Far Check_Versions[] = "CHECK_VERSIONS";
+#  endif
+#  ifdef COPYRIGHT_CLEAN
+     static ZCONST char Far Copyright_Clean[] =
+     "COPYRIGHT_CLEAN (PKZIP 0.9x unreducing method not supported)";
+#  endif
+#  ifdef DEBUG
+     static ZCONST char Far UDebug[] = "DEBUG";
+#  endif
+#  ifdef DEBUG_TIME
+     static ZCONST char Far DebugTime[] = "DEBUG_TIME";
+#  endif
+#  ifdef DLL
+     static ZCONST char Far Dll[] = "DLL";
+#  endif
+#  ifdef DOSWILD
+     static ZCONST char Far DosWild[] = "DOSWILD";
+#  endif
+#  ifdef LZW_CLEAN
+     static ZCONST char Far LZW_Clean[] =
+     "LZW_CLEAN (PKZIP/Zip 1.x unshrinking method not supported)";
+#  endif
+#  ifndef MORE
+     static ZCONST char Far No_More[] = "NO_MORE";
+#  endif
+#  ifdef NO_ZIPINFO
+     static ZCONST char Far No_ZipInfo[] = "NO_ZIPINFO";
+#  endif
+#  ifdef NTSD_EAS
+     static ZCONST char Far NTSDExtAttrib[] = "NTSD_EAS";
+#  endif
+#  if defined(WIN32) && defined(NO_W32TIMES_IZFIX)
+     static ZCONST char Far W32NoIZTimeFix[] = "NO_W32TIMES_IZFIX";
+#  endif
+#  ifdef OLD_THEOS_EXTRA
+     static ZCONST char Far OldTheosExtra[] =
+     "OLD_THEOS_EXTRA (handle also old Theos port extra field)";
+#  endif
+#  ifdef OS2_EAS
+     static ZCONST char Far OS2ExtAttrib[] = "OS2_EAS";
+#  endif
+#  ifdef QLZIP
+     static ZCONST char Far SMSExFldOnUnix[] = "QLZIP";
+#  endif
+#  ifdef REENTRANT
+     static ZCONST char Far Reentrant[] = "REENTRANT";
+#  endif
+#  ifdef REGARGS
+     static ZCONST char Far RegArgs[] = "REGARGS";
+#  endif
+#  ifdef RETURN_CODES
+     static ZCONST char Far Return_Codes[] = "RETURN_CODES";
+#  endif
+#  ifdef SET_DIR_ATTRIB
+     static ZCONST char Far SetDirAttrib[] = "SET_DIR_ATTRIB";
+#  endif
+#  ifdef SYMLINKS
+     static ZCONST char Far SymLinkSupport[] =
+     "SYMLINKS (symbolic links supported, if RTL and file system permit)";
+#  endif
+#  ifdef TIMESTAMP
+     static ZCONST char Far TimeStamp[] = "TIMESTAMP";
+#  endif
+#  ifdef UNIXBACKUP
+     static ZCONST char Far UnixBackup[] = "UNIXBACKUP";
+#  endif
+#  ifdef USE_EF_UT_TIME
+     static ZCONST char Far Use_EF_UT_time[] = "USE_EF_UT_TIME";
+#  endif
+#  ifndef LZW_CLEAN
+     static ZCONST char Far Use_Unshrink[] =
+     "USE_UNSHRINK (PKZIP/Zip 1.x unshrinking method supported)";
+#  endif
+#  ifndef COPYRIGHT_CLEAN
+     static ZCONST char Far Use_Smith_Code[] =
+     "USE_SMITH_CODE (PKZIP 0.9x unreducing method supported)";
+#  endif
+#  ifdef USE_DEFLATE64
+     static ZCONST char Far Use_Deflate64[] =
+     "USE_DEFLATE64 (PKZIP 4.x Deflate64(tm) supported)";
+#  endif
+#  ifdef UNICODE_SUPPORT
+#   ifdef UTF8_MAYBE_NATIVE
+#    ifdef UNICODE_WCHAR
+       /* direct native UTF-8 check AND charset transform via wchar_t */
+       static ZCONST char Far Use_Unicode[] =
+       "UNICODE_SUPPORT [wide-chars, char coding: %s] (handle UTF-8 paths)";
+#    else
+       /* direct native UTF-8 check, only */
+       static ZCONST char Far Use_Unicode[] =
+       "UNICODE_SUPPORT [char coding: %s] (handle UTF-8 paths)";
+#    endif
+       static ZCONST char Far SysChUTF8[] = "UTF-8";
+       static ZCONST char Far SysChOther[] = "other";
+#   else /* !UTF8_MAYBE_NATIVE */
+       /* charset transform via wchar_t, no native UTF-8 support */
+       static ZCONST char Far Use_Unicode[] =
+       "UNICODE_SUPPORT [wide-chars] (handle UTF-8 paths)";
+#   endif /* ?UTF8_MAYBE_NATIVE */
+#  endif /* UNICODE_SUPPORT */
+#  ifdef _MBCS
+     static ZCONST char Far Have_MBCS_Support[] =
+     "MBCS-support (multibyte character support, MB_CUR_MAX = %u)";
+#  endif
+#  ifdef MULT_VOLUME
+     static ZCONST char Far Use_MultiVol[] =
+     "MULT_VOLUME (multi-volume archives supported)";
+#  endif
+#  ifdef LARGE_FILE_SUPPORT
+     static ZCONST char Far Use_LFS[] =
+     "LARGE_FILE_SUPPORT (large files over 2 GiB supported)";
+#  endif
+#  ifdef ZIP64_SUPPORT
+     static ZCONST char Far Use_Zip64[] =
+     "ZIP64_SUPPORT (archives using Zip64 for large files supported)";
+#  endif
+#  if (defined(__DJGPP__) && (__DJGPP__ >= 2))
+#    ifdef USE_DJGPP_ENV
+       static ZCONST char Far Use_DJGPP_Env[] = "USE_DJGPP_ENV";
+#    endif
+#    ifdef USE_DJGPP_GLOB
+       static ZCONST char Far Use_DJGPP_Glob[] = "USE_DJGPP_GLOB";
+#    endif
+#  endif /* __DJGPP__ && (__DJGPP__ >= 2) */
+#  ifdef USE_VFAT
+     static ZCONST char Far Use_VFAT_support[] = "USE_VFAT";
+#  endif
+#  ifdef USE_ZLIB
+     static ZCONST char Far UseZlib[] =
+     "USE_ZLIB (compiled with version %s; using version %s)";
+#  endif
+#  ifdef USE_BZIP2
+     static ZCONST char Far UseBZip2[] =
+     "USE_BZIP2 (PKZIP 4.6+, using bzip2 lib version %s)";
+#  endif
+#  ifdef VMS_TEXT_CONV
+     static ZCONST char Far VmsTextConv[] = "VMS_TEXT_CONV";
+#  endif
+#  ifdef VMSCLI
+     static ZCONST char Far VmsCLI[] = "VMSCLI";
+#  endif
+#  ifdef VMSWILD
+     static ZCONST char Far VmsWild[] = "VMSWILD";
+#  endif
+#  ifdef WILD_STOP_AT_DIR
+     static ZCONST char Far WildStopAtDir[] = "WILD_STOP_AT_DIR";
+#  endif
+#  if CRYPT
+#    ifdef PASSWD_FROM_STDIN
+       static ZCONST char Far PasswdStdin[] = "PASSWD_FROM_STDIN";
+#    endif
+     static ZCONST char Far Decryption[] =
+       "        [decryption, version %d.%d%s of %s]\n";
+     static ZCONST char Far CryptDate[] = CR_VERSION_DATE;
+#  endif
+#  ifndef __RSXNT__
+#    ifdef __EMX__
+       static ZCONST char Far EnvEMX[] = "EMX";
+       static ZCONST char Far EnvEMXOPT[] = "EMXOPT";
+#    endif
+#    if (defined(__GO32__) && (!defined(__DJGPP__) || (__DJGPP__ < 2)))
+       static ZCONST char Far EnvGO32[] = "GO32";
+       static ZCONST char Far EnvGO32TMP[] = "GO32TMP";
+#    endif
+#  endif /* !__RSXNT__ */
+
+#ifdef VMS
+/* UnzipUsageLine1[] is also used in vms/cmdline.c:  do not make it static */
+   ZCONST char Far UnzipUsageLine1[] = "\
+UnZip %d.%d%d%s of %s, by Info-ZIP.  For more details see: unzip -v.\n\n";
+# ifdef COPYRIGHT_CLEAN
+   static ZCONST char Far UnzipUsageLine1v[] = "\
+UnZip %d.%d%d%s of %s, by Info-ZIP.  Maintained by C. Spieler.  Send\n\
+bug reports using http://www.info-zip.org/zip-bug.html; see README for details.\
+\n\n";
+# else
+   static ZCONST char Far UnzipUsageLine1v[] = "\
+UnZip %d.%d%d%s of %s, by Info-ZIP.  UnReduce (c) 1989 by S. H. Smith.\n\
+Send bug reports using //www.info-zip.org/zip-bug.html; see README for details.\
+\n\n";
+# endif /* ?COPYRIGHT_CLEAN */
+#else /* !VMS */
+# ifdef COPYRIGHT_CLEAN
+   static ZCONST char Far UnzipUsageLine1[] = "\
+UnZip %d.%d%d%s of %s, by Info-ZIP.  Maintained by C. Spieler.  Send\n\
+bug reports using http://www.info-zip.org/zip-bug.html; see README for details.\
+\n\n";
+# else
+   static ZCONST char Far UnzipUsageLine1[] = "\
+UnZip %d.%d%d%s of %s, by Info-ZIP.  UnReduce (c) 1989 by S. H. Smith.\n\
+Send bug reports using //www.info-zip.org/zip-bug.html; see README for details.\
+\n\n";
+# endif /* ?COPYRIGHT_CLEAN */
+# define UnzipUsageLine1v       UnzipUsageLine1
+#endif /* ?VMS */
+
+static ZCONST char Far UnzipUsageLine2v[] = "\
+Latest sources and executables are at ftp://ftp.info-zip.org/pub/infozip/ ;\
+\nsee ftp://ftp.info-zip.org/pub/infozip/UnZip.html for other sites.\
+\n\n";
+
+#ifdef MACOS
+static ZCONST char Far UnzipUsageLine2[] = "\
+Usage: unzip %s[-opts[modifiers]] file[.zip] [list] [-d exdir]\n \
+ Default action is to extract files in list, to exdir;\n\
+  file[.zip] may be a wildcard.  %s\n";
+#else /* !MACOS */
+#ifdef VM_CMS
+static ZCONST char Far UnzipUsageLine2[] = "\
+Usage: unzip %s[-opts[modifiers]] file[.zip] [list] [-x xlist] [-d fm]\n \
+ Default action is to extract files in list, except those in xlist, to disk fm;\
+\n  file[.zip] may be a wildcard.  %s\n";
+#else /* !VM_CMS */
+static ZCONST char Far UnzipUsageLine2[] = "\
+Usage: unzip %s[-opts[modifiers]] file[.zip] [list] [-x xlist] [-d exdir]\n \
+ Default action is to extract files in list, except those in xlist, to exdir;\n\
+  file[.zip] may be a wildcard.  %s\n";
+#endif /* ?VM_CMS */
+#endif /* ?MACOS */
+
+#ifdef NO_ZIPINFO
+#  define ZIPINFO_MODE_OPTION  ""
+   static ZCONST char Far ZipInfoMode[] =
+     "(ZipInfo mode is disabled in this version.)";
+#else
+#  define ZIPINFO_MODE_OPTION  "[-Z] "
+   static ZCONST char Far ZipInfoMode[] =
+     "-Z => ZipInfo mode (\"unzip -Z\" for usage).";
+#endif /* ?NO_ZIPINFO */
+
+#ifdef VMS
+   static ZCONST char Far VMSusageLine2b[] = "\
+=> define foreign command symbol in LOGIN.COM:  $ unzip :== $dev:[dir]unzip.exe\
+\n";
+#endif
+
+#ifdef MACOS
+static ZCONST char Far UnzipUsageLine3[] = "\n\
+  -d  extract files into exdir               -l  list files (short format)\n\
+  -f  freshen existing files, create none    -t  test compressed archive data\n\
+  -u  update files, create if necessary      -z  display archive comment only\n\
+  -v  list verbosely/show version info     %s\n";
+#else /* !MACOS */
+#ifdef VM_CMS
+static ZCONST char Far UnzipUsageLine3[] = "\n\
+  -p  extract files to pipe, no messages     -l  list files (short format)\n\
+  -f  freshen existing files, create none    -t  test compressed archive data\n\
+  -u  update files, create if necessary      -z  display archive comment only\n\
+  -v  list verbosely/show version info     %s\n\
+  -x  exclude files that follow (in xlist)   -d  extract files onto disk fm\n";
+#else /* !VM_CMS */
+static ZCONST char Far UnzipUsageLine3[] = "\n\
+  -p  extract files to pipe, no messages     -l  list files (short format)\n\
+  -f  freshen existing files, create none    -t  test compressed archive data\n\
+  -u  update files, create if necessary      -z  display archive comment only\n\
+  -v  list verbosely/show version info     %s\n\
+  -x  exclude files that follow (in xlist)   -d  extract files into exdir\n";
+#endif /* ?VM_CMS */
+#endif /* ?MACOS */
+
+/* There is not enough space on a standard 80x25 Windows console screen for
+ * the additional line advertising the UTF-8 debugging options. This may
+ * eventually also be the case for other ports. Probably, the -U option need
+ * not be shown on the introductory screen at all. [Chr. Spieler, 2008-02-09]
+ *
+ * Likely, other advanced options should be moved to an extended help page and
+ * the option to list that page put here.  [E. Gordon, 2008-3-16]
+ */
+#if (defined(UNICODE_SUPPORT) && !defined(WIN32))
+#ifdef VMS
+static ZCONST char Far UnzipUsageLine4[] = "\
+modifiers:\n\
+  -n  never overwrite or make a new version of an existing file\n\
+  -o  always make a new version (-oo: overwrite original) of an existing file\n\
+  -q  quiet mode (-qq => quieter)            -a  auto-convert any text files\n\
+  -j  junk paths (do not make directories)   -aa treat ALL files as text\n\
+  -U  use escapes for all non-ASCII Unicode  -UU ignore any Unicode fields\n\
+  -C  match filenames case-insensitively     -L  make (some) names \
+lowercase\n %-42s  -V  retain VMS version numbers\n%s";
+#else /* !VMS */
+static ZCONST char Far UnzipUsageLine4[] = "\
+modifiers:\n\
+  -n  never overwrite existing files         -q  quiet mode (-qq => quieter)\n\
+  -o  overwrite files WITHOUT prompting      -a  auto-convert any text files\n\
+  -j  junk paths (do not make directories)   -aa treat ALL files as text\n\
+  -U  use escapes for all non-ASCII Unicode  -UU ignore any Unicode fields\n\
+  -C  match filenames case-insensitively     -L  make (some) names \
+lowercase\n %-42s  -V  retain VMS version numbers\n%s";
+#endif /* ?VMS */
+#else /* !UNICODE_SUPPORT */
+#ifdef VMS
+static ZCONST char Far UnzipUsageLine4[] = "\
+modifiers:\n\
+  -n  never overwrite or make a new version of an existing file\n\
+  -o  always make a new version (-oo: overwrite original) of an existing file\n\
+  -q  quiet mode (-qq => quieter)            -a  auto-convert any text files\n\
+  -j  junk paths (do not make directories)   -aa treat ALL files as text\n\
+  -C  match filenames case-insensitively     -L  make (some) names \
+lowercase\n %-42s  -V  retain VMS version numbers\n%s";
+#else /* !VMS */
+static ZCONST char Far UnzipUsageLine4[] = "\
+modifiers:\n\
+  -n  never overwrite existing files         -q  quiet mode (-qq => quieter)\n\
+  -o  overwrite files WITHOUT prompting      -a  auto-convert any text files\n\
+  -j  junk paths (do not make directories)   -aa treat ALL files as text\n\
+  -C  match filenames case-insensitively     -L  make (some) names \
+lowercase\n %-42s  -V  retain VMS version numbers\n%s";
+#endif /* ?VMS */
+#endif /* ?UNICODE_SUPPORT */
+
+static ZCONST char Far UnzipUsageLine5[] = "\
+See \"unzip -hh\" or unzip.txt for more help.  Examples:\n\
+  unzip data1 -x joe   => extract all files except joe from zipfile data1.zip\n\
+%s\
+  unzip -fo foo %-6s => quietly replace existing %s if archive file newer\n";
+#endif /* ?SFX */
+
+
+
+
+
+/*****************************/
+/*  main() / UzpMain() stub  */
+/*****************************/
+
+int MAIN(argc, argv)   /* return PK-type error code (except under VMS) */
+    int argc;
+    char *argv[];
+{
+    int r;
+
+    CONSTRUCTGLOBALS();
+    r = unzip(__G__ argc, argv);
+    DESTROYGLOBALS();
+    RETURN(r);
+}
+
+
+
+
+/*******************************/
+/*  Primary UnZip entry point  */
+/*******************************/
+
+int unzip(__G__ argc, argv)
+    __GDEF
+    int argc;
+    char *argv[];
+{
+#ifndef NO_ZIPINFO
+    char *p;
+#endif
+#if (defined(DOS_FLX_H68_NLM_OS2_W32) || !defined(SFX))
+    int i;
+#endif
+    int retcode, error=FALSE;
+#ifndef NO_EXCEPT_SIGNALS
+#ifdef REENTRANT
+    savsigs_info *oldsighandlers = NULL;
+#   define SET_SIGHANDLER(sigtype, newsighandler) \
+      if ((retcode = setsignalhandler(__G__ &oldsighandlers, (sigtype), \
+                                      (newsighandler))) > PK_WARN) \
+          goto cleanup_and_exit
+#else
+#   define SET_SIGHANDLER(sigtype, newsighandler) \
+      signal((sigtype), (newsighandler))
+#endif
+#endif /* NO_EXCEPT_SIGNALS */
+
+    /* initialize international char support to the current environment */
+    SETLOCALE(LC_CTYPE, "");
+
+#ifdef UNICODE_SUPPORT
+    /* see if can use UTF-8 Unicode locale */
+# ifdef UTF8_MAYBE_NATIVE
+    {
+        char *codeset;
+#  if !(defined(NO_NL_LANGINFO) || defined(NO_LANGINFO_H))
+        /* get the codeset (character set encoding) currently used */
+#       include <langinfo.h>
+
+        codeset = nl_langinfo(CODESET);
+#  else /* NO_NL_LANGINFO || NO_LANGINFO_H */
+        /* query the current locale setting for character classification */
+        codeset = setlocale(LC_CTYPE, NULL);
+        if (codeset != NULL) {
+            /* extract the codeset portion of the locale name */
+            codeset = strchr(codeset, '.');
+            if (codeset != NULL) ++codeset;
+        }
+#  endif /* ?(NO_NL_LANGINFO || NO_LANGINFO_H) */
+        /* is the current codeset UTF-8 ? */
+        if ((codeset != NULL) && (strcmp(codeset, "UTF-8") == 0)) {
+            /* successfully found UTF-8 char coding */
+            G.native_is_utf8 = TRUE;
+        } else {
+            /* Current codeset is not UTF-8 or cannot be determined. */
+            G.native_is_utf8 = FALSE;
+        }
+        /* Note: At least for UnZip, trying to change the process codeset to
+         *       UTF-8 does not work.  For the example Linux setup of the
+         *       UnZip maintainer, a successful switch to "en-US.UTF-8"
+         *       resulted in garbage display of all non-basic ASCII characters.
+         */
+    }
+# endif /* UTF8_MAYBE_NATIVE */
+
+    /* initialize Unicode */
+    G.unicode_escape_all = 0;
+    G.unicode_mismatch = 0;
+
+    G.unipath_version = 0;
+    G.unipath_checksum = 0;
+    G.unipath_filename = NULL;
+#endif /* UNICODE_SUPPORT */
+
+
+#if (defined(__IBMC__) && defined(__DEBUG_ALLOC__))
+    extern void DebugMalloc(void);
+
+    atexit(DebugMalloc);
+#endif
+
+#ifdef MALLOC_WORK
+    /* The following (rather complex) expression determines the allocation
+       size of the decompression work area.  It simulates what the
+       combined "union" and "struct" declaration of the "static" work
+       area reservation achieves automatically at compile time.
+       Any decent compiler should evaluate this expression completely at
+       compile time and provide constants to the zcalloc() call.
+       (For better readability, some subexpressions are encapsulated
+       in temporarly defined macros.)
+     */
+#   define UZ_SLIDE_CHUNK (sizeof(shrint)+sizeof(uch)+sizeof(uch))
+#   define UZ_NUMOF_CHUNKS \
+      (unsigned)(((WSIZE+UZ_SLIDE_CHUNK-1)/UZ_SLIDE_CHUNK > HSIZE) ? \
+                 (WSIZE+UZ_SLIDE_CHUNK-1)/UZ_SLIDE_CHUNK : HSIZE)
+    G.area.Slide = (uch *)zcalloc(UZ_NUMOF_CHUNKS, UZ_SLIDE_CHUNK);
+#   undef UZ_SLIDE_CHUNK
+#   undef UZ_NUMOF_CHUNKS
+    G.area.shrink.Parent = (shrint *)G.area.Slide;
+    G.area.shrink.value = G.area.Slide + (sizeof(shrint)*(HSIZE));
+    G.area.shrink.Stack = G.area.Slide +
+                           (sizeof(shrint) + sizeof(uch))*(HSIZE);
+#endif
+
+/*---------------------------------------------------------------------------
+    Set signal handler for restoring echo, warn of zipfile corruption, etc.
+  ---------------------------------------------------------------------------*/
+#ifndef NO_EXCEPT_SIGNALS
+#ifdef SIGINT
+    SET_SIGHANDLER(SIGINT, handler);
+#endif
+#ifdef SIGTERM                 /* some systems really have no SIGTERM */
+    SET_SIGHANDLER(SIGTERM, handler);
+#endif
+#if defined(SIGABRT) && !(defined(AMIGA) && defined(__SASC))
+    SET_SIGHANDLER(SIGABRT, handler);
+#endif
+#ifdef SIGBREAK
+    SET_SIGHANDLER(SIGBREAK, handler);
+#endif
+#ifdef SIGBUS
+    SET_SIGHANDLER(SIGBUS, handler);
+#endif
+#ifdef SIGILL
+    SET_SIGHANDLER(SIGILL, handler);
+#endif
+#ifdef SIGSEGV
+    SET_SIGHANDLER(SIGSEGV, handler);
+#endif
+#endif /* NO_EXCEPT_SIGNALS */
+
+#if (defined(WIN32) && defined(__RSXNT__))
+    for (i = 0 ; i < argc; i++) {
+        _ISO_INTERN(argv[i]);
+    }
+#endif
+
+/*---------------------------------------------------------------------------
+    Macintosh initialization code.
+  ---------------------------------------------------------------------------*/
+
+#ifdef MACOS
+    {
+        int a;
+
+        for (a = 0;  a < 4;  ++a)
+            G.rghCursor[a] = GetCursor(a+128);
+        G.giCursor = 0;
+    }
+#endif
+
+/*---------------------------------------------------------------------------
+    NetWare initialization code.
+  ---------------------------------------------------------------------------*/
+
+#ifdef NLM
+    InitUnZipConsole();
+#endif
+
+/*---------------------------------------------------------------------------
+    Acorn RISC OS initialization code.
+  ---------------------------------------------------------------------------*/
+
+#ifdef RISCOS
+    set_prefix();
+#endif
+
+/*---------------------------------------------------------------------------
+    Theos initialization code.
+  ---------------------------------------------------------------------------*/
+
+#ifdef THEOS
+    /* The easiest way found to force creation of libraries when selected
+     * members are to be unzipped. Explicitly add libraries names to the
+     * arguments list before the first member of the library.
+     */
+    if (! _setargv(&argc, &argv)) {
+        Info(slide, 0x401, ((char *)slide, "cannot process argv\n"));
+        retcode = PK_MEM;
+        goto cleanup_and_exit;
+    }
+#endif
+
+/*---------------------------------------------------------------------------
+    Sanity checks.  Commentary by Otis B. Driftwood and Fiorello:
+
+    D:  It's all right.  That's in every contract.  That's what they
+        call a sanity clause.
+
+    F:  Ha-ha-ha-ha-ha.  You can't fool me.  There ain't no Sanity
+        Claus.
+  ---------------------------------------------------------------------------*/
+
+#ifdef DEBUG
+# ifdef LARGE_FILE_SUPPORT
+  /* test if we can support large files - 10/6/04 EG */
+    if (sizeof(zoff_t) < 8) {
+        Info(slide, 0x401, ((char *)slide, "LARGE_FILE_SUPPORT set but not supported\n"));
+        retcode = PK_BADERR;
+        goto cleanup_and_exit;
+    }
+    /* test if we can show 64-bit values */
+    {
+        zoff_t z = ~(zoff_t)0;  /* z should be all 1s now */
+        char *sz;
+
+        sz = FmZofft(z, FZOFFT_HEX_DOT_WID, "X");
+        if ((sz[0] != 'F') || (strlen(sz) != 16))
+        {
+            z = 0;
+        }
+
+        /* shift z so only MSB is set */
+        z <<= 63;
+        sz = FmZofft(z, FZOFFT_HEX_DOT_WID, "X");
+        if ((sz[0] != '8') || (strlen(sz) != 16))
+        {
+            Info(slide, 0x401, ((char *)slide,
+              "Can't show 64-bit values correctly\n"));
+            retcode = PK_BADERR;
+            goto cleanup_and_exit;
+        }
+    }
+# endif /* LARGE_FILE_SUPPORT */
+
+    /* 2004-11-30 SMS.
+       Test the NEXTBYTE macro for proper operation.
+    */
+    {
+        int test_char;
+        static uch test_buf[2] = { 'a', 'b' };
+
+        G.inptr = test_buf;
+        G.incnt = 1;
+
+        test_char = NEXTBYTE;           /* Should get 'a'. */
+        if (test_char == 'a')
+        {
+            test_char = NEXTBYTE;       /* Should get EOF, not 'b'. */
+        }
+        if (test_char != EOF)
+        {
+            Info(slide, 0x401, ((char *)slide,
+ "NEXTBYTE macro failed.  Try compiling with ALT_NEXTBYTE defined?"));
+
+            retcode = PK_BADERR;
+            goto cleanup_and_exit;
+        }
+    }
+#endif /* DEBUG */
+
+/*---------------------------------------------------------------------------
+    First figure out if we're running in UnZip mode or ZipInfo mode, and put
+    the appropriate environment-variable options into the queue.  Then rip
+    through any command-line options lurking about...
+  ---------------------------------------------------------------------------*/
+
+#ifdef SFX
+    G.argv0 = argv[0];
+#if (defined(OS2) || defined(WIN32))
+    G.zipfn = GetLoadPath(__G);/* non-MSC NT puts path into G.filename[] */
+#else
+    G.zipfn = G.argv0;
+#endif
+
+#ifdef VMSCLI
+    {
+        ulg status = vms_unzip_cmdline(&argc, &argv);
+        if (!(status & 1)) {
+            retcode = (int)status;
+            goto cleanup_and_exit;
+        }
+    }
+#endif /* VMSCLI */
+
+    uO.zipinfo_mode = FALSE;
+    error = uz_opts(__G__ &argc, &argv);   /* UnZipSFX call only */
+
+#else /* !SFX */
+
+#ifdef RISCOS
+    /* get the extensions to swap from environment */
+    getRISCOSexts(ENV_UNZIPEXTS);
+#endif
+
+#ifdef MSDOS
+    /* extract MKS extended argument list from environment (before envargs!) */
+    mksargs(&argc, &argv);
+#endif
+
+#ifdef VMSCLI
+    {
+        ulg status = vms_unzip_cmdline(&argc, &argv);
+        if (!(status & 1)) {
+            retcode = (int)status;
+            goto cleanup_and_exit;
+        }
+    }
+#endif /* VMSCLI */
+
+    G.noargs = (argc == 1);   /* no options, no zipfile, no anything */
+
+#ifndef NO_ZIPINFO
+    for (p = argv[0] + strlen(argv[0]); p >= argv[0]; --p) {
+        if (*p == DIR_END
+#ifdef DIR_END2
+            || *p == DIR_END2
+#endif
+           )
+            break;
+    }
+    ++p;
+
+#ifdef THEOS
+    if (strncmp(p, "ZIPINFO.",8) == 0 || strstr(p, ".ZIPINFO:") != NULL ||
+        strncmp(p, "II.",3) == 0 || strstr(p, ".II:") != NULL ||
+#else
+    if (STRNICMP(p, LoadFarStringSmall(Zipnfo), 7) == 0 ||
+        STRNICMP(p, "ii", 2) == 0 ||
+#endif
+        (argc > 1 && strncmp(argv[1], "-Z", 2) == 0))
+    {
+        uO.zipinfo_mode = TRUE;
+#ifndef _WIN32_WCE /* Win CE does not support environment variables */
+        if ((error = envargs(&argc, &argv, LoadFarStringSmall(EnvZipInfo),
+                             LoadFarStringSmall2(EnvZipInfo2))) != PK_OK)
+            perror(LoadFarString(NoMemEnvArguments));
+#endif
+    } else
+#endif /* !NO_ZIPINFO */
+    {
+        uO.zipinfo_mode = FALSE;
+#ifndef _WIN32_WCE /* Win CE does not support environment variables */
+        if ((error = envargs(&argc, &argv, LoadFarStringSmall(EnvUnZip),
+                             LoadFarStringSmall2(EnvUnZip2))) != PK_OK)
+            perror(LoadFarString(NoMemEnvArguments));
+#endif
+    }
+
+    if (!error) {
+        /* Check the length of all passed command line parameters.
+         * Command arguments might get sent through the Info() message
+         * system, which uses the sliding window area as string buffer.
+         * As arguments may additionally get fed through one of the FnFilter
+         * macros, we require all command line arguments to be shorter than
+         * WSIZE/4 (and ca. 2 standard line widths for fixed message text).
+         */
+        for (i = 1 ; i < argc; i++) {
+           if (strlen(argv[i]) > ((WSIZE>>2) - 160)) {
+               Info(slide, 0x401, ((char *)slide,
+                 LoadFarString(CmdLineParamTooLong), i));
+               retcode = PK_PARAM;
+               goto cleanup_and_exit;
+           }
+        }
+#ifndef NO_ZIPINFO
+        if (uO.zipinfo_mode)
+            error = zi_opts(__G__ &argc, &argv);
+        else
+#endif /* !NO_ZIPINFO */
+            error = uz_opts(__G__ &argc, &argv);
+    }
+
+#endif /* ?SFX */
+
+    if ((argc < 0) || error) {
+        retcode = error;
+        goto cleanup_and_exit;
+    }
+
+/*---------------------------------------------------------------------------
+    Now get the zipfile name from the command line and then process any re-
+    maining options and file specifications.
+  ---------------------------------------------------------------------------*/
+
+#ifdef DOS_FLX_H68_NLM_OS2_W32
+    /* convert MSDOS-style 'backward slash' directory separators to Unix-style
+     * 'forward slashes' for user's convenience (include zipfile name itself)
+     */
+#ifdef SFX
+    for (G.pfnames = argv, i = argc;  i > 0;  --i) {
+#else
+    /* argc does not include the zipfile specification */
+    for (G.pfnames = argv, i = argc+1;  i > 0;  --i) {
+#endif
+#ifdef __human68k__
+        extern char *_toslash(char *);
+        _toslash(*G.pfnames);
+#else /* !__human68k__ */
+        char *q = *G.pfnames;
+
+        while (*q != '\0') {
+            if (*q == '\\')
+                *q = '/';
+            INCSTR(q);
+        }
+#endif /* ?__human68k__ */
+        ++G.pfnames;
+    }
+#endif /* DOS_FLX_H68_NLM_OS2_W32 */
+
+#ifndef SFX
+    G.wildzipfn = *argv++;
+#endif
+
+#if (defined(SFX) && !defined(SFX_EXDIR)) /* only check for -x */
+
+    G.filespecs = argc;
+    G.xfilespecs = 0;
+
+    if (argc > 0) {
+        char **pp = argv-1;
+
+        G.pfnames = argv;
+        while (*++pp)
+            if (strcmp(*pp, "-x") == 0) {
+                if (pp > argv) {
+                    *pp = 0;              /* terminate G.pfnames */
+                    G.filespecs = pp - G.pfnames;
+                } else {
+                    G.pfnames = (char **)fnames;  /* defaults */
+                    G.filespecs = 0;
+                }
+                G.pxnames = pp + 1;      /* excluded-names ptr: _after_ -x */
+                G.xfilespecs = argc - G.filespecs - 1;
+                break;                    /* skip rest of args */
+            }
+        G.process_all_files = FALSE;
+    } else
+        G.process_all_files = TRUE;      /* for speed */
+
+#else /* !SFX || SFX_EXDIR */             /* check for -x or -d */
+
+    G.filespecs = argc;
+    G.xfilespecs = 0;
+
+    if (argc > 0) {
+        int in_files=FALSE, in_xfiles=FALSE;
+        char **pp = argv-1;
+
+        G.process_all_files = FALSE;
+        G.pfnames = argv;
+        while (*++pp) {
+            Trace((stderr, "pp - argv = %d\n", pp-argv));
+#ifdef CMS_MVS
+            if (!uO.exdir && STRNICMP(*pp, "-d", 2) == 0) {
+#else
+            if (!uO.exdir && strncmp(*pp, "-d", 2) == 0) {
+#endif
+                int firstarg = (pp == argv);
+
+                uO.exdir = (*pp) + 2;
+                if (in_files) {      /* ... zipfile ... -d exdir ... */
+                    *pp = (char *)NULL;         /* terminate G.pfnames */
+                    G.filespecs = pp - G.pfnames;
+                    in_files = FALSE;
+                } else if (in_xfiles) {
+                    *pp = (char *)NULL;         /* terminate G.pxnames */
+                    G.xfilespecs = pp - G.pxnames;
+                    /* "... -x xlist -d exdir":  nothing left */
+                }
+                /* first check for "-dexdir", then for "-d exdir" */
+                if (*uO.exdir == '\0') {
+                    if (*++pp)
+                        uO.exdir = *pp;
+                    else {
+                        Info(slide, 0x401, ((char *)slide,
+                          LoadFarString(MustGiveExdir)));
+                        /* don't extract here by accident */
+                        retcode = PK_PARAM;
+                        goto cleanup_and_exit;
+                    }
+                }
+                if (firstarg) { /* ... zipfile -d exdir ... */
+                    if (pp[1]) {
+                        G.pfnames = pp + 1;  /* argv+2 */
+                        G.filespecs = argc - (G.pfnames-argv);  /* for now... */
+                    } else {
+                        G.process_all_files = TRUE;
+                        G.pfnames = (char **)fnames;  /* GRR: necessary? */
+                        G.filespecs = 0;     /* GRR: necessary? */
+                        break;
+                    }
+                }
+            } else if (!in_xfiles) {
+                if (strcmp(*pp, "-x") == 0) {
+                    in_xfiles = TRUE;
+                    if (pp == G.pfnames) {
+                        G.pfnames = (char **)fnames;  /* defaults */
+                        G.filespecs = 0;
+                    } else if (in_files) {
+                        *pp = 0;                   /* terminate G.pfnames */
+                        G.filespecs = pp - G.pfnames;  /* adjust count */
+                        in_files = FALSE;
+                    }
+                    G.pxnames = pp + 1; /* excluded-names ptr starts after -x */
+                    G.xfilespecs = argc - (G.pxnames-argv);  /* anything left */
+                } else
+                    in_files = TRUE;
+            }
+        }
+    } else
+        G.process_all_files = TRUE;      /* for speed */
+
+    if (uO.exdir != (char *)NULL && !G.extract_flag)    /* -d ignored */
+        Info(slide, 0x401, ((char *)slide, LoadFarString(NotExtracting)));
+#endif /* ?(SFX && !SFX_EXDIR) */
+
+#ifdef UNICODE_SUPPORT
+    /* set Unicode-escape-all if option -U used */
+    if (uO.U_flag == 1)
+# ifdef UNICODE_WCHAR
+        G.unicode_escape_all = TRUE;
+# else
+        Info(slide, 0x401, ((char *)slide, LoadFarString(UTF8EscapeUnSupp)));
+# endif
+#endif
+
+
+/*---------------------------------------------------------------------------
+    Okey dokey, we have everything we need to get started.  Let's roll.
+  ---------------------------------------------------------------------------*/
+
+    retcode = process_zipfiles(__G);
+
+cleanup_and_exit:
+#if (defined(REENTRANT) && !defined(NO_EXCEPT_SIGNALS))
+    /* restore all signal handlers back to their state at function entry */
+    while (oldsighandlers != NULL) {
+        savsigs_info *thissigsav = oldsighandlers;
+
+        signal(thissigsav->sigtype, thissigsav->sighandler);
+        oldsighandlers = thissigsav->previous;
+        free(thissigsav);
+    }
+#endif
+#if (defined(MALLOC_WORK) && !defined(REENTRANT))
+    if (G.area.Slide != (uch *)NULL) {
+        free(G.area.Slide);
+        G.area.Slide = (uch *)NULL;
+    }
+#endif
+#if (defined(MSDOS) && !defined(SFX) && !defined(WINDLL))
+    if (retcode != PK_OK)
+        check_for_windows("UnZip");
+#endif
+    return(retcode);
+
+} /* end main()/unzip() */
+
+
+
+
+
+#if (defined(REENTRANT) && !defined(NO_EXCEPT_SIGNALS))
+/*******************************/
+/* Function setsignalhandler() */
+/*******************************/
+
+static int setsignalhandler(__G__ p_savedhandler_chain, signal_type,
+                            newhandler)
+    __GDEF
+    savsigs_info **p_savedhandler_chain;
+    int signal_type;
+    void (*newhandler)(int);
+{
+    savsigs_info *savsig;
+
+    savsig = malloc(sizeof(savsigs_info));
+    if (savsig == NULL) {
+        /* error message and break */
+        Info(slide, 0x401, ((char *)slide, LoadFarString(CantSaveSigHandler)));
+        return PK_MEM;
+    }
+    savsig->sigtype = signal_type;
+    savsig->sighandler = signal(SIGINT, newhandler);
+    if (savsig->sighandler == SIG_ERR) {
+        free(savsig);
+    } else {
+        savsig->previous = *p_savedhandler_chain;
+        *p_savedhandler_chain = savsig;
+    }
+    return PK_OK;
+
+} /* end function setsignalhandler() */
+
+#endif /* REENTRANT && !NO_EXCEPT_SIGNALS */
+
+
+
+
+
+/**********************/
+/* Function uz_opts() */
+/**********************/
+
+int uz_opts(__G__ pargc, pargv)
+    __GDEF
+    int *pargc;
+    char ***pargv;
+{
+    char **argv, *s;
+    int argc, c, error=FALSE, negative=0, showhelp=0;
+
+
+    argc = *pargc;
+    argv = *pargv;
+
+    while (++argv, (--argc > 0 && *argv != NULL && **argv == '-')) {
+        s = *argv + 1;
+        while ((c = *s++) != 0) {    /* "!= 0":  prevent Turbo C warning */
+#ifdef CMS_MVS
+            switch (tolower(c))
+#else
+            switch (c)
+#endif
+            {
+                case ('-'):
+                    ++negative;
+                    break;
+#ifdef RISCOS
+                case ('/'):
+                    if (negative) {   /* negative not allowed with -/ swap */
+                        Info(slide, 0x401, ((char *)slide,
+                          "error:  must give extensions list"));
+                        return(PK_PARAM);  /* don't extract here by accident */
+                    }
+                    exts2swap = s; /* override Unzip$Exts */
+                    s += strlen(s);
+                    break;
+#endif
+                case ('a'):
+                    if (negative) {
+                        uO.aflag = MAX(uO.aflag-negative,0);
+                        negative = 0;
+                    } else
+                        ++uO.aflag;
+                    break;
+#if (defined(DLL) && defined(API_DOC))
+                case ('A'):    /* extended help for API */
+                    APIhelp(__G__ argc, argv);
+                    *pargc = -1;  /* signal to exit successfully */
+                    return 0;
+#endif
+                case ('b'):
+                    if (negative) {
+#if (defined(TANDEM) || defined(VMS))
+                        uO.bflag = MAX(uO.bflag-negative,0);
+#endif
+                        negative = 0;   /* do nothing:  "-b" is default */
+                    } else {
+#ifdef VMS
+                        if (uO.aflag == 0)
+                           ++uO.bflag;
+#endif
+#ifdef TANDEM
+                        ++uO.bflag;
+#endif
+                        uO.aflag = 0;
+                    }
+                    break;
+#ifdef UNIXBACKUP
+                case ('B'): /* -B: back up existing files */
+                    if (negative)
+                        uO.B_flag = FALSE, negative = 0;
+                    else
+                        uO.B_flag = TRUE;
+                    break;
+#endif
+                case ('c'):
+                    if (negative) {
+                        uO.cflag = FALSE, negative = 0;
+#ifdef NATIVE
+                        uO.aflag = 0;
+#endif
+                    } else {
+                        uO.cflag = TRUE;
+#ifdef NATIVE
+                        uO.aflag = 2;   /* so you can read it on the screen */
+#endif
+#ifdef DLL
+                        if (G.redirect_text)
+                            G.redirect_data = 2;
+#endif
+                    }
+                    break;
+#ifndef CMS_MVS
+                case ('C'):    /* -C:  match filenames case-insensitively */
+                    if (negative)
+                        uO.C_flag = FALSE, negative = 0;
+                    else
+                        uO.C_flag = TRUE;
+                    break;
+#endif /* !CMS_MVS */
+#if (!defined(SFX) || defined(SFX_EXDIR))
+                case ('d'):
+                    if (negative) {   /* negative not allowed with -d exdir */
+                        Info(slide, 0x401, ((char *)slide,
+                          LoadFarString(MustGiveExdir)));
+                        return(PK_PARAM);  /* don't extract here by accident */
+                    }
+                    if (uO.exdir != (char *)NULL) {
+                        Info(slide, 0x401, ((char *)slide,
+                          LoadFarString(OnlyOneExdir)));
+                        return(PK_PARAM);    /* GRR:  stupid restriction? */
+                    } else {
+                        /* first check for "-dexdir", then for "-d exdir" */
+                        uO.exdir = s;
+                        if (*uO.exdir == '\0') {
+                            if (argc > 1) {
+                                --argc;
+                                uO.exdir = *++argv;
+                                if (*uO.exdir == '-') {
+                                    Info(slide, 0x401, ((char *)slide,
+                                      LoadFarString(MustGiveExdir)));
+                                    return(PK_PARAM);
+                                }
+                                /* else uO.exdir points at extraction dir */
+                            } else {
+                                Info(slide, 0x401, ((char *)slide,
+                                  LoadFarString(MustGiveExdir)));
+                                return(PK_PARAM);
+                            }
+                        }
+                        /* uO.exdir now points at extraction dir (-dexdir or
+                         *  -d exdir); point s at end of exdir to avoid mis-
+                         *  interpretation of exdir characters as more options
+                         */
+                        if (*s != 0)
+                            while (*++s != 0)
+                                ;
+                    }
+                    break;
+#endif /* !SFX || SFX_EXDIR */
+#if (!defined(NO_TIMESTAMPS))
+                case ('D'):    /* -D: Skip restoring dir (or any) timestamp. */
+                    if (negative) {
+                        uO.D_flag = MAX(uO.D_flag-negative,0);
+                        negative = 0;
+                    } else
+                        uO.D_flag++;
+                    break;
+#endif /* (!NO_TIMESTAMPS) */
+                case ('e'):    /* just ignore -e, -x options (extract) */
+                    break;
+#ifdef MACOS
+                case ('E'): /* -E [MacOS] display Mac e.f. when restoring */
+                    if( negative ) {
+                        uO.E_flag = FALSE, negative = 0;
+                    } else {
+                        uO.E_flag = TRUE;
+                    }
+                    break;
+#endif /* MACOS */
+                case ('f'):    /* "freshen" (extract only newer files) */
+                    if (negative)
+                        uO.fflag = uO.uflag = FALSE, negative = 0;
+                    else
+                        uO.fflag = uO.uflag = TRUE;
+                    break;
+#if (defined(RISCOS) || defined(ACORN_FTYPE_NFS))
+                case ('F'):    /* Acorn filetype & NFS extension handling */
+                    if (negative)
+                        uO.acorn_nfs_ext = FALSE, negative = 0;
+                    else
+                        uO.acorn_nfs_ext = TRUE;
+                    break;
+#endif /* RISCOS || ACORN_FTYPE_NFS */
+                case ('h'):    /* just print help message and quit */
+                    if (showhelp == 0) {
+#ifndef SFX
+                        if (*s == 'h')
+                            showhelp = 2;
+                        else
+#endif /* !SFX */
+                        {
+                            showhelp = 1;
+                        }
+                    }
+                    break;
+#ifdef MACOS
+                case ('i'): /* -i [MacOS] ignore filenames stored in Mac ef */
+                    if( negative ) {
+                        uO.i_flag = FALSE, negative = 0;
+                    } else {
+                        uO.i_flag = TRUE;
+                    }
+                    break;
+#endif  /* MACOS */
+                case ('j'):    /* junk pathnames/directory structure */
+                    if (negative)
+                        uO.jflag = FALSE, negative = 0;
+                    else
+                        uO.jflag = TRUE;
+                    break;
+#if (defined(ATH_BEO) || defined(MACOS))
+                case ('J'):    /* Junk AtheOS, BeOS or MacOS file attributes */
+                    if( negative ) {
+                        uO.J_flag = FALSE, negative = 0;
+                    } else {
+                        uO.J_flag = TRUE;
+                    }
+                    break;
+#endif /* ATH_BEO || MACOS */
+#ifdef ATH_BEO_UNX
+                case ('K'):
+                    if (negative) {
+                        uO.K_flag = FALSE, negative = 0;
+                    } else {
+                        uO.K_flag = TRUE;
+                    }
+                    break;
+#endif /* ATH_BEO_UNX */
+#ifndef SFX
+                case ('l'):
+                    if (negative) {
+                        uO.vflag = MAX(uO.vflag-negative,0);
+                        negative = 0;
+                    } else
+                        ++uO.vflag;
+                    break;
+#endif /* !SFX */
+#ifndef CMS_MVS
+                case ('L'):    /* convert (some) filenames to lowercase */
+                    if (negative) {
+                        uO.L_flag = MAX(uO.L_flag-negative,0);
+                        negative = 0;
+                    } else
+                        ++uO.L_flag;
+                    break;
+#endif /* !CMS_MVS */
+#ifdef MORE
+#ifdef CMS_MVS
+                case ('m'):
+#endif
+                case ('M'):    /* send all screen output through "more" fn. */
+/* GRR:  eventually check for numerical argument => height */
+                    if (negative)
+                        G.M_flag = FALSE, negative = 0;
+                    else
+                        G.M_flag = TRUE;
+                    break;
+#endif /* MORE */
+                case ('n'):    /* don't overwrite any files */
+                    if (negative)
+                        uO.overwrite_none = FALSE, negative = 0;
+                    else
+                        uO.overwrite_none = TRUE;
+                    break;
+#ifdef AMIGA
+                case ('N'):    /* restore comments as filenotes */
+                    if (negative)
+                        uO.N_flag = FALSE, negative = 0;
+                    else
+                        uO.N_flag = TRUE;
+                    break;
+#endif /* AMIGA */
+                case ('o'):    /* OK to overwrite files without prompting */
+                    if (negative) {
+                        uO.overwrite_all = MAX(uO.overwrite_all-negative,0);
+                        negative = 0;
+                    } else
+                        ++uO.overwrite_all;
+                    break;
+                case ('p'):    /* pipes:  extract to stdout, no messages */
+                    if (negative) {
+                        uO.cflag = FALSE;
+                        uO.qflag = MAX(uO.qflag-999,0);
+                        negative = 0;
+                    } else {
+                        uO.cflag = TRUE;
+                        uO.qflag += 999;
+                    }
+                    break;
+#if CRYPT
+                /* GRR:  yes, this is highly insecure, but dozens of people
+                 * have pestered us for this, so here we go... */
+                case ('P'):
+                    if (negative) {   /* negative not allowed with -P passwd */
+                        Info(slide, 0x401, ((char *)slide,
+                          LoadFarString(MustGivePasswd)));
+                        return(PK_PARAM);  /* don't extract here by accident */
+                    }
+                    if (uO.pwdarg != (char *)NULL) {
+/*
+                        GRR:  eventually support multiple passwords?
+                        Info(slide, 0x401, ((char *)slide,
+                          LoadFarString(OnlyOnePasswd)));
+                        return(PK_PARAM);
+ */
+                    } else {
+                        /* first check for "-Ppasswd", then for "-P passwd" */
+                        uO.pwdarg = s;
+                        if (*uO.pwdarg == '\0') {
+                            if (argc > 1) {
+                                --argc;
+                                uO.pwdarg = *++argv;
+                                if (*uO.pwdarg == '-') {
+                                    Info(slide, 0x401, ((char *)slide,
+                                      LoadFarString(MustGivePasswd)));
+                                    return(PK_PARAM);
+                                }
+                                /* else pwdarg points at decryption password */
+                            } else {
+                                Info(slide, 0x401, ((char *)slide,
+                                  LoadFarString(MustGivePasswd)));
+                                return(PK_PARAM);
+                            }
+                        }
+                        /* pwdarg now points at decryption password (-Ppasswd or
+                         *  -P passwd); point s at end of passwd to avoid mis-
+                         *  interpretation of passwd characters as more options
+                         */
+                        if (*s != 0)
+                            while (*++s != 0)
+                                ;
+                    }
+                    break;
+#endif /* CRYPT */
+                case ('q'):    /* quiet:  fewer comments/messages */
+                    if (negative) {
+                        uO.qflag = MAX(uO.qflag-negative,0);
+                        negative = 0;
+                    } else
+                        ++uO.qflag;
+                    break;
+#ifdef QDOS
+                case ('Q'):   /* QDOS flags */
+                    qlflag ^= strtol(s, &s, 10);
+                    break;    /* we XOR this as we can config qlflags */
+#endif
+#ifdef TANDEM
+                case ('r'):    /* remove file extensions */
+                    if (negative)
+                        uO.rflag = FALSE, negative = 0;
+                    else
+                        uO.rflag = TRUE;
+                    break;
+#endif /* TANDEM */
+#ifdef DOS_FLX_NLM_OS2_W32
+                case ('s'):    /* spaces in filenames:  allow by default */
+                    if (negative)
+                        uO.sflag = FALSE, negative = 0;
+                    else
+                        uO.sflag = TRUE;
+                    break;
+#endif /* DOS_FLX_NLM_OS2_W32 */
+#ifdef VMS
+                /* VMS:  extract "text" files in Stream_LF format (-a[a]) */
+                case ('S'):
+                    if (negative)
+                        uO.S_flag = FALSE, negative = 0;
+                    else
+                        uO.S_flag = TRUE;
+                    break;
+#endif /* VMS */
+                case ('t'):
+                    if (negative)
+                        uO.tflag = FALSE, negative = 0;
+                    else
+                        uO.tflag = TRUE;
+                    break;
+#ifdef TIMESTAMP
+                case ('T'):
+                    if (negative)
+                        uO.T_flag = FALSE, negative = 0;
+                    else
+                        uO.T_flag = TRUE;
+                    break;
+#endif
+                case ('u'):    /* update (extract only new and newer files) */
+                    if (negative)
+                        uO.uflag = FALSE, negative = 0;
+                    else
+                        uO.uflag = TRUE;
+                    break;
+#ifdef UNICODE_SUPPORT
+                case ('U'):    /* escape UTF-8, or disable UTF-8 support */
+                    if (negative) {
+                        uO.U_flag = MAX(uO.U_flag-negative,0);
+                        negative = 0;
+                    } else
+                        uO.U_flag++;
+                    break;
+#else /* !UNICODE_SUPPORT */
+#ifndef CMS_MVS
+                case ('U'):    /* obsolete; to be removed in version 6.0 */
+                    if (negative)
+                        uO.L_flag = TRUE, negative = 0;
+                    else
+                        uO.L_flag = FALSE;
+                    break;
+#endif /* !CMS_MVS */
+#endif /* ?UNICODE_SUPPORT */
+#ifndef SFX
+                case ('v'):    /* verbose */
+                    if (negative) {
+                        uO.vflag = MAX(uO.vflag-negative,0);
+                        negative = 0;
+                    } else if (uO.vflag)
+                        ++uO.vflag;
+                    else
+                        uO.vflag = 2;
+                    break;
+#endif /* !SFX */
+#ifndef CMS_MVS
+                case ('V'):    /* Version (retain VMS/DEC-20 file versions) */
+                    if (negative)
+                        uO.V_flag = FALSE, negative = 0;
+                    else
+                        uO.V_flag = TRUE;
+                    break;
+#endif /* !CMS_MVS */
+#ifdef WILD_STOP_AT_DIR
+                case ('W'):    /* Wildcard interpretation (stop at '/'?) */
+                    if (negative)
+                        uO.W_flag = FALSE, negative = 0;
+                    else
+                        uO.W_flag = TRUE;
+                    break;
+#endif /* WILD_STOP_AT_DIR */
+                case ('x'):    /* extract:  default */
+#ifdef SFX
+                    /* when 'x' is the only option in this argument, and the
+                     * next arg is not an option, assume this initiates an
+                     * exclusion list (-x xlist):  terminate option-scanning
+                     * and leave uz_opts with argv still pointing to "-x";
+                     * the xlist is processed later
+                     */
+                    if (s - argv[0] == 2 && *s == '\0' &&
+                        argc > 1 && argv[1][0] != '-') {
+                        /* break out of nested loops without "++argv;--argc" */
+                        goto opts_done;
+                    }
+#endif /* SFX */
+                    break;
+#if (defined(RESTORE_UIDGID) || defined(RESTORE_ACL))
+                case ('X'):   /* restore owner/protection info (need privs?) */
+                    if (negative) {
+                        uO.X_flag = MAX(uO.X_flag-negative,0);
+                        negative = 0;
+                    } else
+                        ++uO.X_flag;
+                    break;
+#endif /* RESTORE_UIDGID || RESTORE_ACL */
+#ifdef VMS
+                case ('Y'):    /* Treat ".nnn" as ";nnn" version. */
+                    if (negative)
+                        uO.Y_flag = FALSE, negative = 0;
+                    else
+                        uO.Y_flag = TRUE;
+                    break;
+#endif /* VMS */
+                case ('z'):    /* display only the archive comment */
+                    if (negative) {
+                        uO.zflag = MAX(uO.zflag-negative,0);
+                        negative = 0;
+                    } else
+                        ++uO.zflag;
+                    break;
+#ifndef SFX
+                case ('Z'):    /* should have been first option (ZipInfo) */
+                    Info(slide, 0x401, ((char *)slide, LoadFarString(Zfirst)));
+                    error = TRUE;
+                    break;
+#endif /* !SFX */
+#ifdef VMS
+                case ('2'):    /* Force ODS2-compliant names. */
+                    if (negative)
+                        uO.ods2_flag = FALSE, negative = 0;
+                    else
+                        uO.ods2_flag = TRUE;
+                    break;
+#endif /* VMS */
+#ifdef DOS_H68_OS2_W32
+                case ('$'):
+                    if (negative) {
+                        uO.volflag = MAX(uO.volflag-negative,0);
+                        negative = 0;
+                    } else
+                        ++uO.volflag;
+                    break;
+#endif /* DOS_H68_OS2_W32 */
+#if (!defined(RISCOS) && !defined(CMS_MVS) && !defined(TANDEM))
+                case (':'):    /* allow "parent dir" path components */
+                    if (negative) {
+                        uO.ddotflag = MAX(uO.ddotflag-negative,0);
+                        negative = 0;
+                    } else
+                        ++uO.ddotflag;
+                    break;
+#endif /* !RISCOS && !CMS_MVS && !TANDEM */
+#ifdef UNIX
+                case ('^'):    /* allow control chars in filenames */
+                    if (negative) {
+                        uO.cflxflag = MAX(uO.cflxflag-negative,0);
+                        negative = 0;
+                    } else
+                        ++uO.cflxflag;
+                    break;
+#endif /* UNIX */
+                default:
+                    error = TRUE;
+                    break;
+
+            } /* end switch */
+        } /* end while (not end of argument string) */
+    } /* end while (not done with switches) */
+
+/*---------------------------------------------------------------------------
+    Check for nonsensical combinations of options.
+  ---------------------------------------------------------------------------*/
+
+#ifdef SFX
+opts_done:  /* yes, very ugly...but only used by UnZipSFX with -x xlist */
+#endif
+
+    if (showhelp > 0) {         /* just print help message and quit */
+        *pargc = -1;
+#ifndef SFX
+        if (showhelp == 2) {
+            help_extended(__G);
+            return PK_OK;
+        } else
+#endif /* !SFX */
+        {
+            return USAGE(PK_OK);
+        }
+    }
+
+    if ((uO.cflag && (uO.tflag || uO.uflag)) ||
+        (uO.tflag && uO.uflag) || (uO.fflag && uO.overwrite_none))
+    {
+        Info(slide, 0x401, ((char *)slide, LoadFarString(InvalidOptionsMsg)));
+        error = TRUE;
+    }
+    if (uO.aflag > 2)
+        uO.aflag = 2;
+#ifdef VMS
+    if (uO.bflag > 2)
+        uO.bflag = 2;
+    /* Clear -s flag when converting text files. */
+    if (uO.aflag <= 0)
+        uO.S_flag = 0;
+#endif /* VMS */
+    if (uO.overwrite_all && uO.overwrite_none) {
+        Info(slide, 0x401, ((char *)slide, LoadFarString(IgnoreOOptionMsg)));
+        uO.overwrite_all = FALSE;
+    }
+#ifdef MORE
+    if (G.M_flag && !isatty(1))  /* stdout redirected: "more" func. useless */
+        G.M_flag = 0;
+#endif
+
+#ifdef SFX
+    if (error)
+#else
+    if ((argc-- == 0) || error)
+#endif
+    {
+        *pargc = argc;
+        *pargv = argv;
+#ifndef SFX
+        if (uO.vflag >= 2 && argc == -1) {              /* "unzip -v" */
+            show_version_info(__G);
+            return PK_OK;
+        }
+        if (!G.noargs && !error)
+            error = TRUE;       /* had options (not -h or -v) but no zipfile */
+#endif /* !SFX */
+        return USAGE(error);
+    }
+
+#ifdef SFX
+    /* print our banner unless we're being fairly quiet */
+    if (uO.qflag < 2)
+        Info(slide, error? 1 : 0, ((char *)slide, LoadFarString(UnzipSFXBanner),
+          UZ_MAJORVER, UZ_MINORVER, UZ_PATCHLEVEL, UZ_BETALEVEL,
+          LoadFarStringSmall(VersionDate)));
+#ifdef BETA
+    /* always print the beta warning:  no unauthorized distribution!! */
+    Info(slide, error? 1 : 0, ((char *)slide, LoadFarString(BetaVersion), "\n",
+      "SFX"));
+#endif
+#endif /* SFX */
+
+    if (uO.cflag || uO.tflag || uO.vflag || uO.zflag
+#ifdef TIMESTAMP
+                                                     || uO.T_flag
+#endif
+                                                                 )
+        G.extract_flag = FALSE;
+    else
+        G.extract_flag = TRUE;
+
+    *pargc = argc;
+    *pargv = argv;
+    return PK_OK;
+
+} /* end function uz_opts() */
+
+
+
+
+/********************/
+/* Function usage() */
+/********************/
+
+#ifdef SFX
+#  ifdef VMS
+#    define LOCAL "X.\n\
+(Must quote upper-case options, like \"-V\", unless SET PROC/PARSE=EXTEND.)"
+#  endif
+#  ifdef UNIX
+#    define LOCAL "X"
+#  endif
+#  ifdef DOS_OS2_W32
+#    define LOCAL "s$"
+#  endif
+#  if (defined(FLEXOS) || defined(NLM))
+#    define LOCAL "s"
+#  endif
+#  ifdef AMIGA
+#    define LOCAL "N"
+#  endif
+   /* Default for all other systems: */
+#  ifndef LOCAL
+#    define LOCAL ""
+#  endif
+
+#  ifndef NO_TIMESTAMP
+#    ifdef MORE
+#      define SFXOPT1 "DM"
+#    else
+#      define SFXOPT1 "D"
+#    endif
+#  else
+#    ifdef MORE
+#      define SFXOPT1 "M"
+#    else
+#      define SFXOPT1 ""
+#    endif
+#  endif
+
+int usage(__G__ error)   /* return PK-type error code */
+    __GDEF
+    int error;
+{
+    Info(slide, error? 1 : 0, ((char *)slide, LoadFarString(UnzipSFXBanner),
+      UZ_MAJORVER, UZ_MINORVER, UZ_PATCHLEVEL, UZ_BETALEVEL,
+      LoadFarStringSmall(VersionDate)));
+    Info(slide, error? 1 : 0, ((char *)slide, LoadFarString(UnzipSFXOpts),
+      SFXOPT1, LOCAL));
+#ifdef BETA
+    Info(slide, error? 1 : 0, ((char *)slide, LoadFarString(BetaVersion), "\n",
+      "SFX"));
+#endif
+
+    if (error)
+        return PK_PARAM;
+    else
+        return PK_COOL;     /* just wanted usage screen: no error */
+
+} /* end function usage() */
+
+
+
+
+
+#else /* !SFX */
+#  ifdef VMS
+#    define QUOT '\"'
+#    define QUOTS "\""
+#  else
+#    define QUOT ' '
+#    define QUOTS ""
+#  endif
+
+int usage(__G__ error)   /* return PK-type error code */
+    __GDEF
+    int error;
+{
+    int flag = (error? 1 : 0);
+
+
+/*---------------------------------------------------------------------------
+    Print either ZipInfo usage or UnZip usage, depending on incantation.
+    (Strings must be no longer than 512 bytes for Turbo C, apparently.)
+  ---------------------------------------------------------------------------*/
+
+    if (uO.zipinfo_mode) {
+
+#ifndef NO_ZIPINFO
+
+        Info(slide, flag, ((char *)slide, LoadFarString(ZipInfoUsageLine1),
+          ZI_MAJORVER, ZI_MINORVER, UZ_PATCHLEVEL, UZ_BETALEVEL,
+          LoadFarStringSmall(VersionDate),
+          LoadFarStringSmall2(ZipInfoExample), QUOTS,QUOTS));
+        Info(slide, flag, ((char *)slide, LoadFarString(ZipInfoUsageLine2)));
+        Info(slide, flag, ((char *)slide, LoadFarString(ZipInfoUsageLine3),
+          LoadFarStringSmall(ZipInfoUsageLine4)));
+#ifdef VMS
+        Info(slide, flag, ((char *)slide, "\n\
+You must quote non-lowercase options and filespecs, unless SET PROC/PARSE=EXT.\
+\n"));
+#endif
+
+#endif /* !NO_ZIPINFO */
+
+    } else {   /* UnZip mode */
+
+        Info(slide, flag, ((char *)slide, LoadFarString(UnzipUsageLine1),
+          UZ_MAJORVER, UZ_MINORVER, UZ_PATCHLEVEL, UZ_BETALEVEL,
+          LoadFarStringSmall(VersionDate)));
+#ifdef BETA
+        Info(slide, flag, ((char *)slide, LoadFarString(BetaVersion), "", ""));
+#endif
+
+        Info(slide, flag, ((char *)slide, LoadFarString(UnzipUsageLine2),
+          ZIPINFO_MODE_OPTION, LoadFarStringSmall(ZipInfoMode)));
+#ifdef VMS
+        if (!error)  /* maybe no command-line tail found; show extra help */
+            Info(slide, flag, ((char *)slide, LoadFarString(VMSusageLine2b)));
+#endif
+
+        Info(slide, flag, ((char *)slide, LoadFarString(UnzipUsageLine3),
+          LoadFarStringSmall(local1)));
+
+        Info(slide, flag, ((char *)slide, LoadFarString(UnzipUsageLine4),
+          LoadFarStringSmall(local2), LoadFarStringSmall2(local3)));
+
+        /* This is extra work for SMALL_MEM, but it will work since
+         * LoadFarStringSmall2 uses the same buffer.  Remember, this
+         * is a hack. */
+        Info(slide, flag, ((char *)slide, LoadFarString(UnzipUsageLine5),
+          LoadFarStringSmall(Example2), LoadFarStringSmall2(Example3),
+          LoadFarStringSmall2(Example3)));
+
+    } /* end if (uO.zipinfo_mode) */
+
+    if (error)
+        return PK_PARAM;
+    else
+        return PK_COOL;     /* just wanted usage screen: no error */
+
+} /* end function usage() */
+
+#endif /* ?SFX */
+
+
+
+
+#ifndef SFX
+
+/* Print extended help to stdout. */
+static void help_extended(__G)
+    __GDEF
+{
+    extent i;             /* counter for help array */
+
+    /* help array */
+    static ZCONST char *text[] = {
+  "",
+  "Extended Help for UnZip",
+  "",
+  "See the UnZip Manual for more detailed help",
+  "",
+  "",
+  "UnZip lists and extracts files in zip archives.  The default action is to",
+  "extract zipfile entries to the current directory, creating directories as",
+  "needed.  With appropriate options, UnZip lists the contents of archives",
+  "instead.",
+  "",
+  "Basic unzip command line:",
+  "  unzip [-Z] options archive[.zip] [file ...] [-x xfile ...] [-d exdir]",
+  "",
+  "Some examples:",
+  "  unzip -l foo.zip        - list files in short format in archive foo.zip",
+  "",
+  "  unzip -t foo            - test the files in archive foo",
+  "",
+  "  unzip -Z foo            - list files using more detailed zipinfo format",
+  "",
+  "  unzip foo               - unzip the contents of foo in current dir",
+  "",
+  "  unzip -a foo            - unzip foo and convert text files to local OS",
+  "",
+  "If unzip is run in zipinfo mode, a more detailed list of archive contents",
+  "is provided.  The -Z option sets zipinfo mode and changes the available",
+  "options.",
+  "",
+  "Basic zipinfo command line:",
+  "  zipinfo options archive[.zip] [file ...] [-x xfile ...]",
+  "  unzip -Z options archive[.zip] [file ...] [-x xfile ...]",
+  "",
+  "Below, Mac OS refers to Mac OS before Mac OS X.  Mac OS X is a Unix based",
+  "port and is referred to as Unix Apple.",
+  "",
+  "",
+  "unzip options:",
+  "  -Z   Switch to zipinfo mode.  Must be first option.",
+  "  -hh  Display extended help.",
+  "  -A   [OS/2, Unix DLL] Print extended help for DLL.",
+  "  -c   Extract files to stdout/screen.  As -p but include names.  Also,",
+  "         -a allowed and EBCDIC conversions done if needed.",
+  "  -f   Freshen by extracting only if older file on disk.",
+  "  -l   List files using short form.",
+  "  -p   Extract files to pipe (stdout).  Only file data is output and all",
+  "         files extracted in binary mode (as stored).",
+  "  -t   Test archive files.",
+  "  -T   Set timestamp on archive(s) to that of newest file.  Similar to",
+  "       zip -o but faster.",
+  "  -u   Update existing older files on disk as -f and extract new files.",
+  "  -v   Use verbose list format.  If given alone as unzip -v show version",
+  "         information.  Also can be added to other list commands for more",
+  "         verbose output.",
+  "  -z   Display only archive comment.",
+  "",
+  "unzip modifiers:",
+  "  -a   Convert text files to local OS format.  Convert line ends, EOF",
+  "         marker, and from or to EBCDIC character set as needed.",
+  "  -b   Treat all files as binary.  [Tandem] Force filecode 180 ('C').",
+  "         [VMS] Autoconvert binary files.  -bb forces convert of all files.",
+  "  -B   [UNIXBACKUP compile option enabled] Save a backup copy of each",
+  "         overwritten file in foo~ or foo~99999 format.",
+  "  -C   Use case-insensitive matching.",
+  "  -D   Skip restoration of timestamps for extracted directories.  On VMS this",
+  "         is on by default and -D essentially becames -DD.",
+  "  -DD  Skip restoration of timestamps for all entries.",
+  "  -E   [MacOS (not Unix Apple)]  Display contents of MacOS extra field during",
+  "         restore.",
+  "  -F   [Acorn] Suppress removal of NFS filetype extension.  [Non-Acorn if",
+  "         ACORN_FTYPE_NFS] Translate filetype and append to name.",
+  "  -i   [MacOS] Ignore filenames in MacOS extra field.  Instead, use name in",
+  "         standard header.",
+  "  -j   Junk paths and deposit all files in extraction directory.",
+  "  -J   [BeOS] Junk file attributes.  [MacOS] Ignore MacOS specific info.",
+  "  -K   [AtheOS, BeOS, Unix] Restore SUID/SGID/Tacky file attributes.",
+  "  -L   Convert to lowercase any names from uppercase only file system.",
+  "  -LL  Convert all files to lowercase.",
+  "  -M   Pipe all output through internal pager similar to Unix more(1).",
+  "  -n   Never overwrite existing files.  Skip extracting that file, no prompt.",
+  "  -N   [Amiga] Extract file comments as Amiga filenotes.",
+  "  -o   Overwrite existing files without prompting.  Useful with -f.  Use with",
+  "         care.",
+  "  -P p Use password p to decrypt files.  THIS IS INSECURE!  Some OS show",
+  "         command line to other users.",
+  "  -q   Perform operations quietly.  The more q (as in -qq) the quieter.",
+  "  -s   [OS/2, NT, MS-DOS] Convert spaces in filenames to underscores.",
+  "  -S   [VMS] Convert text files (-a, -aa) into Stream_LF format.",
+  "  -U   [UNICODE enabled] Show non-local characters as #Uxxxx or #Lxxxxxx ASCII",
+  "         text escapes where x is hex digit.  [Old] -U used to leave names",
+  "         uppercase if created on MS-DOS, VMS, etc.  See -L.",
+  "  -UU  [UNICODE enabled] Disable use of stored UTF-8 paths.  Note that UTF-8",
+  "         paths stored as native local paths are still processed as Unicode.",
+  "  -V   Retain VMS file version numbers.",
+  "  -W   [Only if WILD_STOP_AT_DIR] Modify pattern matching so ? and * do not",
+  "         match directory separator /, but ** does.  Allows matching at specific",
+  "         directory levels.",
+  "  -X   [VMS, Unix, OS/2, NT, Tandem] Restore UICs and ACL entries under VMS,",
+  "         or UIDs/GIDs under Unix, or ACLs under certain network-enabled",
+  "         versions of OS/2, or security ACLs under Windows NT.  Can require",
+  "         user privileges.",
+  "  -XX  [NT] Extract NT security ACLs after trying to enable additional",
+  "         system privileges.",
+  "  -Y   [VMS] Treat archived name endings of .nnn as VMS version numbers.",
+  "  -$   [MS-DOS, OS/2, NT] Restore volume label if extraction medium is",
+  "         removable.  -$$ allows fixed media (hard drives) to be labeled.",
+  "  -/ e [Acorn] Use e as extension list.",
+  "  -:   [All but Acorn, VM/CMS, MVS, Tandem] Allow extract archive members into",
+  "         locations outside of current extraction root folder.  This allows",
+  "         paths such as ../foo to be extracted above the current extraction",
+  "         directory, which can be a security problem.",
+  "  -^   [Unix] Allow control characters in names of extracted entries.  Usually",
+  "         this is not a good thing and should be avoided.",
+  "  -2   [VMS] Force unconditional conversion of names to ODS-compatible names.",
+  "         Default is to exploit destination file system, preserving cases and",
+  "         extended name characters on ODS5 and applying ODS2 filtering on ODS2.",
+  "",
+  "",
+  "Wildcards:",
+  "  Internally unzip supports the following wildcards:",
+  "    ?       (or %% or #, depending on OS) matches any single character",
+  "    *       matches any number of characters, including zero",
+  "    [list]  matches char in list (regex), can do range [ac-f], all but [!bf]",
+  "  If port supports [], must escape [ as [[]",
+  "  For shells that expand wildcards, escape (\\* or \"*\") so unzip can recurse.",
+  "",
+  "Include and Exclude:",
+  "  -i pattern pattern ...   include files that match a pattern",
+  "  -x pattern pattern ...   exclude files that match a pattern",
+  "  Patterns are paths with optional wildcards and match paths as stored in",
+  "  archive.  Exclude and include lists end at next option or end of line.",
+  "    unzip archive -x pattern pattern ...",
+  "",
+  "Multi-part (split) archives (archives created as a set of split files):",
+  "  Currently split archives are not readable by unzip.  A workaround is",
+  "  to use zip to convert the split archive to a single-file archive and",
+  "  use unzip on that.  See the manual page for Zip 3.0 or later.",
+  "",
+  "Streaming (piping into unzip):",
+  "  Currently unzip does not support streaming.  The funzip utility can be",
+  "  used to process the first entry in a stream.",
+  "    cat archive | funzip",
+  "",
+  "Testing archives:",
+  "  -t        test contents of archive",
+  "  This can be modified using -q for quieter operation, and -qq for even",
+  "  quieter operation.",
+  "",
+  "Unicode:",
+  "  If compiled with Unicode support, unzip automatically handles archives",
+  "  with Unicode entries.  Currently Unicode on Win32 systems is limited.",
+  "  Characters not in the current character set are shown as ASCII escapes",
+  "  in the form #Uxxxx where the Unicode character number fits in 16 bits,",
+  "  or #Lxxxxxx where it doesn't, where x is the ASCII character for a hex",
+  "  digit.",
+  "",
+  "",
+  "zipinfo options (these are used in zipinfo mode (unzip -Z ...)):",
+  "  -1  List names only, one per line.  No headers/trailers.  Good for scripts.",
+  "  -2  List names only as -1, but include headers, trailers, and comments.",
+  "  -s  List archive entries in short Unix ls -l format.  Default list format.",
+  "  -m  List in long Unix ls -l format.  As -s, but includes compression %.",
+  "  -l  List in long Unix ls -l format.  As -m, but compression in bytes.",
+  "  -v  List zipfile information in verbose, multi-page format.",
+  "  -h  List header line.  Includes archive name, actual size, total files.",
+  "  -M  Pipe all output through internal pager similar to Unix more(1) command.",
+  "  -t  List totals for files listed or for all files.  Includes uncompressed",
+  "        and compressed sizes, and compression factors.",
+  "  -T  Print file dates and times in a sortable decimal format (yymmdd.hhmmss)",
+  "        Default date and time format is a more human-readable version.",
+  "  -U  [UNICODE] If entry has a UTF-8 Unicode path, display any characters",
+  "        not in current character set as text #Uxxxx and #Lxxxxxx escapes",
+  "        representing the Unicode character number of the character in hex.",
+  "  -UU [UNICODE]  Disable use of any UTF-8 path information.",
+  "  -z  Include archive comment if any in listing.",
+  "",
+  "",
+  "funzip stream extractor:",
+  "  funzip extracts the first member in an archive to stdout.  Typically",
+  "  used to unzip the first member of a stream or pipe.  If a file argument",
+  "  is given, read from that file instead of stdin.",
+  "",
+  "funzip command line:",
+  "  funzip [-password] [input[.zip|.gz]]",
+  "",
+  "",
+  "unzipsfx self extractor:",
+  "  Self-extracting archives made with unzipsfx are no more (or less)",
+  "  portable across different operating systems than unzip executables.",
+  "  In general, a self-extracting archive made on a particular Unix system,",
+  "  for example, will only self-extract under the same flavor of Unix.",
+  "  Regular unzip may still be used to extract embedded archive however.",
+  "",
+  "unzipsfx command line:",
+  "  <unzipsfx+archive_filename>  [-options] [file(s) ... [-x xfile(s) ...]]",
+  "",
+  "unzipsfx options:",
+  "  -c, -p - Output to pipe.  (See above for unzip.)",
+  "  -f, -u - Freshen and Update, as for unzip.",
+  "  -t     - Test embedded archive.  (Can be used to list contents.)",
+  "  -z     - Print archive comment.  (See unzip above.)",
+  "",
+  "unzipsfx modifiers:",
+  "  Most unzip modifiers are supported.  These include",
+  "  -a     - Convert text files.",
+  "  -n     - Never overwrite.",
+  "  -o     - Overwrite without prompting.",
+  "  -q     - Quiet operation.",
+  "  -C     - Match names case-insensitively.",
+  "  -j     - Junk paths.",
+  "  -V     - Keep version numbers.",
+  "  -s     - Convert spaces to underscores.",
+  "  -$     - Restore volume label.",
+  "",
+  "If unzipsfx compiled with SFX_EXDIR defined, -d option also available:",
+  "  -d exd - Extract to directory exd.",
+  "By default, all files extracted to current directory.  This option",
+  "forces extraction to specified directory.",
+  "",
+  "See unzipsfx manual page for more information.",
+  ""
+    };
+
+    for (i = 0; i < sizeof(text)/sizeof(char *); i++)
+    {
+        Info(slide, 0, ((char *)slide, "%s\n", text[i]));
+    }
+} /* end function help_extended() */
+
+
+
+
+#ifndef _WIN32_WCE /* Win CE does not support environment variables */
+#if (!defined(MODERN) || defined(NO_STDLIB_H))
+/* Declare getenv() to be sure (might be missing in some environments) */
+extern char *getenv();
+#endif
+#endif
+
+/********************************/
+/* Function show_version_info() */
+/********************************/
+
+static void show_version_info(__G)
+    __GDEF
+{
+    if (uO.qflag > 3)                           /* "unzip -vqqqq" */
+        Info(slide, 0, ((char *)slide, "%d\n",
+          (UZ_MAJORVER*100 + UZ_MINORVER*10 + UZ_PATCHLEVEL)));
+    else {
+#ifndef _WIN32_WCE /* Win CE does not support environment variables */
+        char *envptr;
+#endif
+        int numopts = 0;
+
+        Info(slide, 0, ((char *)slide, LoadFarString(UnzipUsageLine1v),
+          UZ_MAJORVER, UZ_MINORVER, UZ_PATCHLEVEL, UZ_BETALEVEL,
+          LoadFarStringSmall(VersionDate)));
+        Info(slide, 0, ((char *)slide,
+          LoadFarString(UnzipUsageLine2v)));
+        version(__G);
+        Info(slide, 0, ((char *)slide, LoadFarString(CompileOptions)));
+#ifdef ACORN_FTYPE_NFS
+        Info(slide, 0, ((char *)slide, LoadFarString(CompileOptFormat),
+          LoadFarStringSmall(AcornFtypeNFS)));
+        ++numopts;
+#endif
+#ifdef ASM_CRC
+        Info(slide, 0, ((char *)slide, LoadFarString(CompileOptFormat),
+          LoadFarStringSmall(AsmCRC)));
+        ++numopts;
+#endif
+#ifdef ASM_INFLATECODES
+        Info(slide, 0, ((char *)slide, LoadFarString(CompileOptFormat),
+          LoadFarStringSmall(AsmInflateCodes)));
+        ++numopts;
+#endif
+#ifdef CHECK_VERSIONS
+        Info(slide, 0, ((char *)slide, LoadFarString(CompileOptFormat),
+          LoadFarStringSmall(Check_Versions)));
+        ++numopts;
+#endif
+#ifdef COPYRIGHT_CLEAN
+        Info(slide, 0, ((char *)slide, LoadFarString(CompileOptFormat),
+          LoadFarStringSmall(Copyright_Clean)));
+        ++numopts;
+#endif
+#ifdef DEBUG
+        Info(slide, 0, ((char *)slide, LoadFarString(CompileOptFormat),
+          LoadFarStringSmall(UDebug)));
+        ++numopts;
+#endif
+#ifdef DEBUG_TIME
+        Info(slide, 0, ((char *)slide, LoadFarString(CompileOptFormat),
+          LoadFarStringSmall(DebugTime)));
+        ++numopts;
+#endif
+#ifdef DLL
+        Info(slide, 0, ((char *)slide, LoadFarString(CompileOptFormat),
+          LoadFarStringSmall(Dll)));
+        ++numopts;
+#endif
+#ifdef DOSWILD
+        Info(slide, 0, ((char *)slide, LoadFarString(CompileOptFormat),
+          LoadFarStringSmall(DosWild)));
+        ++numopts;
+#endif
+#ifdef LZW_CLEAN
+        Info(slide, 0, ((char *)slide, LoadFarString(CompileOptFormat),
+          LoadFarStringSmall(LZW_Clean)));
+        ++numopts;
+#endif
+#ifndef MORE
+        Info(slide, 0, ((char *)slide, LoadFarString(CompileOptFormat),
+          LoadFarStringSmall(No_More)));
+        ++numopts;
+#endif
+#ifdef NO_ZIPINFO
+        Info(slide, 0, ((char *)slide, LoadFarString(CompileOptFormat),
+          LoadFarStringSmall(No_ZipInfo)));
+        ++numopts;
+#endif
+#ifdef NTSD_EAS
+        Info(slide, 0, ((char *)slide, LoadFarString(CompileOptFormat),
+          LoadFarStringSmall(NTSDExtAttrib)));
+        ++numopts;
+#endif
+#if defined(WIN32) && defined(NO_W32TIMES_IZFIX)
+        Info(slide, 0, ((char *)slide, LoadFarString(CompileOptFormat),
+          LoadFarStringSmall(W32NoIZTimeFix)));
+        ++numopts;
+#endif
+#ifdef OLD_THEOS_EXTRA
+        Info(slide, 0, ((char *)slide, LoadFarString(CompileOptFormat),
+          LoadFarStringSmall(OldTheosExtra)));
+        ++numopts;
+#endif
+#ifdef OS2_EAS
+        Info(slide, 0, ((char *)slide, LoadFarString(CompileOptFormat),
+          LoadFarStringSmall(OS2ExtAttrib)));
+        ++numopts;
+#endif
+#ifdef QLZIP
+        Info(slide, 0, ((char *)slide, LoadFarString(CompileOptFormat),
+          LoadFarStringSmall(SMSExFldOnUnix)));
+        ++numopts;
+#endif
+#ifdef REENTRANT
+        Info(slide, 0, ((char *)slide, LoadFarString(CompileOptFormat),
+          LoadFarStringSmall(Reentrant)));
+        ++numopts;
+#endif
+#ifdef REGARGS
+        Info(slide, 0, ((char *)slide, LoadFarString(CompileOptFormat),
+          LoadFarStringSmall(RegArgs)));
+        ++numopts;
+#endif
+#ifdef RETURN_CODES
+        Info(slide, 0, ((char *)slide, LoadFarString(CompileOptFormat),
+          LoadFarStringSmall(Return_Codes)));
+        ++numopts;
+#endif
+#ifdef SET_DIR_ATTRIB
+        Info(slide, 0, ((char *)slide, LoadFarString(CompileOptFormat),
+          LoadFarStringSmall(SetDirAttrib)));
+        ++numopts;
+#endif
+#ifdef SYMLINKS
+        Info(slide, 0, ((char *)slide, LoadFarString(CompileOptFormat),
+          LoadFarStringSmall(SymLinkSupport)));
+        ++numopts;
+#endif
+#ifdef TIMESTAMP
+        Info(slide, 0, ((char *)slide, LoadFarString(CompileOptFormat),
+          LoadFarStringSmall(TimeStamp)));
+        ++numopts;
+#endif
+#ifdef UNIXBACKUP
+        Info(slide, 0, ((char *)slide, LoadFarString(CompileOptFormat),
+          LoadFarStringSmall(UnixBackup)));
+        ++numopts;
+#endif
+#ifdef USE_EF_UT_TIME
+        Info(slide, 0, ((char *)slide, LoadFarString(CompileOptFormat),
+          LoadFarStringSmall(Use_EF_UT_time)));
+        ++numopts;
+#endif
+#ifndef COPYRIGHT_CLEAN
+        Info(slide, 0, ((char *)slide, LoadFarString(CompileOptFormat),
+          LoadFarStringSmall(Use_Smith_Code)));
+        ++numopts;
+#endif
+#ifndef LZW_CLEAN
+        Info(slide, 0, ((char *)slide, LoadFarString(CompileOptFormat),
+          LoadFarStringSmall(Use_Unshrink)));
+        ++numopts;
+#endif
+#ifdef USE_DEFLATE64
+        Info(slide, 0, ((char *)slide, LoadFarString(CompileOptFormat),
+          LoadFarStringSmall(Use_Deflate64)));
+        ++numopts;
+#endif
+#ifdef UNICODE_SUPPORT
+# ifdef UTF8_MAYBE_NATIVE
+        sprintf((char *)(slide+256), LoadFarStringSmall(Use_Unicode),
+          LoadFarStringSmall2(G.native_is_utf8 ? SysChUTF8 : SysChOther));
+        Info(slide, 0, ((char *)slide, LoadFarString(CompileOptFormat),
+          (char *)(slide+256)));
+# else
+        Info(slide, 0, ((char *)slide, LoadFarString(CompileOptFormat),
+          LoadFarStringSmall(Use_Unicode)));
+# endif
+        ++numopts;
+#endif
+#ifdef _MBCS
+        sprintf((char *)(slide+256), LoadFarStringSmall(Have_MBCS_Support),
+          (unsigned int)MB_CUR_MAX);
+        Info(slide, 0, ((char *)slide, LoadFarString(CompileOptFormat),
+          (char *)(slide+256)));
+        ++numopts;
+#endif
+#ifdef MULT_VOLUME
+        Info(slide, 0, ((char *)slide, LoadFarString(CompileOptFormat),
+          LoadFarStringSmall(Use_MultiVol)));
+        ++numopts;
+#endif
+#ifdef LARGE_FILE_SUPPORT
+        Info(slide, 0, ((char *)slide, LoadFarString(CompileOptFormat),
+          LoadFarStringSmall(Use_LFS)));
+        ++numopts;
+#endif
+#ifdef ZIP64_SUPPORT
+        Info(slide, 0, ((char *)slide, LoadFarString(CompileOptFormat),
+          LoadFarStringSmall(Use_Zip64)));
+        ++numopts;
+#endif
+#if (defined(__DJGPP__) && (__DJGPP__ >= 2))
+#  ifdef USE_DJGPP_ENV
+        Info(slide, 0, ((char *)slide, LoadFarString(CompileOptFormat),
+          LoadFarStringSmall(Use_DJGPP_Env)));
+        ++numopts;
+#  endif
+#  ifdef USE_DJGPP_GLOB
+        Info(slide, 0, ((char *)slide, LoadFarString(CompileOptFormat),
+          LoadFarStringSmall(Use_DJGPP_Glob)));
+        ++numopts;
+#  endif
+#endif /* __DJGPP__ && (__DJGPP__ >= 2) */
+#ifdef USE_VFAT
+        Info(slide, 0, ((char *)slide, LoadFarString(CompileOptFormat),
+          LoadFarStringSmall(Use_VFAT_support)));
+        ++numopts;
+#endif
+#ifdef USE_ZLIB
+        sprintf((char *)(slide+256), LoadFarStringSmall(UseZlib),
+          ZLIB_VERSION, zlibVersion());
+        Info(slide, 0, ((char *)slide, LoadFarString(CompileOptFormat),
+          (char *)(slide+256)));
+        ++numopts;
+#endif
+#ifdef USE_BZIP2
+        sprintf((char *)(slide+256), LoadFarStringSmall(UseBZip2),
+          BZ2_bzlibVersion());
+        Info(slide, 0, ((char *)slide, LoadFarString(CompileOptFormat),
+          (char *)(slide+256)));
+        ++numopts;
+#endif
+#ifdef VMS_TEXT_CONV
+        Info(slide, 0, ((char *)slide, LoadFarString(CompileOptFormat),
+          LoadFarStringSmall(VmsTextConv)));
+        ++numopts;
+#endif
+#ifdef VMSCLI
+        Info(slide, 0, ((char *)slide, LoadFarString(CompileOptFormat),
+          LoadFarStringSmall(VmsCLI)));
+        ++numopts;
+#endif
+#ifdef VMSWILD
+        Info(slide, 0, ((char *)slide, LoadFarString(CompileOptFormat),
+          LoadFarStringSmall(VmsWild)));
+        ++numopts;
+#endif
+#ifdef WILD_STOP_AT_DIR
+        Info(slide, 0, ((char *)slide, LoadFarString(CompileOptFormat),
+          LoadFarStringSmall(WildStopAtDir)));
+        ++numopts;
+#endif
+#if CRYPT
+# ifdef PASSWD_FROM_STDIN
+        Info(slide, 0, ((char *)slide, LoadFarString(CompileOptFormat),
+          LoadFarStringSmall(PasswdStdin)));
+# endif
+        Info(slide, 0, ((char *)slide, LoadFarString(Decryption),
+          CR_MAJORVER, CR_MINORVER, CR_BETA_VER,
+          LoadFarStringSmall(CryptDate)));
+        ++numopts;
+#endif /* CRYPT */
+        if (numopts == 0)
+            Info(slide, 0, ((char *)slide,
+              LoadFarString(CompileOptFormat),
+              LoadFarStringSmall(None)));
+
+#ifndef _WIN32_WCE /* Win CE does not support environment variables */
+        Info(slide, 0, ((char *)slide, LoadFarString(EnvOptions)));
+        envptr = getenv(LoadFarStringSmall(EnvUnZip));
+        Info(slide, 0, ((char *)slide, LoadFarString(EnvOptFormat),
+          LoadFarStringSmall(EnvUnZip),
+          (envptr == (char *)NULL || *envptr == 0)?
+          LoadFarStringSmall2(None) : envptr));
+        envptr = getenv(LoadFarStringSmall(EnvUnZip2));
+        Info(slide, 0, ((char *)slide, LoadFarString(EnvOptFormat),
+          LoadFarStringSmall(EnvUnZip2),
+          (envptr == (char *)NULL || *envptr == 0)?
+          LoadFarStringSmall2(None) : envptr));
+        envptr = getenv(LoadFarStringSmall(EnvZipInfo));
+        Info(slide, 0, ((char *)slide, LoadFarString(EnvOptFormat),
+          LoadFarStringSmall(EnvZipInfo),
+          (envptr == (char *)NULL || *envptr == 0)?
+          LoadFarStringSmall2(None) : envptr));
+        envptr = getenv(LoadFarStringSmall(EnvZipInfo2));
+        Info(slide, 0, ((char *)slide, LoadFarString(EnvOptFormat),
+          LoadFarStringSmall(EnvZipInfo2),
+          (envptr == (char *)NULL || *envptr == 0)?
+          LoadFarStringSmall2(None) : envptr));
+#ifndef __RSXNT__
+#ifdef __EMX__
+        envptr = getenv(LoadFarStringSmall(EnvEMX));
+        Info(slide, 0, ((char *)slide, LoadFarString(EnvOptFormat),
+          LoadFarStringSmall(EnvEMX),
+          (envptr == (char *)NULL || *envptr == 0)?
+          LoadFarStringSmall2(None) : envptr));
+        envptr = getenv(LoadFarStringSmall(EnvEMXOPT));
+        Info(slide, 0, ((char *)slide, LoadFarString(EnvOptFormat),
+          LoadFarStringSmall(EnvEMXOPT),
+          (envptr == (char *)NULL || *envptr == 0)?
+          LoadFarStringSmall2(None) : envptr));
+#endif /* __EMX__ */
+#if (defined(__GO32__) && (!defined(__DJGPP__) || (__DJGPP__ < 2)))
+        envptr = getenv(LoadFarStringSmall(EnvGO32));
+        Info(slide, 0, ((char *)slide, LoadFarString(EnvOptFormat),
+          LoadFarStringSmall(EnvGO32),
+          (envptr == (char *)NULL || *envptr == 0)?
+          LoadFarStringSmall2(None) : envptr));
+        envptr = getenv(LoadFarStringSmall(EnvGO32TMP));
+        Info(slide, 0, ((char *)slide, LoadFarString(EnvOptFormat),
+          LoadFarStringSmall(EnvGO32TMP),
+          (envptr == (char *)NULL || *envptr == 0)?
+          LoadFarStringSmall2(None) : envptr));
+#endif /* __GO32__ && !(__DJGPP__ >= 2) */
+#endif /* !__RSXNT__ */
+#ifdef RISCOS
+        envptr = getenv(LoadFarStringSmall(EnvUnZipExts));
+        Info(slide, 0, ((char *)slide, LoadFarString(EnvOptFormat),
+          LoadFarStringSmall(EnvUnZipExts),
+          (envptr == (char *)NULL || *envptr == 0)?
+          LoadFarStringSmall2(None) : envptr));
+#endif /* RISCOS */
+#endif /* !_WIN32_WCE */
+    }
+} /* end function show_version() */
+
+#endif /* !SFX */
+#endif /* !WINDLL */
diff -Naur a/unzpriv.h b/unzpriv.h
--- a/unzpriv.h	2009-04-20 00:59:26.000000000 +0100
+++ b/unzpriv.h	2019-12-02 01:05:52.857702371 +0000
@@ -1211,6 +1211,7 @@
 # ifdef UNICODE_WCHAR
 #  if !(defined(_WIN32_WCE) || defined(POCKET_UNZIP))
 #   include <wchar.h>
+#   include <wctype.h>
 #  endif
 # endif
 # ifndef _MBCS  /* no need to include <locale.h> twice, see below */
@@ -1806,6 +1807,8 @@
 #define EB_NTSD_VERSION   4    /* offset of NTSD version byte */
 #define EB_NTSD_MAX_VER   (0)  /* maximum version # we know how to handle */
 
+#define EB_PKVMS_MINLEN   4    /* minimum data length of PKVMS extra block */
+
 #define EB_ASI_CRC32      0    /* offset of ASI Unix field's crc32 checksum */
 #define EB_ASI_MODE       4    /* offset of ASI Unix permission mode field */
 
@@ -2393,6 +2396,12 @@
 char  *fnfilter                  OF((ZCONST char *raw, uch *space,
                                      extent size));
 
+# if defined( UNICODE_SUPPORT) && defined( _MBCS)
+wchar_t *fnfilterw               OF((ZCONST wchar_t *src, wchar_t *dst,
+                                     extent siz));
+#endif
+
+
 /*---------------------------------------------------------------------------
     Decompression functions:
   ---------------------------------------------------------------------------*/
@@ -2604,7 +2613,7 @@
    int   SetFileSize     OF((FILE *file, zusz_t filesize));         /* local */
 #endif
 #ifndef MTS /* macro in MTS */
-   void  close_outfile   OF((__GPRO));                              /* local */
+   int  close_outfile   OF((__GPRO));                              /* local */
 #endif
 #ifdef SET_SYMLINK_ATTRIBS
    int  set_symlnk_attribs  OF((__GPRO__ slinkentry *slnk_entry));  /* local */
@@ -3008,7 +3017,7 @@
          !(((islochdr) || (isuxatt)) && \
            ((hostver) == 25 || (hostver) == 26 || (hostver) == 40))) || \
         (hostnum) == FS_HPFS_ || \
-        ((hostnum) == FS_NTFS_ && (hostver) == 50)) { \
+        ((hostnum) == FS_NTFS_ /* && (hostver) == 50 */ )) { \
         _OEM_INTERN((string)); \
     } else { \
         _ISO_INTERN((string)); \
diff -Naur a/unzpriv.h.orig b/unzpriv.h.orig
--- a/unzpriv.h.orig	1970-01-01 01:00:00.000000000 +0100
+++ b/unzpriv.h.orig	2019-12-02 01:04:10.077348607 +0000
@@ -0,0 +1,3125 @@
+/*
+  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
+*/
+/*---------------------------------------------------------------------------
+
+  unzpriv.h
+
+  This header file contains private (internal) macros, typedefs, prototypes
+  and global-variable declarations used by all of the UnZip source files.
+  In a prior life it was part of the main unzip.h header, but now it is only
+  included by that header if UNZIP_INTERNAL is defined.
+
+  ---------------------------------------------------------------------------*/
+
+
+
+#ifndef __unzpriv_h   /* prevent multiple inclusions */
+#define __unzpriv_h
+
+/* First thing: Signal all following code that we compile UnZip utilities! */
+#ifndef UNZIP
+#  define UNZIP
+#endif
+
+/* GRR 960204:  MORE defined here in preparation for removal altogether */
+#ifndef MORE
+# ifndef RISCOS
+#  define MORE
+# endif
+#endif
+
+/* fUnZip should never need to be reentrant */
+#ifdef FUNZIP
+#  ifdef REENTRANT
+#    undef REENTRANT
+#  endif
+#  ifdef DLL
+#    undef DLL
+#  endif
+#  ifdef SFX            /* fUnZip is NOT the sfx stub! */
+#    undef SFX
+#  endif
+#  ifdef USE_BZIP2      /* fUnZip does not support bzip2 decompression */
+#    undef USE_BZIP2
+#  endif
+#endif
+
+#if (defined(USE_ZLIB) && !defined(HAVE_ZL_INFLAT64) && !defined(NO_DEFLATE64))
+   /* zlib does not (yet?) provide Deflate64(tm) support */
+#  define NO_DEFLATE64
+#endif
+
+#ifdef NO_DEFLATE64
+   /* disable support for Deflate64(tm) */
+#  ifdef USE_DEFLATE64
+#    undef USE_DEFLATE64
+#  endif
+#else
+   /* enable Deflate64(tm) support unless compiling for SFX stub */
+#  if (!defined(USE_DEFLATE64) && !defined(SFX))
+#    define USE_DEFLATE64
+#  endif
+#endif
+
+/* disable bzip2 support for SFX stub, unless explicitly requested */
+#if (defined(SFX) && !defined(BZIP2_SFX) && defined(USE_BZIP2))
+#  undef USE_BZIP2
+#endif
+
+#if (defined(NO_VMS_TEXT_CONV) || defined(VMS))
+#  ifdef VMS_TEXT_CONV
+#    undef VMS_TEXT_CONV
+#  endif
+#else
+#  if (!defined(VMS_TEXT_CONV) && !defined(SFX))
+#    define VMS_TEXT_CONV
+#  endif
+#endif
+
+/* Enable -B option per default on specific systems, to allow backing up
+ * files that would be overwritten.
+ * (This list of systems must be kept in sync with the list of systems
+ * that add the B_flag to the UzpOpts structure, see unzip.h.)
+ */
+#if (!defined(NO_UNIXBACKUP) && !defined(UNIXBACKUP))
+#  if defined(UNIX) || defined(OS2) || defined(WIN32)
+#    define UNIXBACKUP
+#  endif
+#endif
+
+#if (defined(DLL) && !defined(REENTRANT))
+#  define REENTRANT
+#endif
+
+#if (!defined(DYNAMIC_CRC_TABLE) && !defined(FUNZIP))
+#  define DYNAMIC_CRC_TABLE
+#endif
+
+#if (defined(DYNAMIC_CRC_TABLE) && !defined(REENTRANT))
+#  ifndef DYNALLOC_CRCTAB
+#    define DYNALLOC_CRCTAB
+#  endif
+#endif
+
+/*---------------------------------------------------------------------------
+    OS-dependent configuration for UnZip internals
+  ---------------------------------------------------------------------------*/
+
+/* Some compiler distributions for Win32/i386 systems try to emulate
+ * a Unix (POSIX-compatible) environment.
+ */
+#if (defined(WIN32) && defined(UNIX))
+   /* UnZip does not support merging both ports in a single executable. */
+#  if (defined(FORCE_WIN32_OVER_UNIX) && defined(FORCE_UNIX_OVER_WIN32))
+     /* conflicting choice requests -> we prefer the Win32 environment */
+#    undef FORCE_UNIX_OVER_WIN32
+#  endif
+#  ifdef FORCE_WIN32_OVER_UNIX
+     /* native Win32 support was explicitly requested... */
+#    undef UNIX
+#  else
+     /* use the POSIX (Unix) emulation features by default... */
+#    undef WIN32
+#  endif
+#endif
+
+/* bad or (occasionally?) missing stddef.h: */
+#if (defined(M_XENIX) || defined(DNIX))
+#  define NO_STDDEF_H
+#endif
+
+#if (defined(M_XENIX) && !defined(M_UNIX))   /* SCO Xenix only, not SCO Unix */
+#  define SCO_XENIX
+#  define NO_LIMITS_H        /* no limits.h, but MODERN defined */
+#  define NO_UID_GID         /* no uid_t/gid_t */
+#  define size_t int
+#endif
+
+#ifdef realix   /* Modcomp Real/IX, real-time SysV.3 variant */
+#  define SYSV
+#  define NO_UID_GID         /* no uid_t/gid_t */
+#endif
+
+#if (defined(_AIX) && !defined(_ALL_SOURCE))
+#  define _ALL_SOURCE
+#endif
+
+#if defined(apollo)          /* defines __STDC__ */
+#    define NO_STDLIB_H
+#endif
+
+#ifdef DNIX
+#  define SYSV
+#  define SHORT_NAMES         /* 14-char limitation on path components */
+/* #  define FILENAME_MAX  14 */
+#  define FILENAME_MAX  NAME_MAX    /* GRR:  experiment */
+#endif
+
+#if (defined(SYSTEM_FIVE) || defined(__SYSTEM_FIVE))
+#  ifndef SYSV
+#    define SYSV
+#  endif
+#endif /* SYSTEM_FIVE || __SYSTEM_FIVE */
+#if (defined(M_SYSV) || defined(M_SYS5))
+#  ifndef SYSV
+#    define SYSV
+#  endif
+#endif /* M_SYSV || M_SYS5 */
+/* __SVR4 and __svr4__ catch Solaris on at least some combos of compiler+OS */
+#if (defined(__SVR4) || defined(__svr4__) || defined(sgi) || defined(__hpux))
+#  ifndef SYSV
+#    define SYSV
+#  endif
+#endif /* __SVR4 || __svr4__ || sgi || __hpux */
+#if (defined(LINUX) || defined(__QNX__))
+#  ifndef SYSV
+#    define SYSV
+#  endif
+#endif /* LINUX || __QNX__ */
+
+#if (defined(ultrix) || defined(__ultrix) || defined(bsd4_2))
+#  if (!defined(BSD) && !defined(SYSV))
+#    define BSD
+#  endif
+#endif /* ultrix || __ultrix || bsd4_2 */
+#if (defined(sun) || defined(pyr) || defined(CONVEX))
+#  if (!defined(BSD) && !defined(SYSV))
+#    define BSD
+#  endif
+#endif /* sun || pyr || CONVEX */
+
+#ifdef pyr  /* Pyramid:  has BSD and AT&T "universes" */
+#  ifdef BSD
+#    define pyr_bsd
+#    define USE_STRINGS_H  /* instead of more common string.h */
+#    define ZMEM           /* ZMEM now uses bcopy/bzero: not in AT&T universe */
+#  endif                   /* (AT&T memcpy claimed to be very slow, though) */
+#  define DECLARE_ERRNO
+#endif /* pyr */
+
+/* stat() bug for Borland, VAX C RTL, and Atari ST MiNT on TOS
+ * filesystems:  returns 0 for wildcards!  (returns 0xffffffff on Minix
+ * filesystem or `U:' drive under Atari MiNT.)  Watcom C was previously
+ * included on this list; it would be good to know what version the problem
+ * was fixed at, if it did exist.  */
+#if (defined(__TURBOC__) && !defined(WIN32))
+/*#  define WILD_STAT_BUG*/
+#endif
+#if (defined(VMS) || defined(__MINT__))
+#  define WILD_STAT_BUG
+#endif
+
+/*---------------------------------------------------------------------------
+    OS-dependent includes
+  ---------------------------------------------------------------------------*/
+
+
+/*---------------------------------------------------------------------------
+    API (DLL) section:
+  ---------------------------------------------------------------------------*/
+
+#ifdef DLL
+#  define MAIN   UZ_EXP UzpMain   /* was UzpUnzip */
+#  ifdef OS2DLL
+#    undef Info
+#    define REDIRECTC(c)             varputchar(__G__ c)
+#    define REDIRECTPRINT(buf,size)  varmessage(__G__ buf, size)
+#    define FINISH_REDIRECT()        finish_REXX_redirect(__G)
+#  else
+#    define REDIRECTC(c)
+#    define REDIRECTPRINT(buf,size)  0
+#    define FINISH_REDIRECT()        close_redirect(__G)
+#  endif
+#endif
+
+/*---------------------------------------------------------------------------
+    Acorn RISCOS section:
+  ---------------------------------------------------------------------------*/
+
+#ifdef RISCOS
+#  include "acorn/riscos.h"
+#endif
+
+/*---------------------------------------------------------------------------
+    Amiga section:
+  ---------------------------------------------------------------------------*/
+
+#ifdef AMIGA
+#  include "amiga/amiga.h"
+#endif
+
+/*---------------------------------------------------------------------------
+    AOS/VS section (somewhat similar to Unix, apparently):
+  ---------------------------------------------------------------------------*/
+
+#ifdef AOS_VS
+#  ifdef __FILEIO_C
+#    include "aosvs/aosvs.h"
+#  endif
+#endif
+
+/*---------------------------------------------------------------------------
+    Atari ST section:
+  ---------------------------------------------------------------------------*/
+
+#ifdef ATARI
+#  include <time.h>
+#  include <stat.h>
+#  include <fcntl.h>
+#  include <limits.h>
+#  define SYMLINKS
+#  define EXE_EXTENSION  ".tos"
+#  ifndef DATE_FORMAT
+#    define DATE_FORMAT  DF_DMY
+#  endif
+#  define DIR_END        '/'
+#  define INT_SPRINTF
+#  define timezone      _timezone
+#  define lenEOL        2
+#  define PutNativeEOL  {*q++ = native(CR); *q++ = native(LF);}
+#  undef SHORT_NAMES
+#  if (!defined(NOTIMESTAMP) && !defined(TIMESTAMP))
+#    define TIMESTAMP
+#  endif
+#endif
+
+/*---------------------------------------------------------------------------
+    AtheOS section:
+  ---------------------------------------------------------------------------*/
+
+#ifdef __ATHEOS__
+#  include "atheos/athcfg.h"
+#endif
+
+/*---------------------------------------------------------------------------
+    BeOS section:
+  ---------------------------------------------------------------------------*/
+
+#ifdef __BEOS__
+#  include "beos/beocfg.h"
+#endif
+
+/*---------------------------------------------------------------------------
+    Human68k/X680x0 section:
+  ---------------------------------------------------------------------------*/
+
+#ifdef __human68k__
+   /* DO NOT DEFINE DOS_OS2 HERE!  If Human68k is so much */
+   /*  like MS-DOS and/or OS/2, create DOS_H68_OS2 macro. */
+#  if (!defined(_MBCS) && !defined(NO_MBCS))
+     /* enable MBCS support by default for this system */
+#    define _MBCS
+#  endif
+#  if (defined(_MBCS) && defined(NO_MBCS))
+     /* disable MBCS support when explicitely requested */
+#    undef _MBCS
+#  endif
+#  include <time.h>
+#  include <fcntl.h>
+#  include <io.h>
+#  include <conio.h>
+#  include <sys/stat.h>
+#  ifdef HAVE_MBSTRING_H
+#    include <mbstring.h>
+#  endif
+#  ifdef HAVE_MBCTYPE_H
+#    include <mbctype.h>
+#  else
+#    ifndef _ismbblead
+#      define _ismbblead(c) (0x80 <= (c) && ((c) < 0xa0 || 0xe0 <= (c)))
+#    endif
+#  endif
+#  ifndef DATE_FORMAT
+#    define DATE_FORMAT DF_YMD   /* Japanese standard */
+#  endif
+#  define lenEOL        1
+#  define PutNativeEOL  *q++ = native(LF);
+#  define INT_SPRINTF
+#  define SYMLINKS
+#  ifdef SFX
+#    define MAIN main_sfx
+#  endif
+#endif
+
+/*---------------------------------------------------------------------------
+    Mac section:
+  ---------------------------------------------------------------------------*/
+
+#ifdef MACOS
+#  include "maccfg.h"
+#endif /* MACOS */
+
+/*---------------------------------------------------------------------------
+    MS-DOS, OS/2, FLEXOS section:
+  ---------------------------------------------------------------------------*/
+
+#ifdef WINDLL
+#  ifdef MORE
+#    undef MORE
+#  endif
+#  ifdef OS2_EAS
+#    undef OS2_EAS
+#  endif
+#endif
+
+#if (defined(_MSC_VER) || (defined(M_I86) && !defined(__WATCOMC__)))
+#  ifndef MSC
+#    define MSC               /* This should work for older MSC, too!  */
+#  endif
+#endif
+
+#if (defined(MSDOS) || defined(OS2) || defined(FLEXOS))
+#  include <sys/types.h>      /* off_t, time_t, dev_t, ... */
+#  include <sys/stat.h>
+#  include <io.h>             /* lseek(), open(), setftime(), dup(), creat() */
+#  include <time.h>           /* localtime() */
+#  include <fcntl.h>          /* O_BINARY for open() w/o CR/LF translation */
+
+#  ifdef OS2                  /* defined for all OS/2 compilers */
+#    include "os2/os2cfg.h"
+#  else
+#    ifdef FLEXOS
+#      include "flexos/flxcfg.h"
+#    else
+#      include "msdos/doscfg.h"
+#    endif
+#  endif
+
+#  if (defined(_MSC_VER) && (_MSC_VER == 700) && !defined(GRR))
+    /*
+     * ARGH.  MSC 7.0 libraries think times are based on 1899 Dec 31 00:00, not
+     *  1970 Jan 1 00:00.  So we have to diddle time_t's appropriately:  add or
+     *  subtract 70 years' worth of seconds; i.e., number of days times 86400;
+     *  i.e., (70*365 regular days + 17 leap days + 1 1899 day) * 86400 ==
+     *  (25550 + 17 + 1) * 86400 == 2209075200 seconds.  We know time_t is an
+     *  unsigned long (ulg) on the only system with this bug.
+     */
+#    define TIMET_TO_NATIVE(x)  (x) += (ulg)2209075200L;
+#    define NATIVE_TO_TIMET(x)  (x) -= (ulg)2209075200L;
+#  endif
+#  if (defined(__BORLANDC__) && (__BORLANDC__ >= 0x0450))
+#    define timezone      _timezone
+#  endif
+#  if (defined(__GO32__) || defined(FLEXOS))
+#    define DIR_END       '/'
+#  else
+#    define DIR_END       '\\'  /* OS uses '\\' as directory separator */
+#    define DIR_END2      '/'   /* also check for '/' (RTL may convert) */
+#  endif
+#  ifdef DATE_FORMAT
+#    undef DATE_FORMAT
+#  endif
+#  define DATE_FORMAT     dateformat()
+#  define lenEOL          2
+#  define PutNativeEOL    {*q++ = native(CR); *q++ = native(LF);}
+#  if (!defined(NO_EF_UT_TIME) && !defined(USE_EF_UT_TIME))
+#    define USE_EF_UT_TIME
+#  endif
+#endif /* MSDOS || OS2 || FLEXOS */
+
+/*---------------------------------------------------------------------------
+    MTS section (piggybacks UNIX, I think):
+  ---------------------------------------------------------------------------*/
+
+#ifdef MTS
+#  include <sys/types.h>      /* off_t, time_t, dev_t, ... */
+#  include <sys/stat.h>
+#  include <sys/file.h>       /* MTS uses this instead of fcntl.h */
+#  include <timeb.h>
+#  include <time.h>
+#  include <unix.h>           /* some important non-ANSI routines */
+#  define mkdir(s,n) (-1)     /* no "make directory" capability */
+#  define EBCDIC              /* set EBCDIC conversion on */
+#  define NO_STRNICMP         /* unzip's is as good the one in MTS */
+#  define USE_FWRITE
+#  define close_outfile()  fclose(G.outfile)   /* can't set time on files */
+#  define umask(n)            /* don't have umask() on MTS */
+#  define FOPWT         "w"   /* open file for writing in TEXT mode */
+#  ifndef DATE_FORMAT
+#    define DATE_FORMAT DF_MDY
+#  endif
+#  define lenEOL        1
+#  define PutNativeEOL  *q++ = native(LF);
+#endif /* MTS */
+
+ /*---------------------------------------------------------------------------
+    Novell Netware NLM section
+  ---------------------------------------------------------------------------*/
+
+#ifdef NLM
+#  include "netware/nlmcfg.h"
+#endif
+
+ /*---------------------------------------------------------------------------
+    QDOS section
+  ---------------------------------------------------------------------------*/
+
+#ifdef QDOS
+#  define DIRENT
+#  include <fcntl.h>
+#  include <unistd.h>
+#  include <sys/stat.h>
+#  include <time.h>
+#  include "qdos/izqdos.h"
+#  ifndef DATE_FORMAT
+#    define DATE_FORMAT DF_MDY
+#  endif
+#  define lenEOL        1
+#  define PutNativeEOL  *q++ = native(LF);
+#  define DIR_END       '_'
+#  define RETURN        QReturn
+#  undef PATH_MAX
+#  define PATH_MAX      36
+#  if (!defined(NOTIMESTAMP) && !defined(TIMESTAMP))
+#    define TIMESTAMP
+#  endif
+#  define SCREENSIZE(ttrows, ttcols)  screensize(ttrows, ttcols)
+#  define SCREENWIDTH 80
+#endif
+
+/*---------------------------------------------------------------------------
+    Tandem NSK section:
+  ---------------------------------------------------------------------------*/
+
+#ifdef TANDEM
+#  include "tandem.h"
+#  include <fcntl.h>
+#  ifndef __INT32
+     /* We are compiling with non-WIDE memory model, int = 16 bits */
+#    ifndef INT_16BIT
+#      define INT_16BIT   /* report "int" size is 16-bit to inflate setup */
+#    endif
+#    ifdef USE_DEFLATE64
+       /* Following required for 64k WSIZE of Deflate64 support */
+#      define MED_MEM     /* else OUTBUFSIZ is 64K and fails in do_string */
+#      define INBUFSIZ  8192  /* but larger buffer for real OSes */
+#    endif
+#  endif
+   /* use a single LF delimiter so that writes to 101 text files work */
+#  define PutNativeEOL  *q++ = native(LF);
+#  define lenEOL        1
+#  ifndef DATE_FORMAT
+#    define DATE_FORMAT  DF_DMY
+#  endif
+#  define SCREENLINES   25
+   /* USE_EF_UT_TIME is set in tandem.h */
+#  define RESTORE_UIDGID
+#  define NO_STRNICMP
+#endif
+
+/*---------------------------------------------------------------------------
+    THEOS section:
+  ---------------------------------------------------------------------------*/
+
+#ifdef THEOS
+#  include "theos/thscfg.h"
+#endif
+
+/*---------------------------------------------------------------------------
+    TOPS-20 section:
+  ---------------------------------------------------------------------------*/
+
+#ifdef TOPS20
+#  include <sys/types.h>        /* off_t, time_t, dev_t, ... */
+#  include <sys/stat.h>
+#  include <sys/param.h>
+#  include <sys/time.h>
+#  include <sys/timeb.h>
+#  include <sys/file.h>
+#  include <timex.h>
+#  include <monsym.h>           /* get amazing monsym() macro */
+   extern int open(), close(), read();
+   extern int stat(), unlink(), jsys(), fcntl();
+   extern long lseek(), dup(), creat();
+#  define strchr    index       /* GRR: necessary? */
+#  define strrchr   rindex
+#  define REALLY_SHORT_SYMS
+#  define NO_MKDIR
+#  ifndef HAVE_STRNICMP
+#    define NO_STRNICMP           /* probably not provided by TOPS20 C RTL  */
+#  endif
+#  define DIR_BEG       '<'
+#  define DIR_END       '>'
+#  define DIR_EXT       ".directory"
+#  ifndef DATE_FORMAT
+#    define DATE_FORMAT DF_MDY
+#  endif
+#  define EXE_EXTENSION ".exe"  /* just a guess... */
+#endif /* TOPS20 */
+
+/*---------------------------------------------------------------------------
+    Unix section:
+  ---------------------------------------------------------------------------*/
+
+#ifdef UNIX
+#  include "unix/unxcfg.h"
+#endif /* UNIX */
+
+/*---------------------------------------------------------------------------
+    VM/CMS and MVS section:
+  ---------------------------------------------------------------------------*/
+
+#ifdef CMS_MVS
+#  include "vmmvs.h"
+#  define CLOSE_INFILE()  close_infile(__G)
+#endif
+
+/*---------------------------------------------------------------------------
+    VMS section:
+  ---------------------------------------------------------------------------*/
+
+#ifdef VMS
+#  include "vms/vmscfg.h"
+#endif /* VMS */
+
+/*---------------------------------------------------------------------------
+    Win32 (Windows 95/NT) section:
+  ---------------------------------------------------------------------------*/
+
+#if (defined(WIN32) && !defined(POCKET_UNZIP) && !defined(_WIN32_WCE))
+#  include "win32/w32cfg.h"
+#endif
+
+/*---------------------------------------------------------------------------
+    Win32 Windows CE section (also POCKET_UNZIP)
+  ---------------------------------------------------------------------------*/
+
+#if (defined(_WIN32_WCE) || defined(POCKET_UNZIP))
+#  include "wince/wcecfg.h"
+#endif
+
+
+
+/* ----------------------------------------------------------------------------
+   MUST BE AFTER LARGE FILE INCLUDES
+   ---------------------------------------------------------------------------- */
+/* This stuff calls in types and messes up large file includes.  It needs to
+   go after large file defines in local includes.
+   I am guessing that moving them here probably broke some ports, but hey.
+   10/31/2004 EG */
+/* ----------------------------------------------------------------------------
+   Common includes
+   ---------------------------------------------------------------------------- */
+
+/* Some ports apply specific adjustments which must be in effect before
+   reading the "standard" include headers.
+ */
+
+#ifdef EFT
+#  define Z_OFF_T off_t  /* Amdahl UTS nonsense ("extended file types") */
+#else
+#if (defined(UNIX) && defined(_FILE_OFFSET_BITS) && (_FILE_OFFSET_BITS == 64))
+#  define Z_OFF_T off_t /* 64bit offsets to support 2GB < zipfile size < 4GB */
+#else
+#  define Z_OFF_T long
+#endif
+#endif
+
+#ifndef ZOFF_T_DEFINED
+   typedef Z_OFF_T zoff_t;
+#  define ZOFF_T_DEFINED
+#endif
+#ifndef Z_STAT_DEFINED
+   typedef struct stat z_stat;
+#  define Z_STAT_DEFINED
+#endif
+
+#ifndef MINIX            /* Minix needs it after all the other includes (?) */
+#  include <stdio.h>
+#endif
+
+#include <ctype.h>       /* skip for VMS, to use tolower() function? */
+#include <errno.h>       /* used in mapname() */
+#ifdef USE_STRINGS_H
+#  include <strings.h>   /* strcpy, strcmp, memcpy, index/rindex, etc. */
+#else
+#  include <string.h>    /* strcpy, strcmp, memcpy, strchr/strrchr, etc. */
+#endif
+#if (defined(MODERN) && !defined(NO_LIMITS_H))
+#  include <limits.h>    /* MAX/MIN constant symbols for system types... */
+#endif
+
+/* this include must be down here for SysV.4, for some reason... */
+#include <signal.h>      /* used in unzip.c, fileio.c */
+
+
+#ifdef MODERN
+#  ifndef NO_STDDEF_H
+#    include <stddef.h>
+#  endif
+#  ifndef NO_STDLIB_H
+#    include <stdlib.h>  /* standard library prototypes, malloc(), etc. */
+#  endif
+   typedef size_t extent;
+#else /* !MODERN */
+#  ifndef AOS_VS         /* mostly modern? */
+     Z_OFF_T lseek();
+#    ifdef VAXC          /* not fully modern, but has stdlib.h and void */
+#      include <stdlib.h>
+#    else
+       char *malloc();
+#    endif /* ?VAXC */
+#  endif /* !AOS_VS */
+   typedef unsigned int extent;
+#endif /* ?MODERN */
+
+
+
+
+/*************/
+/*  Defines  */
+/*************/
+
+#define UNZIP_BZ2VERS   46
+#ifdef ZIP64_SUPPORT
+# ifdef USE_BZIP2
+#  define UNZIP_VERSION   UNZIP_BZ2VERS
+# else
+#  define UNZIP_VERSION   45
+# endif
+#else
+#ifdef USE_DEFLATE64
+#  define UNZIP_VERSION   21   /* compatible with PKUNZIP 4.0 */
+#else
+#  define UNZIP_VERSION   20   /* compatible with PKUNZIP 2.0 */
+#endif
+#endif
+#define VMS_UNZIP_VERSION 42   /* if OS-needed-to-extract is VMS:  can do */
+
+#if (defined(MSDOS) || defined(OS2))
+#  define DOS_OS2
+#endif
+
+#if (defined(OS2) || defined(WIN32))
+#  define OS2_W32
+#endif
+
+#if (defined(DOS_OS2) || defined(WIN32))
+#  define DOS_OS2_W32
+#  define DOS_W32_OS2          /* historical:  don't use */
+#endif
+
+#if (defined(DOS_OS2_W32) || defined(__human68k__))
+#  define DOS_H68_OS2_W32
+#endif
+
+#if (defined(DOS_OS2) || defined(FLEXOS))
+#  define DOS_FLX_OS2
+#endif
+
+#if (defined(DOS_OS2_W32) || defined(FLEXOS))
+#  define DOS_FLX_OS2_W32
+#endif
+
+#if (defined(DOS_H68_OS2_W32) || defined(FLEXOS))
+#  define DOS_FLX_H68_OS2_W32
+#endif
+
+#if (defined(DOS_FLX_OS2) || defined(NLM))
+#  define DOS_FLX_NLM_OS2
+#endif
+
+#if (defined(DOS_FLX_OS2_W32) || defined(NLM))
+#  define DOS_FLX_NLM_OS2_W32
+#endif
+
+#if (defined(DOS_FLX_H68_OS2_W32) || defined(NLM))
+#  define DOS_FLX_H68_NLM_OS2_W32
+#endif
+
+#if (defined(TOPS20) || defined(VMS))
+#  define T20_VMS
+#endif
+
+#if (defined(MSDOS) || defined(T20_VMS))
+#  define DOS_T20_VMS
+#endif
+
+#if (defined(__ATHEOS__) || defined(__BEOS__))
+#  define ATH_BEO
+#endif
+
+#if (defined(ATH_BEO) || defined(UNIX))
+#  define ATH_BEO_UNX
+#endif
+
+#if (defined(ATH_BEO_UNX) || defined(THEOS))
+#  define ATH_BEO_THS_UNX
+#endif
+
+/* clean up with a few defaults */
+#ifndef DIR_END
+#  define DIR_END       '/'     /* last char before program name or filename */
+#endif
+#ifndef DATE_FORMAT
+# ifdef DATEFMT_ISO_DEFAULT
+#  define DATE_FORMAT   DF_YMD  /* defaults to invariant ISO-style */
+# else
+#  define DATE_FORMAT   DF_MDY  /* defaults to US convention */
+# endif
+#endif
+#ifndef DATE_SEPCHAR
+#  define DATE_SEPCHAR  '-'
+#endif
+#ifndef CLOSE_INFILE
+#  define CLOSE_INFILE()  close(G.zipfd)
+#endif
+#ifndef RETURN
+#  define RETURN        return  /* only used in main() */
+#endif
+#ifndef EXIT
+#  define EXIT          exit
+#endif
+#ifndef USAGE
+#  define USAGE(ret)    usage(__G__ (ret))    /* used in unzip.c, zipinfo.c */
+#endif
+#ifndef TIMET_TO_NATIVE         /* everybody but MSC 7.0 and Macintosh */
+#  define TIMET_TO_NATIVE(x)
+#  define NATIVE_TO_TIMET(x)
+#endif
+#ifndef STRNICMP
+#  ifdef NO_STRNICMP
+#    define STRNICMP zstrnicmp
+#  else
+#    define STRNICMP strnicmp
+#  endif
+#endif
+
+
+#if (defined(DOS_FLX_NLM_OS2_W32) || defined(ATH_BEO_UNX) || defined(RISCOS))
+#  ifndef HAVE_UNLINK
+#    define HAVE_UNLINK
+#  endif
+#endif
+#if (defined(AOS_VS) || defined(ATARI)) /* GRR: others? */
+#  ifndef HAVE_UNLINK
+#    define HAVE_UNLINK
+#  endif
+#endif
+
+/* OS-specific exceptions to the "ANSI <--> INT_SPRINTF" rule */
+
+#if (!defined(PCHAR_SPRINTF) && !defined(INT_SPRINTF))
+#  if (defined(SYSV) || defined(CONVEX) || defined(NeXT) || defined(BSD4_4))
+#    define INT_SPRINTF      /* sprintf() returns int:  SysVish/Posix */
+#  endif
+#  if (defined(DOS_FLX_NLM_OS2_W32) || defined(VMS) || defined(AMIGA))
+#    define INT_SPRINTF      /* sprintf() returns int:  ANSI */
+#  endif
+#  if (defined(ultrix) || defined(__ultrix)) /* Ultrix 4.3 and newer */
+#    if (defined(POSIX) || defined(__POSIX))
+#      define INT_SPRINTF    /* sprintf() returns int:  ANSI/Posix */
+#    endif
+#    ifdef __GNUC__
+#      define PCHAR_SPRINTF  /* undetermined actual return value */
+#    endif
+#  endif
+#  if (defined(__osf__) || defined(_AIX) || defined(CMS_MVS) || defined(THEOS))
+#    define INT_SPRINTF      /* sprintf() returns int:  ANSI/Posix */
+#  endif
+#  if defined(sun)
+#    define PCHAR_SPRINTF    /* sprintf() returns char *:  SunOS cc *and* gcc */
+#  endif
+#endif
+
+/* defaults that we hope will take care of most machines in the future */
+
+#if (!defined(PCHAR_SPRINTF) && !defined(INT_SPRINTF))
+#  ifdef __STDC__
+#    define INT_SPRINTF      /* sprintf() returns int:  ANSI */
+#  endif
+#  ifndef INT_SPRINTF
+#    define PCHAR_SPRINTF    /* sprintf() returns char *:  BSDish */
+#  endif
+#endif
+
+#define MSG_STDERR(f)  (f & 1)        /* bit 0:  0 = stdout, 1 = stderr */
+#define MSG_INFO(f)    ((f & 6) == 0) /* bits 1 and 2:  0 = info */
+#define MSG_WARN(f)    ((f & 6) == 2) /* bits 1 and 2:  1 = warning */
+#define MSG_ERROR(f)   ((f & 6) == 4) /* bits 1 and 2:  2 = error */
+#define MSG_FATAL(f)   ((f & 6) == 6) /* bits 1 and 2:  (3 = fatal error) */
+#define MSG_ZFN(f)     (f & 0x0008)   /* bit 3:  1 = print zipfile name */
+#define MSG_FN(f)      (f & 0x0010)   /* bit 4:  1 = print filename */
+#define MSG_LNEWLN(f)  (f & 0x0020)   /* bit 5:  1 = leading newline if !SOL */
+#define MSG_TNEWLN(f)  (f & 0x0040)   /* bit 6:  1 = trailing newline if !SOL */
+#define MSG_MNEWLN(f)  (f & 0x0080)   /* bit 7:  1 = trailing NL for prompts */
+/* the following are subject to change */
+#define MSG_NO_WGUI(f) (f & 0x0100)   /* bit 8:  1 = skip if Windows GUI */
+#define MSG_NO_AGUI(f) (f & 0x0200)   /* bit 9:  1 = skip if Acorn GUI */
+#define MSG_NO_DLL2(f) (f & 0x0400)   /* bit 10:  1 = skip if OS/2 DLL */
+#define MSG_NO_NDLL(f) (f & 0x0800)   /* bit 11:  1 = skip if WIN32 DLL */
+#define MSG_NO_WDLL(f) (f & 0x1000)   /* bit 12:  1 = skip if Windows DLL */
+
+#if (defined(MORE) && !defined(SCREENLINES))
+#  ifdef DOS_FLX_NLM_OS2_W32
+#    define SCREENLINES 25  /* can be (should be) a function instead */
+#  else
+#    define SCREENLINES 24  /* VT-100s are assumed to be minimal hardware */
+#  endif
+#endif
+#if (defined(MORE) && !defined(SCREENSIZE))
+#  ifndef SCREENWIDTH
+#    define SCREENSIZE(scrrows, scrcols) { \
+          if ((scrrows) != NULL) *(scrrows) = SCREENLINES; }
+#  else
+#    define SCREENSIZE(scrrows, scrcols) { \
+          if ((scrrows) != NULL) *(scrrows) = SCREENLINES; \
+          if ((scrcols) != NULL) *(scrcols) = SCREENWIDTH; }
+#  endif
+#endif
+
+#if (defined(__16BIT__) || defined(MED_MEM) || defined(SMALL_MEM))
+# define DIR_BLKSIZ  64     /* number of directory entries per block
+                             *  (should fit in 4096 bytes, usually) */
+#else
+# define DIR_BLKSIZ 16384   /* use more memory, to reduce long-range seeks */
+#endif
+
+#ifndef WSIZE
+#  ifdef USE_DEFLATE64
+#    define WSIZE   65536L  /* window size--must be a power of two, and */
+#  else                     /*  at least 64K for PKZip's deflate64 method */
+#    define WSIZE   0x8000  /* window size--must be a power of two, and */
+#  endif                    /*  at least 32K for zip's deflate method */
+#endif
+
+#ifdef __16BIT__
+#  ifndef INT_16BIT
+#    define INT_16BIT       /* on 16-bit systems int size is 16 bits */
+#  endif
+#else
+#  define nearmalloc  malloc
+#  define nearfree    free
+#  if (!defined(__IBMC__) || !defined(OS2))
+#    ifndef near
+#      define near
+#    endif
+#    ifndef far
+#      define far
+#    endif
+#  endif
+#endif
+
+#if (defined(DYNALLOC_CRCTAB) && !defined(DYNAMIC_CRC_TABLE))
+#  undef DYNALLOC_CRCTAB
+#endif
+
+#if (defined(DYNALLOC_CRCTAB) && defined(REENTRANT))
+#  undef DYNALLOC_CRCTAB   /* not safe with reentrant code */
+#endif
+
+#if (defined(USE_ZLIB) && !defined(USE_OWN_CRCTAB))
+#  ifdef DYNALLOC_CRCTAB
+#    undef DYNALLOC_CRCTAB
+#  endif
+#endif
+
+#if (defined(USE_ZLIB) && defined(ASM_CRC))
+#  undef ASM_CRC
+#endif
+
+#ifdef USE_ZLIB
+#  ifdef IZ_CRC_BE_OPTIMIZ
+#    undef IZ_CRC_BE_OPTIMIZ
+#  endif
+#  ifdef IZ_CRC_LE_OPTIMIZ
+#    undef IZ_CRC_LE_OPTIMIZ
+#  endif
+#endif
+#if (!defined(IZ_CRC_BE_OPTIMIZ) && !defined(IZ_CRC_LE_OPTIMIZ))
+#  ifdef IZ_CRCOPTIM_UNFOLDTBL
+#    undef IZ_CRCOPTIM_UNFOLDTBL
+#  endif
+#endif
+
+#ifndef INBUFSIZ
+#  if (defined(MED_MEM) || defined(SMALL_MEM))
+#    define INBUFSIZ  2048  /* works for MS-DOS small model */
+#  else
+#    define INBUFSIZ  8192  /* larger buffers for real OSes */
+#  endif
+#endif
+
+#if (defined(INT_16BIT) && (defined(USE_DEFLATE64) || lenEOL > 1))
+   /* For environments using 16-bit integers OUTBUFSIZ must be limited to
+    * less than 64k (do_string() uses "unsigned" in calculations involving
+    * OUTBUFSIZ).  This is achieved by defining MED_MEM when WSIZE = 64k (aka
+    * Deflate64 support enabled) or EOL markers contain multiple characters.
+    * (The rule gets applied AFTER the default rule for INBUFSIZ because it
+    * is not neccessary to reduce INBUFSIZE in this case.)
+    */
+#  if (!defined(SMALL_MEM) && !defined(MED_MEM))
+#    define MED_MEM
+#  endif
+#endif
+
+/* Logic for case of small memory, length of EOL > 1:  if OUTBUFSIZ == 2048,
+ * OUTBUFSIZ>>1 == 1024 and OUTBUFSIZ>>7 == 16; therefore rawbuf is 1008 bytes
+ * and transbuf 1040 bytes.  Have room for 32 extra EOL chars; 1008/32 == 31.5
+ * chars/line, smaller than estimated 35-70 characters per line for C source
+ * and normal text.  Hence difference is sufficient for most "average" files.
+ * (Argument scales for larger OUTBUFSIZ.)
+ */
+#ifdef SMALL_MEM          /* i.e., 16-bit OSes:  MS-DOS, OS/2 1.x, etc. */
+#  define LoadFarString(x)       fLoadFarString(__G__ (x))
+#  define LoadFarStringSmall(x)  fLoadFarStringSmall(__G__ (x))
+#  define LoadFarStringSmall2(x) fLoadFarStringSmall2(__G__ (x))
+#  if (defined(_MSC_VER) && (_MSC_VER >= 600))
+#    define zfstrcpy(dest, src)  _fstrcpy((dest), (src))
+#    define zfstrcmp(s1, s2)     _fstrcmp((s1), (s2))
+#  endif
+#  if !(defined(SFX) || defined(FUNZIP))
+#    if (defined(_MSC_VER))
+#      define zfmalloc(sz)       _fmalloc((sz))
+#      define zffree(x)          _ffree(x)
+#    endif
+#    if (defined(__TURBOC__))
+#      include <alloc.h>
+#      define zfmalloc(sz)       farmalloc((unsigned long)(sz))
+#      define zffree(x)          farfree(x)
+#    endif
+#  endif /* !(SFX || FUNZIP) */
+#  ifndef Far
+#    define Far far  /* __far only works for MSC 6.00, not 6.0a or Borland */
+#  endif
+#  define OUTBUFSIZ INBUFSIZ
+#  if (lenEOL == 1)
+#    define RAWBUFSIZ (OUTBUFSIZ>>1)
+#  else
+#    define RAWBUFSIZ ((OUTBUFSIZ>>1) - (OUTBUFSIZ>>7))
+#  endif
+#  define TRANSBUFSIZ (OUTBUFSIZ-RAWBUFSIZ)
+   typedef short  shrint;            /* short/int or "shrink int" (unshrink) */
+#else
+#  define zfstrcpy(dest, src)       strcpy((dest), (src))
+#  define zfstrcmp(s1, s2)          strcmp((s1), (s2))
+#  define zfmalloc                  malloc
+#  define zffree(x)                 free(x)
+#  ifdef QDOS
+#    define LoadFarString(x)        Qstrfix(x)   /* fix up _ for '.' */
+#    define LoadFarStringSmall(x)   Qstrfix(x)
+#    define LoadFarStringSmall2(x)  Qstrfix(x)
+#  else
+#    define LoadFarString(x)        (char *)(x)
+#    define LoadFarStringSmall(x)   (char *)(x)
+#    define LoadFarStringSmall2(x)  (char *)(x)
+#  endif
+#  ifdef MED_MEM
+#    define OUTBUFSIZ 0xFF80         /* can't malloc arrays of 0xFFE8 or more */
+#    define TRANSBUFSIZ 0xFF80
+     typedef short  shrint;
+#  else
+#    define OUTBUFSIZ (lenEOL*WSIZE) /* more efficient text conversion */
+#    define TRANSBUFSIZ (lenEOL*OUTBUFSIZ)
+#    ifdef AMIGA
+       typedef short shrint;
+#    else
+       typedef int  shrint;          /* for efficiency/speed, we hope... */
+#    endif
+#  endif /* ?MED_MEM */
+#  define RAWBUFSIZ OUTBUFSIZ
+#endif /* ?SMALL_MEM */
+
+#ifndef Far
+#  define Far
+#endif
+
+#ifndef Cdecl
+#  define Cdecl
+#endif
+
+#ifndef MAIN
+#  define MAIN  main
+#endif
+
+#ifdef SFX      /* disable some unused features for SFX executables */
+#  ifndef NO_ZIPINFO
+#    define NO_ZIPINFO
+#  endif
+#  ifdef TIMESTAMP
+#    undef TIMESTAMP
+#  endif
+#endif
+
+#ifdef SFX
+#  ifdef CHEAP_SFX_AUTORUN
+#    ifndef NO_SFX_EXDIR
+#      define NO_SFX_EXDIR
+#    endif
+#  endif
+#  ifndef NO_SFX_EXDIR
+#    ifndef SFX_EXDIR
+#      define SFX_EXDIR
+#    endif
+#  else
+#    ifdef SFX_EXDIR
+#      undef SFX_EXDIR
+#    endif
+#  endif
+#endif
+
+/* user may have defined both by accident...  NOTIMESTAMP takes precedence */
+#if (defined(TIMESTAMP) && defined(NOTIMESTAMP))
+#  undef TIMESTAMP
+#endif
+
+#if (!defined(COPYRIGHT_CLEAN) && !defined(USE_SMITH_CODE))
+#  define COPYRIGHT_CLEAN
+#endif
+
+/* The LZW patent is expired worldwide since 2004-Jul-07, so USE_UNSHRINK
+ * is now enabled by default.  See unshrink.c.
+ */
+#if (!defined(LZW_CLEAN) && !defined(USE_UNSHRINK))
+#  define USE_UNSHRINK
+#endif
+
+#ifndef O_BINARY
+#  define O_BINARY  0
+#endif
+
+#ifndef PIPE_ERROR
+#  ifndef EPIPE
+#    define EPIPE -1
+#  endif
+#  define PIPE_ERROR (errno == EPIPE)
+#endif
+
+/* File operations--use "b" for binary if allowed or fixed length 512 on VMS */
+#ifdef VMS
+#  define FOPR  "r","ctx=stm"
+#  define FOPM  "r+","ctx=stm","rfm=fix","mrs=512"
+#  define FOPW  "w","ctx=stm","rfm=fix","mrs=512"
+#  define FOPWR "w+","ctx=stm","rfm=fix","mrs=512"
+#endif /* VMS */
+
+#ifdef CMS_MVS
+/* Binary files must be RECFM=F,LRECL=1 for ftell() to get correct pos */
+/* ...unless byteseek is used.  Let's try that for a while.            */
+#  define FOPR "rb,byteseek"
+#  define FOPM "r+b,byteseek"
+#  ifdef MVS
+#    define FOPW "wb,recfm=u,lrecl=32760,byteseek" /* New binary files */
+#    define FOPWE "wb"                             /* Existing binary files */
+#    define FOPWT "w,lrecl=133"                    /* New text files */
+#    define FOPWTE "w"                             /* Existing text files */
+#  else
+#    define FOPW "wb,recfm=v,lrecl=32760"
+#    define FOPWT "w"
+#  endif
+#endif /* CMS_MVS */
+
+#ifdef TOPS20          /* TOPS-20 MODERN?  You kidding? */
+#  define FOPW "w8"
+#endif /* TOPS20 */
+
+/* Defaults when nothing special has been defined previously. */
+#ifdef MODERN
+#  ifndef FOPR
+#    define FOPR "rb"
+#  endif
+#  ifndef FOPM
+#    define FOPM "r+b"
+#  endif
+#  ifndef FOPW
+#    define FOPW "wb"
+#  endif
+#  ifndef FOPWT
+#    define FOPWT "wt"
+#  endif
+#  ifndef FOPWR
+#    define FOPWR "w+b"
+#  endif
+#else /* !MODERN */
+#  ifndef FOPR
+#    define FOPR "r"
+#  endif
+#  ifndef FOPM
+#    define FOPM "r+"
+#  endif
+#  ifndef FOPW
+#    define FOPW "w"
+#  endif
+#  ifndef FOPWT
+#    define FOPWT "w"
+#  endif
+#  ifndef FOPWR
+#    define FOPWR "w+"
+#  endif
+#endif /* ?MODERN */
+
+/*
+ * If <limits.h> exists on most systems, should include that, since it may
+ * define some or all of the following:  NAME_MAX, PATH_MAX, _POSIX_NAME_MAX,
+ * _POSIX_PATH_MAX.
+ */
+#ifdef DOS_FLX_NLM_OS2_W32
+#  include <limits.h>
+#endif
+
+/* 2008-07-22 SMS.
+ * Unfortunately, on VMS, <limits.h> exists, and is included by <stdlib.h>
+ * (so it's pretty much unavoidable), and it defines PATH_MAX to a fixed
+ * short value (256, correct only for older systems without ODS-5 support),
+ * rather than one based on the real RMS NAM[L] situation.  So, we
+ * artificially undefine it here, to allow our better-defined _MAX_PATH
+ * (see vms/vmscfg.h) to be used.
+ */
+#ifdef VMS
+#  undef PATH_MAX
+#endif
+
+#ifndef PATH_MAX
+#  ifdef MAXPATHLEN
+#    define PATH_MAX      MAXPATHLEN    /* in <sys/param.h> on some systems */
+#  else
+#    ifdef _MAX_PATH
+#      define PATH_MAX    _MAX_PATH
+#    else
+#      if FILENAME_MAX > 255
+#        define PATH_MAX  FILENAME_MAX  /* used like PATH_MAX on some systems */
+#      else
+#        define PATH_MAX  1024
+#      endif
+#    endif /* ?_MAX_PATH */
+#  endif /* ?MAXPATHLEN */
+#endif /* !PATH_MAX */
+
+/*
+ * buffer size required to hold the longest legal local filepath
+ * (including the trailing '\0')
+ */
+#define FILNAMSIZ  PATH_MAX
+
+#ifdef UNICODE_SUPPORT
+# if !(defined(UTF8_MAYBE_NATIVE) || defined(UNICODE_WCHAR))
+#  undef UNICODE_SUPPORT
+# endif
+#endif
+/* 2007-09-18 SMS.
+ * Include <locale.h> here if it will be needed later for Unicode.
+ * Otherwise, SETLOCALE may be defined here, and then defined again
+ * (differently) when <locale.h> is read later.
+ */
+#ifdef UNICODE_SUPPORT
+# ifdef UNICODE_WCHAR
+#  if !(defined(_WIN32_WCE) || defined(POCKET_UNZIP))
+#   include <wchar.h>
+#  endif
+# endif
+# ifndef _MBCS  /* no need to include <locale.h> twice, see below */
+#   include <locale.h>
+#   ifndef SETLOCALE
+#     define SETLOCALE(category, locale) setlocale(category, locale)
+#   endif
+# endif
+#endif /* UNICODE_SUPPORT */
+
+/* DBCS support for Info-ZIP  (mainly for japanese (-: )
+ * by Yoshioka Tsuneo (QWF00133@nifty.ne.jp,tsuneo-y@is.aist-nara.ac.jp)
+ */
+#ifdef _MBCS
+#  include <locale.h>
+   /* Multi Byte Character Set */
+#  define ___MBS_TMP_DEF  char *___tmp_ptr;
+#  define ___TMP_PTR      ___tmp_ptr
+#  ifndef CLEN
+#    define NEED_UZMBCLEN
+#    define CLEN(ptr) (int)uzmbclen((ZCONST unsigned char *)(ptr))
+#  endif
+#  ifndef PREINCSTR
+#    define PREINCSTR(ptr) (ptr += CLEN(ptr))
+#  endif
+#  define POSTINCSTR(ptr) (___TMP_PTR=(char *)(ptr), PREINCSTR(ptr),___TMP_PTR)
+   char *plastchar OF((ZCONST char *ptr, extent len));
+#  define lastchar(ptr, len) ((int)(unsigned)*plastchar(ptr, len))
+#  ifndef MBSCHR
+#    define NEED_UZMBSCHR
+#    define MBSCHR(str,c) (char *)uzmbschr((ZCONST unsigned char *)(str), c)
+#  endif
+#  ifndef MBSRCHR
+#    define NEED_UZMBSRCHR
+#    define MBSRCHR(str,c) (char *)uzmbsrchr((ZCONST unsigned char *)(str), c)
+#  endif
+#  ifndef SETLOCALE
+#    define SETLOCALE(category, locale) setlocale(category, locale)
+#  endif
+#else /* !_MBCS */
+#  define ___MBS_TMP_DEF
+#  define ___TMP_PTR
+#  define CLEN(ptr) 1
+#  define PREINCSTR(ptr) (++(ptr))
+#  define POSTINCSTR(ptr) ((ptr)++)
+#  define plastchar(ptr, len) (&ptr[(len)-1])
+#  define lastchar(ptr, len) (ptr[(len)-1])
+#  define MBSCHR(str, c) strchr(str, c)
+#  define MBSRCHR(str, c) strrchr(str, c)
+#  ifndef SETLOCALE
+#    define SETLOCALE(category, locale)
+#  endif
+#endif /* ?_MBCS */
+#define INCSTR(ptr) PREINCSTR(ptr)
+
+
+#if (defined(MALLOC_WORK) && !defined(MY_ZCALLOC))
+   /* Any system without a special calloc function */
+# ifndef zcalloc
+#  define zcalloc(items, size) \
+          (zvoid far *)calloc((unsigned)(items), (unsigned)(size))
+# endif
+# ifndef zcfree
+#  define zcfree    free
+# endif
+#endif /* MALLOC_WORK && !MY_ZCALLOC */
+
+#if (defined(CRAY) && defined(ZMEM))
+#  undef ZMEM
+#endif
+
+#ifdef ZMEM
+#  undef ZMEM
+#  define memcmp(b1,b2,len)      bcmp(b2,b1,len)
+#  define memcpy(dest,src,len)   bcopy(src,dest,len)
+#  define memzero                bzero
+#else
+#  define memzero(dest,len)      memset(dest,0,len)
+#endif
+
+#ifndef TRUE
+#  define TRUE      1   /* sort of obvious */
+#endif
+#ifndef FALSE
+#  define FALSE     0
+#endif
+
+#ifndef SEEK_SET
+#  define SEEK_SET  0
+#  define SEEK_CUR  1
+#  define SEEK_END  2
+#endif
+
+#if (!defined(S_IEXEC) && defined(S_IXUSR))
+#  define S_IEXEC   S_IXUSR
+#endif
+
+#if (defined(UNIX) && defined(S_IFLNK) && !defined(MTS))
+#  define SYMLINKS
+#  ifndef S_ISLNK
+#    define S_ISLNK(m)  (((m) & S_IFMT) == S_IFLNK)
+#  endif
+#endif /* UNIX && S_IFLNK && !MTS */
+
+#ifndef S_ISDIR
+#  ifdef CMS_MVS
+#    define S_ISDIR(m)  (FALSE)
+#  else
+#    define S_ISDIR(m)  (((m) & S_IFMT) == S_IFDIR)
+# endif
+#endif
+
+#ifndef IS_VOLID
+#  define IS_VOLID(m)  ((m) & 0x08)
+#endif
+
+/***********************************/
+/*  LARGE_FILE_SUPPORT             */
+/***********************************/
+/* This whole section lifted from Zip 3b tailor.h
+
+ * Types are in OS dependent headers (eg, w32cfg.h)
+ *
+ * LARGE_FILE_SUPPORT and ZIP64_SUPPORT are automatically
+ * set in OS dependent headers (for some ports) based on the port and compiler.
+ *
+ * Function prototypes are below as OF is defined earlier in this file
+ * but after OS dependent header is included.
+ *
+ * E. Gordon 9/21/2003
+ * Updated 1/28/2004
+ * Lifted and placed here 6/7/2004 - Myles Bennett
+ */
+#ifdef LARGE_FILE_SUPPORT
+  /* 64-bit Large File Support */
+
+/* ---------------------------- */
+
+# if defined(UNIX) || defined(VMS)
+
+    /* 64-bit stat functions */
+#   define zstat stat
+#   define zfstat fstat
+
+    /* 64-bit fseeko */
+#   define zlseek lseek
+#   define zfseeko fseeko
+
+    /* 64-bit ftello */
+#   define zftello ftello
+
+    /* 64-bit fopen */
+#   define zfopen fopen
+#   define zfdopen fdopen
+
+# endif /* UNIX || VMS */
+
+/* ---------------------------- */
+
+# ifdef WIN32
+
+#   if defined(_MSC_VER) || defined(__MINGW32__) || defined(__LCC__)
+    /* MS C (VC), MinGW GCC port and LCC-32 use the MS C Runtime lib */
+
+      /* 64-bit stat functions */
+#     define zstat _stati64
+#     define zfstat _fstati64
+
+      /* 64-bit lseek */
+#     define zlseek _lseeki64
+
+#     if defined(_MSC_VER) && (_MSC_VER >= 1400)
+        /* Beginning with VS 8.0 (Visual Studio 2005, MSC 14), the Microsoft
+           C rtl publishes its (previously internal) implmentations of
+           "fseeko" and "ftello" for 64-bit file offsets. */
+        /* 64-bit fseeko */
+#       define zfseeko _fseeki64
+        /* 64-bit ftello */
+#       define zftello _ftelli64
+
+#     else /* not (defined(_MSC_VER) && (_MSC_VER >= 1400)) */
+
+#     if defined(__MSVCRT_VERSION__) && (__MSVCRT_VERSION__ >= 0x800)
+        /* Up-to-date versions of MinGW define the macro __MSVCRT_VERSION__
+           to denote the version of the MS C rtl dll used for linking.  When
+           configured to link against the runtime of MS Visual Studio 8 (or
+           newer), the built-in 64-bit fseek/ftell functions are available. */
+        /* 64-bit fseeko */
+#       define zfseeko _fseeki64
+        /* 64-bit ftello */
+#       define zftello _ftelli64
+
+#     else /* !(defined(__MSVCRT_VERSION__) && (__MSVCRT_VERSION__>=0x800)) */
+        /* The version of the C runtime is lower than MSC 14 or unknown. */
+
+        /* The newest MinGW port contains built-in extensions to the MSC rtl
+           that provide fseeko and ftello, but our implementations will do
+           for now. */
+       /* 64-bit fseeko */
+       int zfseeko OF((FILE *, zoff_t, int));
+
+       /* 64-bit ftello */
+       zoff_t zftello OF((FILE *));
+
+#     endif /* ? (__MSVCRT_VERSION__ >= 0x800) */
+#     endif /* ? (_MSC_VER >= 1400) */
+
+      /* 64-bit fopen */
+#     define zfopen fopen
+#     define zfdopen fdopen
+
+#   endif /* _MSC_VER || __MINGW__ || __LCC__ */
+
+#   ifdef __CYGWIN__
+    /* CYGWIN GCC Posix emulator on Windows
+       (configuration not yet finished/tested)  */
+
+      /* 64-bit stat functions */
+#     define zstat _stati64
+#     define zfstat _fstati64
+
+      /* 64-bit lseek */
+#     define zlseek _lseeki64
+
+      /* 64-bit fseeko */
+#     define zfseeko fseeko
+
+      /* 64-bit ftello */
+#     define zftello ftello
+
+      /* 64-bit fopen */
+#     define zfopen fopen
+#     define zfdopen fdopen
+
+#   endif
+#   if defined(__WATCOMC__) || defined(__BORLANDC__)
+    /* WATCOM C and Borland C provide their own C runtime libraries,
+       but they are sufficiently compatible with MS CRTL. */
+
+      /* 64-bit stat functions */
+#     define zstat _stati64
+#     define zfstat _fstati64
+
+#   ifdef __WATCOMC__
+      /* 64-bit lseek */
+#     define zlseek _lseeki64
+#   endif
+
+      /* 64-bit fseeko */
+      int zfseeko OF((FILE *, zoff_t, int));
+
+      /* 64-bit ftello */
+      zoff_t zftello OF((FILE *));
+
+      /* 64-bit fopen */
+#     define zfopen fopen
+#     define zfdopen fdopen
+
+#   endif
+#   ifdef __IBMC__
+      /* IBM C */
+
+      /* 64-bit stat functions */
+
+      /* 64-bit fseeko */
+
+      /* 64-bit ftello */
+
+      /* 64-bit fopen */
+
+#   endif
+
+# endif /* WIN32 */
+
+#else
+  /* No Large File Support */
+
+# ifndef REGULUS  /* returns the inode number on success(!)...argh argh argh */
+#   define zstat stat
+# endif
+# define zfstat fstat
+# define zlseek lseek
+# define zfseeko fseek
+# define zftello ftell
+# define zfopen fopen
+# define zfdopen fdopen
+
+# if defined(UNIX) || defined(VMS) || defined(WIN32)
+    /* For these systems, implement "64bit file vs. 32bit prog" check  */
+#   ifndef DO_SAFECHECK_2GB
+#     define DO_SAFECHECK_2GB
+#   endif
+# endif
+
+#endif
+
+/* No "64bit file vs. 32bit prog" check for SFX stub, to save space */
+#if (defined(DO_SAFECHECK_2GB) && defined(SFX))
+#  undef DO_SAFECHECK_2GB
+#endif
+
+#ifndef SSTAT
+#  ifdef WILD_STAT_BUG
+#    define SSTAT(path,pbuf) (iswild(path) || zstat(path,pbuf))
+#  else
+#    define SSTAT    zstat
+#  endif
+#endif
+
+
+/* Default fzofft() format selection. */
+
+#ifndef FZOFFT_FMT
+
+#  ifdef LARGE_FILE_SUPPORT
+#    define FZOFFT_FMT "ll"
+#    define FZOFFT_HEX_WID_VALUE "16"
+#  else /* def LARGE_FILE_SUPPORT */
+#    define FZOFFT_FMT "l"
+#    define FZOFFT_HEX_WID_VALUE "8"
+#  endif /* def LARGE_FILE_SUPPORT */
+
+#endif /* ndef FZOFFT_FMT */
+
+#define FZOFFT_HEX_WID ((char *) -1)
+#define FZOFFT_HEX_DOT_WID ((char *) -2)
+
+#define FZOFFT_NUM 4            /* Number of chambers. */
+#define FZOFFT_LEN 24           /* Number of characters/chamber. */
+
+
+#ifdef SHORT_SYMS                   /* Mark Williams C, ...? */
+#  define extract_or_test_files     xtr_or_tst_files
+#  define extract_or_test_member    xtr_or_tst_member
+#endif
+
+#ifdef REALLY_SHORT_SYMS            /* TOPS-20 linker:  first 6 chars */
+#  define process_cdir_file_hdr     XXpcdfh
+#  define process_local_file_hdr    XXplfh
+#  define extract_or_test_files     XXxotf  /* necessary? */
+#  define extract_or_test_member    XXxotm  /* necessary? */
+#  define check_for_newer           XXcfn
+#  define overwrite_all             XXoa
+#  define process_all_files         XXpaf
+#  define extra_field               XXef
+#  define explode_lit8              XXel8
+#  define explode_lit4              XXel4
+#  define explode_nolit8            XXnl8
+#  define explode_nolit4            XXnl4
+#  define cpdist8                   XXcpdist8
+#  define inflate_codes             XXic
+#  define inflate_stored            XXis
+#  define inflate_fixed             XXif
+#  define inflate_dynamic           XXid
+#  define inflate_block             XXib
+#  define maxcodemax                XXmax
+#endif
+
+#ifndef S_TIME_T_MAX            /* max value of signed (>= 32-bit) time_t */
+#  define S_TIME_T_MAX  ((time_t)(ulg)0x7fffffffL)
+#endif
+#ifndef U_TIME_T_MAX            /* max value of unsigned (>= 32-bit) time_t */
+#  define U_TIME_T_MAX  ((time_t)(ulg)0xffffffffL)
+#endif
+#ifdef DOSTIME_MINIMUM          /* min DOSTIME value (1980-01-01) */
+#  undef DOSTIME_MINIMUM
+#endif
+#define DOSTIME_MINIMUM ((ulg)0x00210000L)
+#ifdef DOSTIME_2038_01_18       /* approximate DOSTIME equivalent of */
+#  undef DOSTIME_2038_01_18     /*  the signed-32-bit time_t limit */
+#endif
+#define DOSTIME_2038_01_18 ((ulg)0x74320000L)
+
+#ifdef QDOS
+#  define ZSUFX         "_zip"
+#  define ALT_ZSUFX     ".zip"
+#else
+#  ifdef RISCOS
+#    define ZSUFX       "/zip"
+#  else
+#    define ZSUFX       ".zip"
+#  endif
+#  define ALT_ZSUFX     ".ZIP"   /* Unix-only so far (only case-sensitive fs) */
+#endif
+
+#define CENTRAL_HDR_SIG   "\001\002"   /* the infamous "PK" signature bytes, */
+#define LOCAL_HDR_SIG     "\003\004"   /*  w/o "PK" (so unzip executable not */
+#define END_CENTRAL_SIG   "\005\006"   /*  mistaken for zipfile itself) */
+#define EXTD_LOCAL_SIG    "\007\010"   /* [ASCII "\113" == EBCDIC "\080" ??] */
+
+/** internal-only return codes **/
+#define IZ_DIR            76   /* potential zipfile is a directory */
+/* special return codes for mapname() */
+#define MPN_OK            0      /* mapname successful */
+#define MPN_INF_TRUNC    (1<<8)  /* caution - filename truncated */
+#define MPN_INF_SKIP     (2<<8)  /* info  - skipped because nothing to do */
+#define MPN_ERR_SKIP     (3<<8)  /* error - entry skipped */
+#define MPN_ERR_TOOLONG  (4<<8)  /* error - path too long */
+#define MPN_NOMEM        (10<<8) /* error - out of memory, file skipped */
+#define MPN_CREATED_DIR  (16<<8) /* directory created: set time & permission */
+#define MPN_VOL_LABEL    (17<<8) /* volume label, but can't set on hard disk */
+#define MPN_INVALID      (99<<8) /* internal logic error, should never reach */
+/* mask for internal mapname&checkdir return codes */
+#define MPN_MASK          0x7F00
+/* error code for extracting/testing extra field blocks */
+#define IZ_EF_TRUNC       79   /* local extra field truncated (PKZIP'd) */
+
+/* choice of activities for do_string() */
+#define SKIP              0             /* skip header block */
+#define DISPLAY           1             /* display archive comment (ASCII) */
+#define DISPL_8           5             /* display file comment (ext. ASCII) */
+#define DS_FN             2             /* read filename (ext. ASCII, chead) */
+#define DS_FN_C           2             /* read filename from central header */
+#define DS_FN_L           6             /* read filename from local header */
+#define EXTRA_FIELD       3             /* copy extra field into buffer */
+#define DS_EF             3
+#ifdef AMIGA
+#  define FILENOTE        4             /* convert file comment to filenote */
+#endif
+#if (defined(SFX) && defined(CHEAP_SFX_AUTORUN))
+#  define CHECK_AUTORUN   7             /* copy command, display remainder */
+#  define CHECK_AUTORUN_Q 8             /* copy command, skip remainder */
+#endif
+
+#define DOES_NOT_EXIST    -1   /* return values for check_for_newer() */
+#define EXISTS_AND_OLDER  0
+#define EXISTS_AND_NEWER  1
+
+#define OVERWRT_QUERY     0    /* status values for G.overwrite_mode */
+#define OVERWRT_ALWAYS    1
+#define OVERWRT_NEVER     2
+
+#define IS_OVERWRT_ALL    (G.overwrite_mode == OVERWRT_ALWAYS)
+#define IS_OVERWRT_NONE   (G.overwrite_mode == OVERWRT_NEVER)
+
+#ifdef VMS
+  /* return codes for VMS-specific open_outfile() function */
+# define OPENOUT_OK       0   /* file openend normally */
+# define OPENOUT_FAILED   1   /* file open failed */
+# define OPENOUT_SKIPOK   2   /* file not opened, skip at error level OK */
+# define OPENOUT_SKIPWARN 3   /* file not opened, skip at error level WARN */
+#endif /* VMS */
+
+#define ROOT              0    /* checkdir() extract-to path:  called once */
+#define INIT              1    /* allocate buildpath:  called once per member */
+#define APPEND_DIR        2    /* append a dir comp.:  many times per member */
+#define APPEND_NAME       3    /* append actual filename:  once per member */
+#define GETPATH           4    /* retrieve the complete path and free it */
+#define END               5    /* free root path prior to exiting program */
+
+/* version_made_by codes (central dir):  make sure these */
+/*  are not defined on their respective systems!! */
+#define FS_FAT_           0    /* filesystem used by MS-DOS, OS/2, Win32 */
+#define AMIGA_            1
+#define VMS_              2
+#define UNIX_             3
+#define VM_CMS_           4
+#define ATARI_            5    /* what if it's a minix filesystem? [cjh] */
+#define FS_HPFS_          6    /* filesystem used by OS/2 (and NT 3.x) */
+#define MAC_              7    /* HFS filesystem used by MacOS */
+#define Z_SYSTEM_         8
+#define CPM_              9
+#define TOPS20_           10
+#define FS_NTFS_          11   /* filesystem used by Windows NT */
+#define QDOS_             12
+#define ACORN_            13   /* Archimedes Acorn RISC OS */
+#define FS_VFAT_          14   /* filesystem used by Windows 95, NT */
+#define MVS_              15
+#define BEOS_             16   /* hybrid POSIX/database filesystem */
+#define TANDEM_           17   /* Tandem NSK */
+#define THEOS_            18   /* THEOS */
+#define MAC_OSX_          19   /* Mac OS/X (Darwin) */
+#define ATHEOS_           30   /* AtheOS */
+#define NUM_HOSTS         31   /* index of last system + 1 */
+/* don't forget to update zipinfo.c appropiately if NUM_HOSTS changes! */
+
+#define STORED            0    /* compression methods */
+#define SHRUNK            1
+#define REDUCED1          2
+#define REDUCED2          3
+#define REDUCED3          4
+#define REDUCED4          5
+#define IMPLODED          6
+#define TOKENIZED         7
+#define DEFLATED          8
+#define ENHDEFLATED       9
+#define DCLIMPLODED      10
+#define BZIPPED          12
+#define LZMAED           14
+#define IBMTERSED        18
+#define IBMLZ77ED        19
+#define WAVPACKED        97
+#define PPMDED           98
+#define NUM_METHODS      17     /* number of known method IDs */
+/* don't forget to update list.c (list_files()), extract.c and zipinfo.c
+ * appropriately if NUM_METHODS changes */
+
+/* (the PK-class error codes are public and have been moved into unzip.h) */
+
+#define DF_MDY            0    /* date format 10/26/91 (USA only) */
+#define DF_DMY            1    /* date format 26/10/91 (most of the world) */
+#define DF_YMD            2    /* date format 91/10/26 (a few countries) */
+
+/*---------------------------------------------------------------------------
+    Extra-field block ID values and offset info.
+  ---------------------------------------------------------------------------*/
+/* extra-field ID values, all little-endian: */
+#define EF_PKSZ64    0x0001    /* PKWARE's 64-bit filesize extensions */
+#define EF_AV        0x0007    /* PKWARE's authenticity verification */
+#define EF_EFS       0x0008    /* PKWARE's extended language encoding */
+#define EF_OS2       0x0009    /* OS/2 extended attributes */
+#define EF_PKW32     0x000a    /* PKWARE's Win95/98/WinNT filetimes */
+#define EF_PKVMS     0x000c    /* PKWARE's VMS */
+#define EF_PKUNIX    0x000d    /* PKWARE's Unix */
+#define EF_PKFORK    0x000e    /* PKWARE's future stream/fork descriptors */
+#define EF_PKPATCH   0x000f    /* PKWARE's patch descriptor */
+#define EF_PKPKCS7   0x0014    /* PKWARE's PKCS#7 store for X.509 Certs */
+#define EF_PKFX509   0x0015    /* PKWARE's file X.509 Cert&Signature ID */
+#define EF_PKCX509   0x0016    /* PKWARE's central dir X.509 Cert ID */
+#define EF_PKENCRHD  0x0017    /* PKWARE's Strong Encryption header */
+#define EF_PKRMCTL   0x0018    /* PKWARE's Record Management Controls*/
+#define EF_PKLSTCS7  0x0019    /* PKWARE's PKCS#7 Encr. Recipient Cert List */
+#define EF_PKIBM     0x0065    /* PKWARE's IBM S/390 & AS/400 attributes */
+#define EF_PKIBM2    0x0066    /* PKWARE's IBM S/390 & AS/400 compr. attribs */
+#define EF_IZVMS     0x4d49    /* Info-ZIP's VMS ("IM") */
+#define EF_IZUNIX    0x5855    /* Info-ZIP's first Unix[1] ("UX") */
+#define EF_IZUNIX2   0x7855    /* Info-ZIP's second Unix[2] ("Ux") */
+#define EF_IZUNIX3   0x7875    /* Info-ZIP's newest Unix[3] ("ux") */
+#define EF_TIME      0x5455    /* universal timestamp ("UT") */
+#define EF_UNIPATH   0x7075    /* Info-ZIP Unicode Path ("up") */
+#define EF_UNICOMNT  0x6375    /* Info-ZIP Unicode Comment ("uc") */
+#define EF_MAC3      0x334d    /* Info-ZIP's new Macintosh (= "M3") */
+#define EF_JLMAC     0x07c8    /* Johnny Lee's old Macintosh (= 1992) */
+#define EF_ZIPIT     0x2605    /* Thomas Brown's Macintosh (ZipIt) */
+#define EF_ZIPIT2    0x2705    /* T. Brown's Mac (ZipIt) v 1.3.8 and newer ? */
+#define EF_SMARTZIP  0x4d63    /* Mac SmartZip by Marco Bambini */
+#define EF_VMCMS     0x4704    /* Info-ZIP's VM/CMS ("\004G") */
+#define EF_MVS       0x470f    /* Info-ZIP's MVS ("\017G") */
+#define EF_ACL       0x4c41    /* (OS/2) access control list ("AL") */
+#define EF_NTSD      0x4453    /* NT security descriptor ("SD") */
+#define EF_ATHEOS    0x7441    /* AtheOS ("At") */
+#define EF_BEOS      0x6542    /* BeOS ("Be") */
+#define EF_QDOS      0xfb4a    /* SMS/QDOS ("J\373") */
+#define EF_AOSVS     0x5356    /* AOS/VS ("VS") */
+#define EF_SPARK     0x4341    /* David Pilling's Acorn/SparkFS ("AC") */
+#define EF_TANDEM    0x4154    /* Tandem NSK ("TA") */
+#define EF_THEOS     0x6854    /* Jean-Michel Dubois' Theos "Th" */
+#define EF_THEOSO    0x4854    /* old Theos port */
+#define EF_MD5       0x4b46    /* Fred Kantor's MD5 ("FK") */
+#define EF_ASIUNIX   0x756e    /* ASi's Unix ("nu") */
+
+#define EB_HEADSIZE       4    /* length of extra field block header */
+#define EB_ID             0    /* offset of block ID in header */
+#define EB_LEN            2    /* offset of data length field in header */
+#define EB_UCSIZE_P       0    /* offset of ucsize field in compr. data */
+#define EB_CMPRHEADLEN    6    /* lenght of compression header */
+
+#define EB_UX_MINLEN      8    /* minimal "UX" field contains atime, mtime */
+#define EB_UX_FULLSIZE    12   /* full "UX" field (atime, mtime, uid, gid) */
+#define EB_UX_ATIME       0    /* offset of atime in "UX" extra field data */
+#define EB_UX_MTIME       4    /* offset of mtime in "UX" extra field data */
+#define EB_UX_UID         8    /* byte offset of UID in "UX" field data */
+#define EB_UX_GID         10   /* byte offset of GID in "UX" field data */
+
+#define EB_UX2_MINLEN     4    /* minimal "Ux" field contains UID/GID */
+#define EB_UX2_UID        0    /* byte offset of UID in "Ux" field data */
+#define EB_UX2_GID        2    /* byte offset of GID in "Ux" field data */
+#define EB_UX2_VALID      (1 << 8)      /* UID/GID present */
+
+#define EB_UX3_MINLEN     7    /* minimal "ux" field size (2-byte UID/GID) */
+
+#define EB_UT_MINLEN      1    /* minimal UT field contains Flags byte */
+#define EB_UT_FLAGS       0    /* byte offset of Flags field */
+#define EB_UT_TIME1       1    /* byte offset of 1st time value */
+#define EB_UT_FL_MTIME    (1 << 0)      /* mtime present */
+#define EB_UT_FL_ATIME    (1 << 1)      /* atime present */
+#define EB_UT_FL_CTIME    (1 << 2)      /* ctime present */
+
+#define EB_FLGS_OFFS      4    /* offset of flags area in generic compressed
+                                  extra field blocks (BEOS, MAC, and others) */
+#define EB_OS2_HLEN       4    /* size of OS2/ACL compressed data header */
+#define EB_BEOS_HLEN      5    /* length of BeOS&AtheOS e.f attribute header */
+#define EB_BE_FL_UNCMPR   0x01 /* "BeOS&AtheOS attribs uncompr." bit flag */
+#define EB_MAC3_HLEN      14   /* length of Mac3 attribute block header */
+#define EB_SMARTZIP_HLEN  64   /* fixed length of the SmartZip extra field */
+#define EB_M3_FL_DATFRK   0x01 /* "this entry is data fork" flag */
+#define EB_M3_FL_UNCMPR   0x04 /* "Mac3 attributes uncompressed" bit flag */
+#define EB_M3_FL_TIME64   0x08 /* "Mac3 time fields are 64 bit wide" flag */
+#define EB_M3_FL_NOUTC    0x10 /* "Mac3 timezone offset fields missing" flag */
+
+#define EB_NTSD_C_LEN     4    /* length of central NT security data */
+#define EB_NTSD_L_LEN     5    /* length of minimal local NT security data */
+#define EB_NTSD_VERSION   4    /* offset of NTSD version byte */
+#define EB_NTSD_MAX_VER   (0)  /* maximum version # we know how to handle */
+
+#define EB_PKVMS_MINLEN   4    /* minimum data length of PKVMS extra block */
+
+#define EB_ASI_CRC32      0    /* offset of ASI Unix field's crc32 checksum */
+#define EB_ASI_MODE       4    /* offset of ASI Unix permission mode field */
+
+#define EB_IZVMS_HLEN     12   /* length of IZVMS attribute block header */
+#define EB_IZVMS_FLGS     4    /* offset of compression type flag */
+#define EB_IZVMS_UCSIZ    6    /* offset of ucsize field in IZVMS header */
+#define EB_IZVMS_BCMASK   07   /* 3 bits for compression type */
+#define EB_IZVMS_BCSTOR   0    /*  Stored */
+#define EB_IZVMS_BC00     1    /*  0byte -> 0bit compression */
+#define EB_IZVMS_BCDEFL   2    /*  Deflated */
+
+
+/*---------------------------------------------------------------------------
+    True sizes of the various headers (excluding their 4-byte signatures),
+    as defined by PKWARE--so it is not likely that these will ever change.
+    But if they do, make sure both these defines AND the typedefs below get
+    updated accordingly.
+
+    12/27/2006
+    The Zip64 End Of Central Directory record is variable size and now
+    comes in two flavors, version 1 and the new version 2 that supports
+    central directory encryption.  We only use the old fields at the
+    top of the Zip64 EOCDR, and this block is a fixed size still, but
+    need to be aware of the stuff following.
+  ---------------------------------------------------------------------------*/
+#define LREC_SIZE    26   /* lengths of local file headers, central */
+#define CREC_SIZE    42   /*  directory headers, end-of-central-dir */
+#define ECREC_SIZE   18   /*  record, zip64 end-of-cent-dir locator */
+#define ECLOC64_SIZE 16   /*  and zip64 end-of-central-dir record,  */
+#define ECREC64_SIZE 52   /*  respectively                          */
+
+#define MAX_BITS    13                 /* used in unshrink() */
+#define HSIZE       (1 << MAX_BITS)    /* size of global work area */
+
+#define LF     10        /* '\n' on ASCII machines; must be 10 due to EBCDIC */
+#define CR     13        /* '\r' on ASCII machines; must be 13 due to EBCDIC */
+#define CTRLZ  26        /* DOS & OS/2 EOF marker (used in fileio.c, vms.c) */
+
+#ifdef EBCDIC
+#  define foreign(c)    ascii[(uch)(c)]
+#  define native(c)     ebcdic[(uch)(c)]
+#  define NATIVE        "EBCDIC"
+#  define NOANSIFILT
+#endif
+
+#ifdef VMS
+#  define ENV_UNZIP       "UNZIP_OPTS"     /* names of environment variables */
+#  define ENV_ZIPINFO     "ZIPINFO_OPTS"
+#endif /* VMS */
+#ifdef RISCOS
+#  define ENV_UNZIP       "Unzip$Options"
+#  define ENV_ZIPINFO     "Zipinfo$Options"
+#  define ENV_UNZIPEXTS   "Unzip$Exts"
+#endif /* RISCOS */
+#ifndef ENV_UNZIP
+#  define ENV_UNZIP       "UNZIP"          /* the standard names */
+#  define ENV_ZIPINFO     "ZIPINFO"
+#endif
+#define ENV_UNZIP2        "UNZIPOPT"     /* alternate names, for zip compat. */
+#define ENV_ZIPINFO2      "ZIPINFOOPT"
+
+#if (!defined(QQ) && !defined(NOQQ))
+#  define QQ
+#endif
+
+#ifdef QQ                         /* Newtware version:  no file */
+#  define QCOND     (!uO.qflag)   /*  comments with -vq or -vqq */
+#else                             /* Bill Davidsen version:  no way to */
+#  define QCOND     (longhdr)     /*  kill file comments when listing */
+#endif
+
+#ifdef OLD_QQ
+#  define QCOND2    (uO.qflag < 2)
+#else
+#  define QCOND2    (!uO.qflag)
+#endif
+
+#ifdef WILD_STOP_AT_DIR
+#  define __WDLPRO  , int sepc
+#  define __WDL     , sepc
+#  define __WDLDEF  int sepc;
+#  define WISEP     , (uO.W_flag ? '/' : '\0')
+#else
+#  define __WDLPRO
+#  define __WDL
+#  define __WDLDEF
+#  define WISEP
+#endif
+
+
+
+
+/**************/
+/*  Typedefs  */
+/**************/
+
+#ifdef ZIP64_SUPPORT
+# ifndef Z_UINT8_DEFINED
+#   if (defined(__GNUC__) || defined(__hpux) || defined(__SUNPRO_C))
+  typedef unsigned long long    z_uint8;
+#   else
+  typedef unsigned __int64      z_uint8;
+#   endif
+#   define Z_UINT8_DEFINED
+# endif
+#endif
+#ifndef Z_UINT4_DEFINED
+# if (defined(MODERN) && !defined(NO_LIMITS_H))
+#  if (defined(UINT_MAX) && (UINT_MAX == 0xffffffffUL))
+     typedef unsigned int       z_uint4;
+#    define Z_UINT4_DEFINED
+#  else
+#  if (defined(ULONG_MAX) && (ULONG_MAX == 0xffffffffUL))
+     typedef unsigned long      z_uint4;
+#    define Z_UINT4_DEFINED
+#  else
+#  if (defined(USHRT_MAX) && (USHRT_MAX == 0xffffffffUL))
+     typedef unsigned short     z_uint4;
+#    define Z_UINT4_DEFINED
+#  endif
+#  endif
+#  endif
+# endif /* MODERN && !NO_LIMITS_H */
+#endif /* !Z_UINT4_DEFINED */
+#ifndef Z_UINT4_DEFINED
+  typedef ulg                   z_uint4;
+# define Z_UINT4_DEFINED
+#endif
+
+/* The following three user-defined unsigned integer types are used for
+   holding zipfile entities (required widths without / with Zip64 support):
+   a) sizes and offset of zipfile entries
+      (4 bytes / 8 bytes)
+   b) enumeration and counts of zipfile entries
+      (2 bytes / 8 bytes)
+      Remark: internally, we use 4 bytes for archive member counting in the
+              No-Zip64 case, because UnZip supports more than 64k entries for
+              classic Zip archives without Zip64 extensions.
+   c) enumeration and counts of zipfile volumes of multivolume archives
+      (2 bytes / 4 bytes)
+ */
+#ifdef ZIP64_SUPPORT
+  typedef  z_uint8              zusz_t;     /* zipentry sizes & offsets */
+  typedef  z_uint8              zucn_t;     /* archive entry counts */
+  typedef  z_uint4              zuvl_t;     /* multivolume numbers */
+# define MASK_ZUCN64            (~(zucn_t)0)
+/* In case we ever get to support an environment where z_uint8 may be WIDER
+   than 64 bit wide, we will have to apply a construct similar to
+     #define MASK_ZUCN64        (~(zucn_t)0 & (zucn_t)0xffffffffffffffffULL)
+   for the 64-bit mask.
+ */
+#else
+  typedef  ulg                  zusz_t;     /* zipentry sizes & offsets */
+  typedef  unsigned int         zucn_t;     /* archive entry counts */
+  typedef  unsigned short       zuvl_t;     /* multivolume numbers */
+# define MASK_ZUCN64            (~(zucn_t)0)
+#endif
+#define MASK_ZUCN16             ((zucn_t)0xFFFF)
+
+#ifdef NO_UID_GID
+#  ifdef UID_USHORT
+     typedef unsigned short  uid_t;    /* TI SysV.3 */
+     typedef unsigned short  gid_t;
+#  else
+     typedef unsigned int    uid_t;    /* SCO Xenix */
+     typedef unsigned int    gid_t;
+#  endif
+#endif
+
+#if (defined(GOT_UTIMBUF) || defined(sgi) || defined(ATARI))
+   typedef struct utimbuf ztimbuf;
+#else
+   typedef struct ztimbuf {
+       time_t actime;        /* new access time */
+       time_t modtime;       /* new modification time */
+   } ztimbuf;
+#endif
+
+typedef struct iztimes {
+   time_t atime;             /* new access time */
+   time_t mtime;             /* new modification time */
+   time_t ctime;             /* used for creation time; NOT same as st_ctime */
+} iztimes;
+
+#ifdef SET_DIR_ATTRIB
+   typedef struct direntry {    /* head of system-specific struct holding */
+       struct direntry *next;   /*  defered directory attributes info */
+       char *fn;                /* filename of directory */
+       char buf[1];             /* start of system-specific internal data */
+   } direntry;
+#endif /* SET_DIR_ATTRIB */
+
+#ifdef SYMLINKS
+   typedef struct slinkentry {  /* info for deferred symlink creation */
+       struct slinkentry *next; /* pointer to next entry in chain */
+       extent targetlen;        /* length of target filespec */
+       extent attriblen;        /* length of system-specific attrib data */
+       char *target;            /* pointer to target filespec */
+       char *fname;             /* pointer to name of link */
+       char buf[1];             /* data/name/link buffer */
+   } slinkentry;
+#endif /* SYMLINKS */
+
+typedef struct min_info {
+    zoff_t offset;
+    zusz_t compr_size;       /* compressed size (needed if extended header) */
+    zusz_t uncompr_size;     /* uncompressed size (needed if extended header) */
+    ulg crc;                 /* crc (needed if extended header) */
+    zuvl_t diskstart;        /* no of volume where this entry starts */
+    uch hostver;
+    uch hostnum;
+    unsigned file_attr;      /* local flavor, as used by creat(), chmod()... */
+    unsigned encrypted : 1;  /* file encrypted: decrypt before uncompressing */
+    unsigned ExtLocHdr : 1;  /* use time instead of CRC for decrypt check */
+    unsigned textfile : 1;   /* file is text (according to zip) */
+    unsigned textmode : 1;   /* file is to be extracted as text */
+    unsigned lcflag : 1;     /* convert filename to lowercase */
+    unsigned vollabel : 1;   /* "file" is an MS-DOS volume (disk) label */
+#ifdef SYMLINKS
+    unsigned symlink : 1;    /* file is a symbolic link */
+#endif
+    unsigned HasUxAtt : 1;   /* crec ext_file_attr has Unix style mode bits */
+#ifdef UNICODE_SUPPORT
+    unsigned GPFIsUTF8: 1;   /* crec gen_purpose_flag UTF-8 bit 11 is set */
+#endif
+#ifndef SFX
+    char Far *cfilname;      /* central header version of filename */
+#endif
+} min_info;
+
+typedef struct VMStimbuf {
+    char *revdate;    /* (both roughly correspond to Unix modtime/st_mtime) */
+    char *credate;
+} VMStimbuf;
+
+/*---------------------------------------------------------------------------
+    Zipfile work area declarations.
+  ---------------------------------------------------------------------------*/
+
+#ifdef MALLOC_WORK
+   union work {
+     struct {                 /* unshrink(): */
+       shrint *Parent;          /* pointer to (8192 * sizeof(shrint)) */
+       uch *value;              /* pointer to 8KB char buffer */
+       uch *Stack;              /* pointer to another 8KB char buffer */
+     } shrink;
+     uch *Slide;              /* explode(), inflate(), unreduce() */
+   };
+#else /* !MALLOC_WORK */
+   union work {
+     struct {                 /* unshrink(): */
+       shrint Parent[HSIZE];    /* (8192 * sizeof(shrint)) == 16KB minimum */
+       uch value[HSIZE];        /* 8KB */
+       uch Stack[HSIZE];        /* 8KB */
+     } shrink;                  /* total = 32KB minimum; 80KB on Cray/Alpha */
+     uch Slide[WSIZE];        /* explode(), inflate(), unreduce() */
+   };
+#endif /* ?MALLOC_WORK */
+
+#define slide  G.area.Slide
+
+#if (defined(DLL) && !defined(NO_SLIDE_REDIR))
+#  define redirSlide G.redirect_sldptr
+#else
+#  define redirSlide G.area.Slide
+#endif
+
+/*---------------------------------------------------------------------------
+    Zipfile layout declarations.  If these headers ever change, make sure the
+    xxREC_SIZE defines (above) change with them!
+  ---------------------------------------------------------------------------*/
+
+   typedef uch   local_byte_hdr[ LREC_SIZE ];
+#      define L_VERSION_NEEDED_TO_EXTRACT_0     0
+#      define L_VERSION_NEEDED_TO_EXTRACT_1     1
+#      define L_GENERAL_PURPOSE_BIT_FLAG        2
+#      define L_COMPRESSION_METHOD              4
+#      define L_LAST_MOD_DOS_DATETIME           6
+#      define L_CRC32                           10
+#      define L_COMPRESSED_SIZE                 14
+#      define L_UNCOMPRESSED_SIZE               18
+#      define L_FILENAME_LENGTH                 22
+#      define L_EXTRA_FIELD_LENGTH              24
+
+   typedef uch   cdir_byte_hdr[ CREC_SIZE ];
+#      define C_VERSION_MADE_BY_0               0
+#      define C_VERSION_MADE_BY_1               1
+#      define C_VERSION_NEEDED_TO_EXTRACT_0     2
+#      define C_VERSION_NEEDED_TO_EXTRACT_1     3
+#      define C_GENERAL_PURPOSE_BIT_FLAG        4
+#      define C_COMPRESSION_METHOD              6
+#      define C_LAST_MOD_DOS_DATETIME           8
+#      define C_CRC32                           12
+#      define C_COMPRESSED_SIZE                 16
+#      define C_UNCOMPRESSED_SIZE               20
+#      define C_FILENAME_LENGTH                 24
+#      define C_EXTRA_FIELD_LENGTH              26
+#      define C_FILE_COMMENT_LENGTH             28
+#      define C_DISK_NUMBER_START               30
+#      define C_INTERNAL_FILE_ATTRIBUTES        32
+#      define C_EXTERNAL_FILE_ATTRIBUTES        34
+#      define C_RELATIVE_OFFSET_LOCAL_HEADER    38
+
+   typedef uch   ec_byte_rec[ ECREC_SIZE+4 ];
+/*     define SIGNATURE                         0   space-holder only */
+#      define NUMBER_THIS_DISK                  4
+#      define NUM_DISK_WITH_START_CEN_DIR       6
+#      define NUM_ENTRIES_CEN_DIR_THS_DISK      8
+#      define TOTAL_ENTRIES_CENTRAL_DIR         10
+#      define SIZE_CENTRAL_DIRECTORY            12
+#      define OFFSET_START_CENTRAL_DIRECTORY    16
+#      define ZIPFILE_COMMENT_LENGTH            20
+
+   typedef uch   ec_byte_loc64[ ECLOC64_SIZE+4 ];
+#      define NUM_DISK_START_EOCDR64            4
+#      define OFFSET_START_EOCDR64              8
+#      define NUM_THIS_DISK_LOC64               16
+
+   typedef uch   ec_byte_rec64[ ECREC64_SIZE+4 ];
+#      define ECREC64_LENGTH                    4
+#      define EC_VERSION_MADE_BY_0              12
+#      define EC_VERSION_NEEDED_0               14
+#      define NUMBER_THIS_DSK_REC64             16
+#      define NUM_DISK_START_CEN_DIR64          20
+#      define NUM_ENTRIES_CEN_DIR_THS_DISK64    24
+#      define TOTAL_ENTRIES_CENTRAL_DIR64       32
+#      define SIZE_CENTRAL_DIRECTORY64          40
+#      define OFFSET_START_CENTRAL_DIRECT64     48
+
+
+/* The following structs are used to hold all header data of a zip entry.
+   Traditionally, the structs' layouts followed the data layout of the
+   corresponding zipfile header structures.  However, the zipfile header
+   layouts were designed in the old ages of 16-bit CPUs, they are subject
+   to structure padding and/or alignment issues on newer systems with a
+   "natural word width" of more than 2 bytes.
+   Please note that the structure members are now reordered by size
+   (top-down), to prevent internal padding and optimize memory usage!
+ */
+   typedef struct local_file_header {                 /* LOCAL */
+       zusz_t csize;
+       zusz_t ucsize;
+       ulg last_mod_dos_datetime;
+       ulg crc32;
+       uch version_needed_to_extract[2];
+       ush general_purpose_bit_flag;
+       ush compression_method;
+       ush filename_length;
+       ush extra_field_length;
+   } local_file_hdr;
+
+   typedef struct central_directory_file_header {     /* CENTRAL */
+       zusz_t csize;
+       zusz_t ucsize;
+       zusz_t relative_offset_local_header;
+       ulg last_mod_dos_datetime;
+       ulg crc32;
+       ulg external_file_attributes;
+       zuvl_t disk_number_start;
+       ush internal_file_attributes;
+       uch version_made_by[2];
+       uch version_needed_to_extract[2];
+       ush general_purpose_bit_flag;
+       ush compression_method;
+       ush filename_length;
+       ush extra_field_length;
+       ush file_comment_length;
+   } cdir_file_hdr;
+
+   typedef struct end_central_dir_record {            /* END CENTRAL */
+       zusz_t size_central_directory;
+       zusz_t offset_start_central_directory;
+       zucn_t num_entries_centrl_dir_ths_disk;
+       zucn_t total_entries_central_dir;
+       zuvl_t number_this_disk;
+       zuvl_t num_disk_start_cdir;
+       int have_ecr64;                  /* valid Zip64 ecdir-record exists */
+       int is_zip64_archive;            /* Zip64 ecdir-record is mandatory */
+       ush zipfile_comment_length;
+   } ecdir_rec;
+
+
+/* Huffman code lookup table entry--this entry is four bytes for machines
+   that have 16-bit pointers (e.g. PC's in the small or medium model).
+   Valid extra bits are 0..16.  e == 31 is EOB (end of block), e == 32
+   means that v is a literal, 32 < e < 64 means that v is a pointer to
+   the next table, which codes (e & 31)  bits, and lastly e == 99 indicates
+   an unused code.  If a code with e == 99 is looked up, this implies an
+   error in the data. */
+
+struct huft {
+    uch e;                /* number of extra bits or operation */
+    uch b;                /* number of bits in this code or subcode */
+    union {
+        ush n;            /* literal, length base, or distance base */
+        struct huft *t;   /* pointer to next level of table */
+    } v;
+};
+
+
+typedef struct _APIDocStruct {
+    char *compare;
+    char *function;
+    char *syntax;
+    char *purpose;
+} APIDocStruct;
+
+
+
+
+/*************/
+/*  Globals  */
+/*************/
+
+#if (defined(OS2) && !defined(FUNZIP))
+#  include "os2/os2data.h"
+#endif
+
+#include "globals.h"
+
+
+
+/*************************/
+/*  Function Prototypes  */
+/*************************/
+
+/*---------------------------------------------------------------------------
+    Functions in unzip.c (initialization routines):
+  ---------------------------------------------------------------------------*/
+
+#ifndef WINDLL
+   int    MAIN                   OF((int argc, char **argv));
+   int    unzip                  OF((__GPRO__ int argc, char **argv));
+   int    uz_opts                OF((__GPRO__ int *pargc, char ***pargv));
+   int    usage                  OF((__GPRO__ int error));
+#endif /* !WINDLL */
+
+/*---------------------------------------------------------------------------
+    Functions in process.c (main driver routines):
+  ---------------------------------------------------------------------------*/
+
+int      process_zipfiles        OF((__GPRO));
+void     free_G_buffers          OF((__GPRO));
+/* static int    do_seekable     OF((__GPRO__ int lastchance)); */
+/* static int    find_ecrec      OF((__GPRO__ long searchlen)); */
+/* static int    process_central_comment OF((__GPRO)); */
+int      process_cdir_file_hdr   OF((__GPRO));
+int      process_local_file_hdr  OF((__GPRO));
+int      getZip64Data            OF((__GPRO__ ZCONST uch *ef_buf,
+                                     unsigned ef_len));
+#ifdef UNICODE_SUPPORT
+  int    getUnicodeData          OF((__GPRO__ ZCONST uch *ef_buf,
+                                     unsigned ef_len));
+#endif
+unsigned ef_scan_for_izux        OF((ZCONST uch *ef_buf, unsigned ef_len,
+                                     int ef_is_c, ulg dos_mdatetime,
+                                     iztimes *z_utim, ulg *z_uidgid));
+#if (defined(RISCOS) || defined(ACORN_FTYPE_NFS))
+   zvoid *getRISCOSexfield       OF((ZCONST uch *ef_buf, unsigned ef_len));
+#endif
+
+#ifndef SFX
+
+/*---------------------------------------------------------------------------
+    Functions in zipinfo.c (`zipinfo-style' listing routines):
+  ---------------------------------------------------------------------------*/
+
+#ifndef NO_ZIPINFO
+#ifndef WINDLL
+   int   zi_opts                 OF((__GPRO__ int *pargc, char ***pargv));
+#endif
+void     zi_end_central          OF((__GPRO));
+int      zipinfo                 OF((__GPRO));
+/* static int      zi_long       OF((__GPRO__ zusz_t *pEndprev)); */
+/* static int      zi_short      OF((__GPRO)); */
+/* static char    *zi_time       OF((__GPRO__ ZCONST ulg *datetimez,
+                                     ZCONST time_t *modtimez, char *d_t_str));*/
+#endif /* !NO_ZIPINFO */
+
+/*---------------------------------------------------------------------------
+    Functions in list.c (generic zipfile-listing routines):
+  ---------------------------------------------------------------------------*/
+
+int      list_files              OF((__GPRO));
+#ifdef TIMESTAMP
+   int   get_time_stamp          OF((__GPRO__  time_t *last_modtime,
+                                     ulg *nmember));
+#endif
+int      ratio                   OF((zusz_t uc, zusz_t c));
+void     fnprint                 OF((__GPRO));
+
+#endif /* !SFX */
+
+/*---------------------------------------------------------------------------
+    Functions in fileio.c:
+  ---------------------------------------------------------------------------*/
+
+int      open_input_file      OF((__GPRO));
+int      open_outfile         OF((__GPRO));                    /* also vms.c */
+void     undefer_input        OF((__GPRO));
+void     defer_leftover_input OF((__GPRO));
+unsigned readbuf              OF((__GPRO__ char *buf, register unsigned len));
+int      readbyte             OF((__GPRO));
+int      fillinbuf            OF((__GPRO));
+int      seek_zipf            OF((__GPRO__ zoff_t abs_offset));
+#ifdef FUNZIP
+   int   flush                OF((__GPRO__ ulg size));  /* actually funzip.c */
+#else
+   int   flush                OF((__GPRO__ uch *buf, ulg size, int unshrink));
+#endif
+/* static int  disk_error     OF((__GPRO)); */
+void     handler              OF((int signal));
+time_t   dos_to_unix_time     OF((ulg dos_datetime));
+int      check_for_newer      OF((__GPRO__ char *filename)); /* os2,vmcms,vms */
+int      do_string            OF((__GPRO__ unsigned int length, int option));
+ush      makeword             OF((ZCONST uch *b));
+ulg      makelong             OF((ZCONST uch *sig));
+zusz_t   makeint64            OF((ZCONST uch *sig));
+char    *fzofft               OF((__GPRO__ zoff_t val,
+                                  ZCONST char *pre, ZCONST char *post));
+#if (!defined(STR_TO_ISO) || defined(NEED_STR2ISO))
+   char *str2iso              OF((char *dst, ZCONST char *src));
+#endif
+#if (!defined(STR_TO_OEM) || defined(NEED_STR2OEM))
+   char *str2oem              OF((char *dst, ZCONST char *src));
+#endif
+#ifdef NO_STRNICMP
+   int   zstrnicmp            OF((register ZCONST char *s1,
+                                  register ZCONST char *s2,
+                                  register unsigned n));
+#endif
+#ifdef REGULUS
+   int   zstat                OF((ZCONST char *p, struct stat *s));
+#endif
+#ifdef ZMEM   /* MUST be ifdef'd because of conflicts with the standard def. */
+   zvoid *memset OF((register zvoid *, register int, register unsigned int));
+   int    memcmp OF((register ZCONST zvoid*, register ZCONST zvoid *,
+                     register unsigned int));
+   zvoid *memcpy OF((register zvoid *, register ZCONST zvoid *,
+                     register unsigned int));
+#endif
+#ifdef NEED_UZMBCLEN
+   extent uzmbclen          OF((ZCONST unsigned char *ptr));
+#endif
+#ifdef NEED_UZMBSCHR
+   unsigned char *uzmbschr  OF((ZCONST unsigned char *str, unsigned int c));
+#endif
+#ifdef NEED_UZMBSRCHR
+   unsigned char *uzmbsrchr OF((ZCONST unsigned char *str, unsigned int c));
+#endif
+#ifdef SMALL_MEM
+   char *fLoadFarString       OF((__GPRO__ const char Far *sz));
+   char *fLoadFarStringSmall  OF((__GPRO__ const char Far *sz));
+   char *fLoadFarStringSmall2 OF((__GPRO__ const char Far *sz));
+   #ifndef zfstrcpy
+     char Far * Far zfstrcpy  OF((char Far *s1, const char Far *s2));
+   #endif
+   #if (!defined(SFX) && !defined(zfstrcmp))
+     int Far zfstrcmp         OF((const char Far *s1, const char Far *s2));
+   #endif
+#endif
+
+
+/*---------------------------------------------------------------------------
+    Functions in extract.c:
+  ---------------------------------------------------------------------------*/
+
+int    extract_or_test_files     OF((__GPRO));
+/* static int   store_info          OF((void)); */
+/* static int   extract_or_test_member   OF((__GPRO)); */
+/* static int   TestExtraField   OF((__GPRO__ uch *ef, unsigned ef_len)); */
+/* static int   test_OS2         OF((__GPRO__ uch *eb, unsigned eb_size)); */
+/* static int   test_NT          OF((__GPRO__ uch *eb, unsigned eb_size)); */
+#ifndef SFX
+  unsigned find_compr_idx        OF((unsigned compr_methodnum));
+#endif
+int    memextract                OF((__GPRO__ uch *tgt, ulg tgtsize,
+                                     ZCONST uch *src, ulg srcsize));
+int    memflush                  OF((__GPRO__ ZCONST uch *rawbuf, ulg size));
+#if (defined(VMS) || defined(VMS_TEXT_CONV))
+   uch   *extract_izvms_block    OF((__GPRO__ ZCONST uch *ebdata,
+                                     unsigned size, unsigned *retlen,
+                                     ZCONST uch *init, unsigned needlen));
+#endif
+char  *fnfilter                  OF((ZCONST char *raw, uch *space,
+                                     extent size));
+
+/*---------------------------------------------------------------------------
+    Decompression functions:
+  ---------------------------------------------------------------------------*/
+
+#if (!defined(SFX) && !defined(FUNZIP))
+int    explode                   OF((__GPRO));                  /* explode.c */
+#endif
+int    huft_free                 OF((struct huft *t));          /* inflate.c */
+int    huft_build                OF((__GPRO__ ZCONST unsigned *b, unsigned n,
+                                     unsigned s, ZCONST ush *d, ZCONST uch *e,
+                                     struct huft **t, unsigned *m));
+#ifdef USE_ZLIB
+   int    UZinflate              OF((__GPRO__ int is_defl64));  /* inflate.c */
+#  define inflate_free(x)        inflateEnd(&((Uz_Globs *)(&G))->dstrm)
+#else
+   int    inflate                OF((__GPRO__ int is_defl64));  /* inflate.c */
+   int    inflate_free           OF((__GPRO));                  /* inflate.c */
+#endif /* ?USE_ZLIB */
+#if (!defined(SFX) && !defined(FUNZIP))
+#ifndef COPYRIGHT_CLEAN
+   int    unreduce               OF((__GPRO));                 /* unreduce.c */
+/* static void  LoadFollowers    OF((__GPRO__ f_array *follower, uch *Slen));
+                                                                * unreduce.c */
+#endif /* !COPYRIGHT_CLEAN */
+#ifndef LZW_CLEAN
+   int    unshrink               OF((__GPRO));                 /* unshrink.c */
+/* static void  partial_clear    OF((__GPRO));                  * unshrink.c */
+#endif /* !LZW_CLEAN */
+#endif /* !SFX && !FUNZIP */
+#ifdef USE_BZIP2
+   int    UZbunzip2              OF((__GPRO));                  /* extract.c */
+   void   bz_internal_error      OF((int bzerrcode));           /* ubz2err.c */
+#endif
+
+/*---------------------------------------------------------------------------
+    Internal API functions (only included in DLL versions):
+  ---------------------------------------------------------------------------*/
+
+#ifdef DLL
+   void     setFileNotFound       OF((__GPRO));                     /* api.c */
+   int      unzipToMemory         OF((__GPRO__ char *zip, char *file,
+                                      UzpBuffer *retstr));          /* api.c */
+   int      redirect_outfile      OF((__GPRO));                     /* api.c */
+   int      writeToMemory         OF((__GPRO__ ZCONST uch *rawbuf,
+                                      extent size));                /* api.c */
+   int      close_redirect        OF((__GPRO));                     /* api.c */
+   /* this obsolescent entry point kept for compatibility: */
+   int      UzpUnzip              OF((int argc, char **argv));/* use UzpMain */
+#ifdef OS2DLL
+   int      varmessage            OF((__GPRO__ ZCONST uch *buf, ulg size));
+   int      varputchar            OF((__GPRO__ int c));         /* rexxapi.c */
+   int      finish_REXX_redirect  OF((__GPRO));                 /* rexxapi.c */
+#endif
+#ifdef API_DOC
+   void     APIhelp               OF((__GPRO__ int argc, char **argv));
+#endif                                                          /* apihelp.c */
+#endif /* DLL */
+
+/*---------------------------------------------------------------------------
+    MSDOS-only functions:
+  ---------------------------------------------------------------------------*/
+
+#ifdef MSDOS
+#if (!defined(FUNZIP) && !defined(SFX) && !defined(WINDLL))
+   void     check_for_windows     OF((ZCONST char *app));         /* msdos.c */
+#endif
+#if (defined(__GO32__) || defined(__EMX__))
+   unsigned _dos_getcountryinfo(void *);                          /* msdos.c */
+#if (!defined(__DJGPP__) || (__DJGPP__ < 2))
+   unsigned _dos_setftime(int, unsigned, unsigned);               /* msdos.c */
+   unsigned _dos_setfileattr(const char *, unsigned);             /* msdos.c */
+   unsigned _dos_creat(const char *, unsigned, int *);            /* msdos.c */
+   void _dos_getdrive(unsigned *);                                /* msdos.c */
+   unsigned _dos_close(int);                                      /* msdos.c */
+#endif /* !__DJGPP__ || (__DJGPP__ < 2) */
+#endif /* __GO32__ || __EMX__ */
+#endif
+
+/*---------------------------------------------------------------------------
+    OS/2-only functions:
+  ---------------------------------------------------------------------------*/
+
+#ifdef OS2   /* GetFileTime conflicts with something in Win32 header files */
+#if (defined(REENTRANT) && defined(USETHREADID))
+   ulg   GetThreadId          OF((void));
+#endif
+   int   GetCountryInfo       OF((void));                           /* os2.c */
+   long  GetFileTime          OF((ZCONST char *name));              /* os2.c */
+/* static void  SetPathAttrTimes OF((__GPRO__ int flags, int dir));    os2.c */
+/* static int   SetEAs        OF((__GPRO__ const char *path,
+                                  void *eablock));                     os2.c */
+/* static int   SetACL        OF((__GPRO__ const char *path,
+                                  void *eablock));                     os2.c */
+/* static int   IsFileNameValid OF((const char *name));                os2.c */
+/* static void  map2fat       OF((char *pathcomp, char **pEndFAT));    os2.c */
+/* static int   SetLongNameEA OF((char *name, char *longname));        os2.c */
+/* static void  InitNLS       OF((void));                              os2.c */
+   int   IsUpperNLS           OF((int nChr));                       /* os2.c */
+   int   ToLowerNLS           OF((int nChr));                       /* os2.c */
+   void  DebugMalloc          OF((void));                           /* os2.c */
+#endif
+
+/*---------------------------------------------------------------------------
+    QDOS-only functions:
+  ---------------------------------------------------------------------------*/
+
+#ifdef QDOS
+   int    QMatch              (uch, uch);
+   void   QFilename           (__GPRO__ char *);
+   char  *Qstrfix             (char *);
+   int    QReturn             (int zip_error);
+#endif
+
+/*---------------------------------------------------------------------------
+    TOPS20-only functions:
+  ---------------------------------------------------------------------------*/
+
+#ifdef TOPS20
+   int    upper               OF((char *s));                     /* tops20.c */
+   int    enquote             OF((char *s));                     /* tops20.c */
+   int    dequote             OF((char *s));                     /* tops20.c */
+   int    fnlegal             OF(()); /* error if prototyped? */ /* tops20.c */
+#endif
+
+/*---------------------------------------------------------------------------
+    VM/CMS- and MVS-only functions:
+  ---------------------------------------------------------------------------*/
+
+#ifdef CMS_MVS
+   extent getVMMVSexfield     OF((char *type, uch *ef_block, unsigned datalen));
+   FILE  *vmmvs_open_infile   OF((__GPRO));                       /* vmmvs.c */
+   void   close_infile        OF((__GPRO));                       /* vmmvs.c */
+#endif
+
+/*---------------------------------------------------------------------------
+    VMS-only functions:
+  ---------------------------------------------------------------------------*/
+
+#ifdef VMS
+   int    check_format        OF((__GPRO));                         /* vms.c */
+/* int    open_outfile        OF((__GPRO));           * (see fileio.c) vms.c */
+/* int    flush               OF((__GPRO__ uch *rawbuf, unsigned size,
+                                  int final_flag));   * (see fileio.c) vms.c */
+   char  *vms_msg_text        OF((void));                           /* vms.c */
+#ifdef RETURN_CODES
+   void   return_VMS          OF((__GPRO__ int zip_error));         /* vms.c */
+#else
+   void   return_VMS          OF((int zip_error));                  /* vms.c */
+#endif
+#ifdef VMSCLI
+   ulg    vms_unzip_cmdline   OF((int *, char ***));            /* cmdline.c */
+   int    VMSCLI_usage        OF((__GPRO__ int error));         /* cmdline.c */
+#endif
+#endif
+
+/*---------------------------------------------------------------------------
+    WIN32-only functions:
+  ---------------------------------------------------------------------------*/
+
+#ifdef WIN32
+   int   IsWinNT        OF((void));                               /* win32.c */
+#ifdef NTSD_EAS
+   void  process_defer_NT     OF((__GPRO));                       /* win32.c */
+   int   test_NTSD      OF((__GPRO__ uch *eb, unsigned eb_size,
+                            uch *eb_ucptr, ulg eb_ucsize));       /* win32.c */
+#  define TEST_NTSD     test_NTSD
+#endif
+#ifdef W32_STAT_BANDAID
+   int   zstat_win32    OF((__W32STAT_GLOBALS__
+                            const char *path, z_stat *buf));      /* win32.c */
+#endif
+#endif
+
+/*---------------------------------------------------------------------------
+    Miscellaneous/shared functions:
+  ---------------------------------------------------------------------------*/
+
+Uz_Globs *globalsCtor    OF((void));                            /* globals.c */
+
+int      envargs         OF((int *Pargc, char ***Pargv,
+                             ZCONST char *envstr, ZCONST char *envstr2));
+                                                                /* envargs.c */
+void     mksargs         OF((int *argcp, char ***argvp));       /* envargs.c */
+
+int      match           OF((ZCONST char *s, ZCONST char *p,
+                             int ic __WDLPRO));                   /* match.c */
+int      iswild          OF((ZCONST char *p));                    /* match.c */
+
+/* declarations of public CRC-32 functions have been moved into crc32.h
+   (free_crc_table(), get_crc_table(), crc32())                      crc32.c */
+
+int      dateformat      OF((void));                                /* local */
+char     dateseparator   OF((void));                                /* local */
+#ifndef WINDLL
+   void  version         OF((__GPRO));                              /* local */
+#endif
+int      mapattr         OF((__GPRO));                              /* local */
+int      mapname         OF((__GPRO__ int renamed));                /* local */
+int      checkdir        OF((__GPRO__ char *pathcomp, int flag));   /* local */
+char    *do_wild         OF((__GPRO__ ZCONST char *wildzipfn));     /* local */
+char    *GetLoadPath     OF((__GPRO));                              /* local */
+#if (defined(MORE) && (defined(ATH_BEO_UNX) || defined(QDOS) || defined(VMS)))
+   int screensize        OF((int *tt_rows, int *tt_cols));          /* local */
+# if defined(VMS)
+   int screenlinewrap    OF((void));                                /* local */
+# endif
+#endif /* MORE && (ATH_BEO_UNX || QDOS || VMS) */
+#ifdef OS2_W32
+   int   SetFileSize     OF((FILE *file, zusz_t filesize));         /* local */
+#endif
+#ifndef MTS /* macro in MTS */
+   int  close_outfile   OF((__GPRO));                              /* local */
+#endif
+#ifdef SET_SYMLINK_ATTRIBS
+   int  set_symlnk_attribs  OF((__GPRO__ slinkentry *slnk_entry));  /* local */
+#endif
+#ifdef SET_DIR_ATTRIB
+   int   defer_dir_attribs  OF((__GPRO__ direntry **pd));           /* local */
+   int   set_direc_attribs  OF((__GPRO__ direntry *d));             /* local */
+#endif
+#ifdef TIMESTAMP
+# ifdef WIN32
+   int   stamp_file      OF((__GPRO__
+                             ZCONST char *fname, time_t modtime));  /* local */
+# else
+   int   stamp_file      OF((ZCONST char *fname, time_t modtime));  /* local */
+# endif
+#endif
+#ifdef NEED_ISO_OEM_INIT
+   void  prepare_ISO_OEM_translat   OF((__GPRO));                   /* local */
+#endif
+#if (defined(MALLOC_WORK) && defined(MY_ZCALLOC))
+   zvoid far *zcalloc    OF((unsigned int, unsigned int));
+   zvoid zcfree          OF((zvoid far *));
+#endif /* MALLOC_WORK && MY_ZCALLOC */
+#ifdef SYSTEM_SPECIFIC_CTOR
+   void  SYSTEM_SPECIFIC_CTOR   OF((__GPRO));                       /* local */
+#endif
+#ifdef SYSTEM_SPECIFIC_DTOR
+   void  SYSTEM_SPECIFIC_DTOR   OF((__GPRO));                       /* local */
+#endif
+
+
+
+
+
+/************/
+/*  Macros  */
+/************/
+
+#ifndef MAX
+#  define MAX(a,b)   ((a) > (b) ? (a) : (b))
+#endif
+#ifndef MIN
+#  define MIN(a,b)   ((a) < (b) ? (a) : (b))
+#endif
+
+#ifdef DEBUG
+#  if (defined(THEOS) && defined(NO_BOGUS_SPC))
+#    define NO_DEBUG_IN_MACROS
+#    define Trace(x)   _fprintf x
+#  else
+#    define Trace(x)   fprintf x
+#  endif
+#else
+#  define Trace(x)
+#endif
+
+#ifdef DEBUG_TIME
+#  define TTrace(x)  fprintf x
+#else
+#  define TTrace(x)
+#endif
+
+#ifdef NO_DEBUG_IN_MACROS
+#  define MTrace(x)
+#else
+#  define MTrace(x)  Trace(x)
+#endif
+
+#if (defined(UNIX) || defined(T20_VMS)) /* generally old systems */
+#  define ToLower(x)   ((char)(isupper((int)x)? tolower((int)x) : x))
+#else
+#  define ToLower      tolower          /* assumed "smart"; used in match() */
+#endif
+
+#ifdef USE_STRM_INPUT
+   /* ``Replace'' the unbuffered UNIX style I/O function with similar
+    * standard C functions from <stdio.h>.
+    */
+#  define read(fd,buf,n) fread((buf),1,(n),(FILE *)(fd))
+#  ifdef zlseek
+#    undef zlseek
+#  endif
+#  define zlseek(fd,o,w) zfseeko((FILE *)(fd),(o),(w))
+#  define close(fd) fclose((FILE *)(fd))
+#endif /* USE_STRM_INPUT */
+
+/* The return value of the Info() "macro function" is never checked in
+ * UnZip. Otherwise, to get the same behaviour as for (*G.message)(), the
+ * Info() definition for "FUNZIP" would have to be corrected:
+ * #define Info(buf,flag,sprf_arg) \
+ *      (fputs((char *)(sprintf sprf_arg, (buf)), \
+ *             (flag)&1? stderr : stdout) < 0)
+ */
+#ifndef Info   /* may already have been defined for redirection */
+#  ifdef FUNZIP
+#    define Info(buf,flag,sprf_arg) \
+     fputs((char *)(sprintf sprf_arg, (buf)), (flag)&1? stderr : stdout)
+#  else
+#    ifdef INT_SPRINTF  /* optimized version for "int sprintf()" flavour */
+#      define Info(buf,flag,sprf_arg) \
+       (*G.message)((zvoid *)&G, (uch *)(buf), (ulg)sprintf sprf_arg, (flag))
+#    else          /* generic version, does not use sprintf() return value */
+#      define Info(buf,flag,sprf_arg) \
+       (*G.message)((zvoid *)&G, (uch *)(buf), \
+                     (ulg)(sprintf sprf_arg, strlen((char *)(buf))), (flag))
+#    endif
+#  endif
+#endif /* !Info */
+
+/*  This wrapper macro around fzofft() is just defined to "hide" the
+ *  argument needed to reference the global storage buffers.
+ */
+#define FmZofft(val, pre, post) fzofft(__G__ val, pre, post)
+
+/*  The following macro wrappers around the fnfilter function are used many
+ *  times to prepare archive entry names or name components for displaying
+ *  listings and (warning/error) messages. They use sections in the upper half
+ *  of 'slide' as buffer, since their output is normally fed through the
+ *  Info() macro with 'slide' (the start of this area) as message buffer.
+ */
+#define FnFilter1(fname) \
+        fnfilter((fname), slide + (extent)(WSIZE>>1), (extent)(WSIZE>>2))
+#define FnFilter2(fname) \
+        fnfilter((fname), slide + (extent)((WSIZE>>1) + (WSIZE>>2)),\
+                 (extent)(WSIZE>>2))
+
+#ifndef FUNZIP   /* used only in inflate.c */
+#  define MESSAGE(str,len,flag)  (*G.message)((zvoid *)&G,(str),(len),(flag))
+#endif
+
+#if 0            /* Optimization: use the (const) result of crc32(0L,NULL,0) */
+#  define CRCVAL_INITIAL  crc32(0L, NULL, 0)
+#else
+#  define CRCVAL_INITIAL  0L
+#endif
+
+#ifdef SYMLINKS
+   /* This macro defines the Zip "made by" hosts that are considered
+      to support storing symbolic link entries. */
+#  define SYMLINK_HOST(hn) ((hn) == UNIX_ || (hn) == ATARI_ || \
+      (hn) == ATHEOS_ || (hn) == BEOS_ || (hn) == VMS_)
+#endif
+
+#ifndef TEST_NTSD               /* "NTSD valid?" checking function */
+#  define TEST_NTSD     NULL    /*   ... is not available */
+#endif
+
+#define SKIP_(length) if(length&&((error=do_string(__G__ length,SKIP))!=0))\
+  {error_in_archive=error; if(error>1) return error;}
+
+/*
+ *  Skip a variable-length field, and report any errors.  Used in zipinfo.c
+ *  and unzip.c in several functions.
+ *
+ *  macro SKIP_(length)
+ *      ush length;
+ *  {
+ *      if (length && ((error = do_string(length, SKIP)) != 0)) {
+ *          error_in_archive = error;   /-* might be warning *-/
+ *          if (error > 1)              /-* fatal *-/
+ *              return (error);
+ *      }
+ *  }
+ *
+ */
+
+
+#ifdef FUNZIP
+#  define FLUSH(w)  flush(__G__ (ulg)(w))
+#  define NEXTBYTE  getc(G.in)   /* redefined in crypt.h if full version */
+#else
+#  define FLUSH(w)  ((G.mem_mode) ? memflush(__G__ redirSlide,(ulg)(w)) \
+                                  : flush(__G__ redirSlide,(ulg)(w),0))
+#  define NEXTBYTE  (G.incnt-- > 0 ? (int)(*G.inptr++) : readbyte(__G))
+#endif
+
+
+#define READBITS(nbits,zdest) {if(nbits>G.bits_left) {int temp; G.zipeof=1;\
+  while (G.bits_left<=8*(int)(sizeof(G.bitbuf)-1) && (temp=NEXTBYTE)!=EOF) {\
+  G.bitbuf|=(ulg)temp<<G.bits_left; G.bits_left+=8; G.zipeof=0;}}\
+  zdest=(shrint)((unsigned)G.bitbuf&mask_bits[nbits]);G.bitbuf>>=nbits;\
+  G.bits_left-=nbits;}
+
+/*
+ * macro READBITS(nbits,zdest)    * only used by unreduce and unshrink *
+ *  {
+ *      if (nbits > G.bits_left) {  * fill G.bitbuf, 8*sizeof(ulg) bits *
+ *          int temp;
+ *
+ *          G.zipeof = 1;
+ *          while (G.bits_left <= 8*(int)(sizeof(G.bitbuf)-1) &&
+ *                 (temp = NEXTBYTE) != EOF) {
+ *              G.bitbuf |= (ulg)temp << G.bits_left;
+ *              G.bits_left += 8;
+ *              G.zipeof = 0;
+ *          }
+ *      }
+ *      zdest = (shrint)((unsigned)G.bitbuf & mask_bits[nbits]);
+ *      G.bitbuf >>= nbits;
+ *      G.bits_left -= nbits;
+ *  }
+ *
+ */
+
+
+/* GRR:  should use StringLower for STRLOWER macro if possible */
+
+/*
+ *  Copy the zero-terminated string in str1 into str2, converting any
+ *  uppercase letters to lowercase as we go.  str2 gets zero-terminated
+ *  as well, of course.  str1 and str2 may be the same character array.
+ */
+#ifdef _MBCS
+#  define STRLOWER(str1, str2) \
+   { \
+       char  *p, *q, c; unsigned i; \
+       p = (char *)(str1); \
+       q = (char *)(str2); \
+       while ((c = *p) != '\0') { \
+           if ((i = CLEN(p)) > 1) { \
+               while (i--) *q++ = *p++; \
+           } else { \
+               *q++ = (char)(isupper((int)(c))? tolower((int)(c)) : c); \
+               p++; \
+           } \
+       } \
+       *q = '\0'; \
+   }
+#else
+#  define STRLOWER(str1, str2) \
+   { \
+       char  *p, *q; \
+       p = (char *)(str1) - 1; \
+       q = (char *)(str2); \
+       while (*++p) \
+           *q++ = (char)(isupper((int)(*p))? tolower((int)(*p)) : *p); \
+       *q = '\0'; \
+   }
+#endif
+/*
+ *  NOTES:  This macro makes no assumptions about the characteristics of
+ *    the tolower() function or macro (beyond its existence), nor does it
+ *    make assumptions about the structure of the character set (i.e., it
+ *    should work on EBCDIC machines, too).  The fact that either or both
+ *    of isupper() and tolower() may be macros has been taken into account;
+ *    watch out for "side effects" (in the C sense) when modifying this
+ *    macro.
+ */
+
+#ifndef foreign
+#  define foreign(c)  (c)
+#endif
+
+#ifndef native
+#  define native(c)   (c)
+#  define A_TO_N(str1)
+#else
+#  ifndef NATIVE
+#    define NATIVE     "native chars"
+#  endif
+#  define A_TO_N(str1) {register uch *p;\
+     for (p=(uch *)(str1); *p; p++) *p=native(*p);}
+#endif
+/*
+ *  Translate the zero-terminated string in str1 from ASCII to the native
+ *  character set. The translation is performed in-place and uses the
+ *  "native" macro to translate each character.
+ *
+ *  NOTE:  Using the "native" macro means that is it the only part of unzip
+ *    which knows which translation table (if any) is actually in use to
+ *    produce the native character set.  This makes adding new character set
+ *    translation tables easy, insofar as all that is needed is an appropriate
+ *    "native" macro definition and the translation table itself.  Currently,
+ *    the only non-ASCII native character set implemented is EBCDIC, but this
+ *    may not always be so.
+ */
+
+
+/* default setup for internal codepage: assume ISO 8859-1 compatibility!! */
+#if (!defined(NATIVE) && !defined(CRTL_CP_IS_ISO) && !defined(CRTL_CP_IS_OEM))
+#  define CRTL_CP_IS_ISO
+#endif
+
+
+/*  Translate "extended ASCII" chars (OEM coding for DOS and OS/2; else
+ *  ISO-8859-1 [ISO Latin 1, Win Ansi,...]) into the internal "native"
+ *  code page.  As with A_TO_N(), conversion is done in place.
+ */
+#ifndef _ISO_INTERN
+#  ifdef CRTL_CP_IS_OEM
+#    ifndef IZ_ISO2OEM_ARRAY
+#      define IZ_ISO2OEM_ARRAY
+#    endif
+#    define _ISO_INTERN(str1) if (iso2oem) {register uch *p;\
+       for (p=(uch *)(str1); *p; p++)\
+         *p = native((*p & 0x80) ? iso2oem[*p & 0x7f] : *p);}
+#  else
+#    define _ISO_INTERN(str1)   A_TO_N(str1)
+#  endif
+#endif
+
+#ifndef _OEM_INTERN
+#  ifdef CRTL_CP_IS_OEM
+#    define _OEM_INTERN(str1)   A_TO_N(str1)
+#  else
+#    ifndef IZ_OEM2ISO_ARRAY
+#      define IZ_OEM2ISO_ARRAY
+#    endif
+#    define _OEM_INTERN(str1) if (oem2iso) {register uch *p;\
+       for (p=(uch *)(str1); *p; p++)\
+         *p = native((*p & 0x80) ? oem2iso[*p & 0x7f] : *p);}
+#  endif
+#endif
+
+#ifndef STR_TO_ISO
+#  ifdef CRTL_CP_IS_ISO
+#    define STR_TO_ISO          strcpy
+#  else
+#    define STR_TO_ISO          str2iso
+#    define NEED_STR2ISO
+#  endif
+#endif
+
+#ifndef STR_TO_OEM
+#  ifdef CRTL_CP_IS_OEM
+#    define STR_TO_OEM          strcpy
+#  else
+#    define STR_TO_OEM          str2oem
+#    define NEED_STR2OEM
+#  endif
+#endif
+
+#if (!defined(INTERN_TO_ISO) && !defined(ASCII2ISO))
+#  ifdef CRTL_CP_IS_OEM
+     /* know: "ASCII" is "OEM" */
+#    define ASCII2ISO(c) \
+       ((((c) & 0x80) && oem2iso) ? oem2iso[(c) & 0x7f] : (c))
+#    if (defined(NEED_STR2ISO) && !defined(CRYP_USES_OEM2ISO))
+#      define CRYP_USES_OEM2ISO
+#    endif
+#  else
+     /* assume: "ASCII" is "ISO-ANSI" */
+#    define ASCII2ISO(c) (c)
+#  endif
+#endif
+
+#if (!defined(INTERN_TO_OEM) && !defined(ASCII2OEM))
+#  ifdef CRTL_CP_IS_OEM
+     /* know: "ASCII" is "OEM" */
+#    define ASCII2OEM(c) (c)
+#  else
+     /* assume: "ASCII" is "ISO-ANSI" */
+#    define ASCII2OEM(c) \
+       ((((c) & 0x80) && iso2oem) ? iso2oem[(c) & 0x7f] : (c))
+#    if (defined(NEED_STR2OEM) && !defined(CRYP_USES_ISO2OEM))
+#      define CRYP_USES_ISO2OEM
+#    endif
+#  endif
+#endif
+
+/* codepage conversion setup for testp() in crypt.c */
+#ifdef CRTL_CP_IS_ISO
+#  ifndef STR_TO_CP2
+#    define STR_TO_CP2  STR_TO_OEM
+#  endif
+#else
+#  ifdef CRTL_CP_IS_OEM
+#    ifndef STR_TO_CP2
+#      define STR_TO_CP2  STR_TO_ISO
+#    endif
+#  else /* native internal CP is neither ISO nor OEM */
+#    ifndef STR_TO_CP1
+#      define STR_TO_CP1  STR_TO_ISO
+#    endif
+#    ifndef STR_TO_CP2
+#      define STR_TO_CP2  STR_TO_OEM
+#    endif
+#  endif
+#endif
+
+
+/* Convert filename (and file comment string) into "internal" charset.
+ * This macro assumes that Zip entry filenames are coded in OEM (IBM DOS)
+ * codepage when made on
+ *  -> DOS (this includes 16-bit Windows 3.1)  (FS_FAT_)
+ *  -> OS/2                                    (FS_HPFS_)
+ *  -> Win95/WinNT with Nico Mak's WinZip      (FS_NTFS_ && hostver == "5.0")
+ * EXCEPTIONS:
+ *  PKZIP for Windows 2.5, 2.6, and 4.0 flag their entries as "FS_FAT_", but
+ *  the filename stored in the local header is coded in Windows ANSI (CP 1252
+ *  resp. ISO 8859-1 on US and western Europe locale settings).
+ *  Likewise, PKZIP for UNIX 2.51 flags its entries as "FS_FAT_", but the
+ *  filenames stored in BOTH the local and the central header are coded
+ *  in the local system's codepage (usually ANSI codings like ISO 8859-1).
+ *
+ * All other ports are assumed to code zip entry filenames in ISO 8859-1.
+ */
+#ifndef Ext_ASCII_TO_Native
+#  define Ext_ASCII_TO_Native(string, hostnum, hostver, isuxatt, islochdr) \
+    if (((hostnum) == FS_FAT_ && \
+         !(((islochdr) || (isuxatt)) && \
+           ((hostver) == 25 || (hostver) == 26 || (hostver) == 40))) || \
+        (hostnum) == FS_HPFS_ || \
+        ((hostnum) == FS_NTFS_ /* && (hostver) == 50 */ )) { \
+        _OEM_INTERN((string)); \
+    } else { \
+        _ISO_INTERN((string)); \
+    }
+#endif
+
+
+
+/**********************/
+/*  Global constants  */
+/**********************/
+
+   extern ZCONST unsigned near mask_bits[17];
+   extern ZCONST char *fnames[2];
+
+#ifdef EBCDIC
+   extern ZCONST uch ebcdic[];
+#endif
+#ifdef IZ_ISO2OEM_ARRAY
+   extern ZCONST uch Far *iso2oem;
+   extern ZCONST uch Far iso2oem_850[];
+#endif
+#ifdef IZ_OEM2ISO_ARRAY
+   extern ZCONST uch Far *oem2iso;
+   extern ZCONST uch Far oem2iso_850[];
+#endif
+
+   extern ZCONST char Far  VersionDate[];
+   extern ZCONST char Far  CentSigMsg[];
+#ifndef SFX
+   extern ZCONST char Far  EndSigMsg[];
+#endif
+   extern ZCONST char Far  SeekMsg[];
+   extern ZCONST char Far  FilenameNotMatched[];
+   extern ZCONST char Far  ExclFilenameNotMatched[];
+   extern ZCONST char Far  ReportMsg[];
+
+#ifndef SFX
+   extern ZCONST char Far  Zipnfo[];
+   extern ZCONST char Far  CompiledWith[];
+#endif /* !SFX */
+
+
+
+/***********************************/
+/*  Global (shared?) RTL variables */
+/***********************************/
+
+#ifdef DECLARE_ERRNO
+   extern int             errno;
+#endif
+
+/*---------------------------------------------------------------------
+    Unicode Support
+    28 August 2005
+  ---------------------------------------------------------------------*/
+#if (defined(UNICODE_SUPPORT) && defined(UNICODE_WCHAR))
+
+  /* Default character when a zwchar too big for wchar_t */
+# define zwchar_to_wchar_t_default_char '_'
+
+  /* Default character string when wchar_t does not convert to mb */
+# define wide_to_mb_default_string "_"
+
+  /* wide character type */
+  typedef unsigned long zwchar;
+
+  /* UTF-8 related conversion functions, currently found in process.c */
+
+# if 0 /* currently unused */
+  /* check if string is all ASCII */
+  int is_ascii_string OF((ZCONST char *mbstring));
+# endif /* unused */
+
+  /* convert UTF-8 string to multi-byte string */
+  char *utf8_to_local_string OF((ZCONST char *utf8_string, int escape_all));
+
+  /* convert UTF-8 string to wide string */
+  zwchar *utf8_to_wide_string OF((ZCONST char *utf8_string));
+
+  /* convert wide string to multi-byte string */
+  char *wide_to_local_string OF((ZCONST zwchar *wide_string, int escape_all));
+
+# if 0 /* currently unused */
+  /* convert local string to multi-byte display string */
+  char *local_to_display_string OF((ZCONST char *local_string));
+# endif /* unused */
+
+  /* convert wide character to escape string */
+  char *wide_to_escape_string OF((unsigned long));
+
+# define utf8_to_escaped_string(utf8_string) \
+         utf8_to_local_string(utf8_string, TRUE)
+
+# if 0 /* currently unused */
+  /* convert escape string to wide character */
+  unsigned long escape_string_to_wide OF((ZCONST char *escape_string));
+
+  /* convert local to UTF-8 */
+  char *local_to_utf8_string OF ((ZCONST char *local_string));
+
+  /* convert local to wide string */
+  zwchar *local_to_wide_string OF ((ZCONST char *local_string));
+
+  /* convert wide string to UTF-8 */
+  char *wide_to_utf8_string OF((ZCONST zwchar *wide_string));
+# endif /* unused */
+
+#endif /* UNICODE_SUPPORT && UNICODE_WCHAR */
+
+
+#endif /* !__unzpriv_h */
diff -Naur a/zipinfo.c b/zipinfo.c
--- a/zipinfo.c	2009-02-08 17:04:30.000000000 +0000
+++ b/zipinfo.c	2019-12-02 01:49:39.895641007 +0000
@@ -457,6 +457,10 @@
     int    tflag_slm=TRUE, tflag_2v=FALSE;
     int    explicit_h=FALSE, explicit_t=FALSE;
 
+#ifdef UNIX
+    extern char OEM_CP[MAX_CP_NAME];
+    extern char ISO_CP[MAX_CP_NAME];
+#endif
 
 #ifdef MACOS
     uO.lflag = LFLAG;         /* reset default on each call */
@@ -501,6 +505,37 @@
                             uO.lflag = 0;
                     }
                     break;
+#ifdef UNIX
+    			case ('I'):
+                    if (negative) {
+                        Info(slide, 0x401, ((char *)slide,
+                          "error:  encodings can't be negated"));
+                        return(PK_PARAM);
+    				} else {
+    					if(*s) { /* Handle the -Icharset case */
+    						/* Assume that charsets can't start with a dash to spot arguments misuse */
+    						if(*s == '-') { 
+    	                        Info(slide, 0x401, ((char *)slide,
+        		                  "error:  a valid character encoding should follow the -I argument"));
+    	                        return(PK_PARAM); 
+    						}
+    						strncpy(ISO_CP, s, MAX_CP_NAME - 1);
+                ISO_CP[MAX_CP_NAME - 1] = '\0';
+    					} else { /* -I charset */
+    						++argv;
+    						if(!(--argc > 0 && *argv != NULL && **argv != '-')) {
+    	                        Info(slide, 0x401, ((char *)slide,
+        		                  "error:  a valid character encoding should follow the -I argument"));
+    	                        return(PK_PARAM); 
+    						}
+    						s = *argv;
+    						strncpy(ISO_CP, s, MAX_CP_NAME - 1);
+                ISO_CP[MAX_CP_NAME - 1] = '\0';
+    					}
+    					while(*(++s)); /* No params straight after charset name */
+    				}
+    				break;
+#endif /* ?UNIX */
                 case 'l':      /* longer form of "ls -l" type listing */
                     if (negative)
                         uO.lflag = -2, negative = 0;
@@ -521,6 +556,37 @@
                         G.M_flag = TRUE;
                     break;
 #endif
+#ifdef UNIX
+    			case ('O'):
+                    if (negative) {
+                        Info(slide, 0x401, ((char *)slide,
+                          "error:  encodings can't be negated"));
+                        return(PK_PARAM);
+    				} else {
+    					if(*s) { /* Handle the -Ocharset case */
+    						/* Assume that charsets can't start with a dash to spot arguments misuse */
+    						if(*s == '-') { 
+    	                        Info(slide, 0x401, ((char *)slide,
+        		                  "error:  a valid character encoding should follow the -I argument"));
+    	                        return(PK_PARAM); 
+    						}
+    						strncpy(OEM_CP, s, MAX_CP_NAME - 1);
+                OEM_CP[MAX_CP_NAME - 1] = '\0';
+    					} else { /* -O charset */
+    						++argv;
+    						if(!(--argc > 0 && *argv != NULL && **argv != '-')) {
+    	                        Info(slide, 0x401, ((char *)slide,
+        		                  "error:  a valid character encoding should follow the -O argument"));
+    	                        return(PK_PARAM); 
+    						}
+    						s = *argv;
+    						strncpy(OEM_CP, s, MAX_CP_NAME - 1);
+                OEM_CP[MAX_CP_NAME - 1] = '\0';
+    					}
+    					while(*(++s)); /* No params straight after charset name */
+    				}
+    				break;
+#endif /* ?UNIX */
                 case 's':      /* default:  shorter "ls -l" type listing */
                     if (negative)
                         uO.lflag = -2, negative = 0;
@@ -771,7 +837,7 @@
                 Info(slide, 0x401,
                      ((char *)slide, LoadFarString(CentSigMsg), j));
                 Info(slide, 0x401,
-                     ((char *)slide, LoadFarString(ReportMsg)));
+                     ((char *)slide,"%s", LoadFarString(ReportMsg)));
                 error_in_archive = PK_BADERR;   /* sig not found */
                 break;
             }
@@ -960,7 +1026,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 */
         }
 
@@ -1881,7 +1948,7 @@
 #endif
     int         k, error, error_in_archive=PK_COOL;
     unsigned    hostnum, hostver, methid, methnum, xattr;
-    char        *p, workspace[12], attribs[16];
+    char        *p, workspace[12], attribs[17];
     char        methbuf[5];
     static ZCONST char dtype[5]="NXFS"; /* normal, maximum, fast, superfast */
     static ZCONST char Far os[NUM_HOSTS+1][4] = {
@@ -1921,7 +1988,19 @@
         ush  dnum=(ush)((G.crec.general_purpose_bit_flag>>1) & 3);
         methbuf[3] = dtype[dnum];
     } else if (methnum >= NUM_METHODS) {   /* unknown */
-        sprintf(&methbuf[1], "%03u", G.crec.compression_method);
+        /* 2016-12-05 SMS.
+         * https://launchpad.net/bugs/1643750
+         * Unexpectedly large compression methods overflow
+         * &methbuf[].  Use the old, three-digit decimal format
+         * for values which fit.  Otherwise, sacrifice the "u",
+         * and use four-digit hexadecimal.
+         */
+        if (G.crec.compression_method <= 999) {
+              sprintf( &methbuf[ 1], "%03u", G.crec.compression_method);
+        } else {
+              sprintf( &methbuf[ 0], "%04X", G.crec.compression_method);
+        }
+
     }
 
     for (k = 0;  k < 15;  ++k)
