🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: vmsnet.sources
Article #63

Zip v1.9 & UnZip v5.0, part 02/22

#63
From: goathunter
Date: Tue, 01 Sep 1992 22:48
1377 lines
49702 bytes
Submitted-by: goathunter@wkuvx1.bitnet
Posting-number: Volume 3, Issue 124
Archive-name: zip_unzip/part02

-+-+-+-+-+-+-+-+ START OF PART 2 -+-+-+-+-+-+-+-+
X          else                  /* do it slow to avoid memcpy() overlap */
X#endif /* !NOMEMCPY */
X            do `7B
X              slide`5Bw++`5D = slide`5Bd++`5D;
X            `7D while (--e);
X        if (w == WSIZE)
X        `7B
X          flush(w);
X          w = u = 0;
X        `7D
X      `7D while (n);
X    `7D
X  `7D
X
X  /* flush out slide */
X  flush(w);
X  return csize ? 5 : 0;         /* should have read csize bytes */
X`7D
X
X
X
Xint explode_nolit8(tl, td, bl, bd)
Xstruct huft *tl, *td;   /* length and distance decoder tables */
Xint bl, bd;             /* number of bits decoded by tl`5B`5D and td`5B`5D *
V/
X/* Decompress the imploded data using uncoded literals and an 8K sliding
X   window. */
X`7B
X  longint s;            /* bytes to decompress */
X  register unsigned e;  /* table entry flag/number of extra bits */
X  unsigned n, d;        /* length and index for copy */
X  unsigned w;           /* current window position */
X  struct huft *t;       /* pointer to table entry */
X  unsigned ml, md;      /* masks for bl and bd bits */
X  register ULONG b;     /* bit buffer */
X  register unsigned k;  /* number of bits in bit buffer */
X  unsigned u;           /* true if unflushed */
X
X
X  /* explode the coded data */
X  b = k = w = 0;                /* initialize bit buffer, window */
X  u = 1;                        /* buffer unflushed */
X  ml = mask_bits`5Bbl`5D;           /* precompute masks for speed */
X  md = mask_bits`5Bbd`5D;
X  s = ucsize;
X  while (s > 0)                 /* do until ucsize bytes uncompressed */
X  `7B
X    NEEDBITS(1)
X    if (b & 1)                  /* then literal--get eight bits */
X    `7B
X      DUMPBITS(1)
X      s--;
X      NEEDBITS(8)
X      slide`5Bw++`5D = (byte)b;
X      if (w == WSIZE)
X      `7B
X        flush(w);
X        w = u = 0;
X      `7D
X      DUMPBITS(8)
X    `7D
X    else                        /* else distance/length */
X    `7B
X      DUMPBITS(1)
X      NEEDBITS(7)               /* get distance low bits */
X      d = (unsigned)b & 0x7f;
X      DUMPBITS(7)
X      NEEDBITS((unsigned)bd)    /* get coded distance high bits */
X      if ((e = (t = td + ((`7E(unsigned)b) & md))->e) > 16)
X        do `7B
X          if (e == 99)
X            return 1;
X          DUMPBITS(t->b)
X          e -= 16;
X          NEEDBITS(e)
X        `7D while ((e = (t = t->v.t + ((`7E(unsigned)b) & mask_bits`5Be`5D))
V->e) > 16);
X      DUMPBITS(t->b)
X      d = w - d - t->v.n;       /* construct offset */
X      NEEDBITS((unsigned)bl)    /* get coded length */
X      if ((e = (t = tl + ((`7E(unsigned)b) & ml))->e) > 16)
X        do `7B
X          if (e == 99)
X            return 1;
X          DUMPBITS(t->b)
X          e -= 16;
X          NEEDBITS(e)
X        `7D while ((e = (t = t->v.t + ((`7E(unsigned)b) & mask_bits`5Be`5D))
V->e) > 16);
X      DUMPBITS(t->b)
X      n = t->v.n;
X      if (e)                    /* get length extra bits */
X      `7B
X        NEEDBITS(8)
X        n += (unsigned)b & 0xff;
X        DUMPBITS(8)
X      `7D
X
X      /* do the copy */
X      s -= n;
X      do `7B
X        n -= (e = (e = WSIZE - ((d &= WSIZE-1) > w ? d : w)) > n ? n : e);
X        if (u && w <= d)
X        `7B
X          memset(slide + w, 0, e);
X          w += e;
X          d += e;
X        `7D
X        else
X#ifndef NOMEMCPY
X          if (w - d >= e)       /* (this test assumes unsigned comparison) *
V/
X          `7B
X            memcpy(slide + w, slide + d, e);
X            w += e;
X            d += e;
X          `7D
X          else                  /* do it slow to avoid memcpy() overlap */
X#endif /* !NOMEMCPY */
X            do `7B
X              slide`5Bw++`5D = slide`5Bd++`5D;
X            `7D while (--e);
X        if (w == WSIZE)
X        `7B
X          flush(w);
X          w = u = 0;
X        `7D
X      `7D while (n);
X    `7D
X  `7D
X
X  /* flush out slide */
X  flush(w);
X  return csize ? 5 : 0;         /* should have read csize bytes */
X`7D
X
X
X
Xint explode_nolit4(tl, td, bl, bd)
Xstruct huft *tl, *td;   /* length and distance decoder tables */
Xint bl, bd;             /* number of bits decoded by tl`5B`5D and td`5B`5D *
V/
X/* Decompress the imploded data using uncoded literals and a 4K sliding
X   window. */
X`7B
X  longint s;            /* bytes to decompress */
X  register unsigned e;  /* table entry flag/number of extra bits */
X  unsigned n, d;        /* length and index for copy */
X  unsigned w;           /* current window position */
X  struct huft *t;       /* pointer to table entry */
X  unsigned ml, md;      /* masks for bl and bd bits */
X  register ULONG b;     /* bit buffer */
X  register unsigned k;  /* number of bits in bit buffer */
X  unsigned u;           /* true if unflushed */
X
X
X  /* explode the coded data */
X  b = k = w = 0;                /* initialize bit buffer, window */
X  u = 1;                        /* buffer unflushed */
X  ml = mask_bits`5Bbl`5D;           /* precompute masks for speed */
X  md = mask_bits`5Bbd`5D;
X  s = ucsize;
X  while (s > 0)                 /* do until ucsize bytes uncompressed */
X  `7B
X    NEEDBITS(1)
X    if (b & 1)                  /* then literal--get eight bits */
X    `7B
X      DUMPBITS(1)
X      s--;
X      NEEDBITS(8)
X      slide`5Bw++`5D = (byte)b;
X      if (w == WSIZE)
X      `7B
X        flush(w);
X        w = u = 0;
X      `7D
X      DUMPBITS(8)
X    `7D
X    else                        /* else distance/length */
X    `7B
X      DUMPBITS(1)
X      NEEDBITS(6)               /* get distance low bits */
X      d = (unsigned)b & 0x3f;
X      DUMPBITS(6)
X      NEEDBITS((unsigned)bd)    /* get coded distance high bits */
X      if ((e = (t = td + ((`7E(unsigned)b) & md))->e) > 16)
X        do `7B
X          if (e == 99)
X            return 1;
X          DUMPBITS(t->b)
X          e -= 16;
X          NEEDBITS(e)
X        `7D while ((e = (t = t->v.t + ((`7E(unsigned)b) & mask_bits`5Be`5D))
V->e) > 16);
X      DUMPBITS(t->b)
X      d = w - d - t->v.n;       /* construct offset */
X      NEEDBITS((unsigned)bl)    /* get coded length */
X      if ((e = (t = tl + ((`7E(unsigned)b) & ml))->e) > 16)
X        do `7B
X          if (e == 99)
X            return 1;
X          DUMPBITS(t->b)
X          e -= 16;
X          NEEDBITS(e)
X        `7D while ((e = (t = t->v.t + ((`7E(unsigned)b) & mask_bits`5Be`5D))
V->e) > 16);
X      DUMPBITS(t->b)
X      n = t->v.n;
X      if (e)                    /* get length extra bits */
X      `7B
X        NEEDBITS(8)
X        n += (unsigned)b & 0xff;
X        DUMPBITS(8)
X      `7D
X
X      /* do the copy */
X      s -= n;
X      do `7B
X        n -= (e = (e = WSIZE - ((d &= WSIZE-1) > w ? d : w)) > n ? n : e);
X        if (u && w <= d)
X        `7B
X          memset(slide + w, 0, e);
X          w += e;
X          d += e;
X        `7D
X        else
X#ifndef NOMEMCPY
X          if (w - d >= e)       /* (this test assumes unsigned comparison) *
V/
X          `7B
X            memcpy(slide + w, slide + d, e);
X            w += e;
X            d += e;
X          `7D
X          else                  /* do it slow to avoid memcpy() overlap */
X#endif /* !NOMEMCPY */
X            do `7B
X              slide`5Bw++`5D = slide`5Bd++`5D;
X            `7D while (--e);
X        if (w == WSIZE)
X        `7B
X          flush(w);
X          w = u = 0;
X        `7D
X      `7D while (n);
X    `7D
X  `7D
X
X  /* flush out slide */
X  flush(w);
X  return csize ? 5 : 0;         /* should have read csize bytes */
X`7D
X
X
X
Xint explode()
X/* Explode an imploded compressed stream.  Based on the general purpose
X   bit flag, decide on coded or uncoded literals, and an 8K or 4K sliding
X   window.  Construct the literal (if any), length, and distance codes and
X   the tables needed to decode them (using huft_build() from inflate.c),
X   and call the appropriate routine for the type of data in the remainder
X   of the stream.  The four routines are nearly identical, differing only
X   in whether the literal is decoded or simply read in, and in how many
X   bits are read in, uncoded, for the low distance bits. */
X`7B
X  unsigned r;           /* return codes */
X  struct huft *tb;      /* literal code table */
X  struct huft *tl;      /* length code table */
X  struct huft *td;      /* distance code table */
X  int bb;               /* bits for tb */
X  int bl;               /* bits for tl */
X  int bd;               /* bits for td */
X  unsigned l`5B256`5D;      /* bit lengths for codes */
X
X
X  /* Tune base table sizes.  Note: I thought that to truly optimize speed,
X     I would have to select different bl, bd, and bb values for different
X     compressed file sizes.  I was suprised to find out the the values of
X     7, 7, and 9 worked best over a very wide range of sizes, except that
X     bd = 8 worked marginally better for large compressed sizes. */
X  bl = 7;
X  bd = csize > 200000L ? 8 : 7;
X
X
X  /* With literal tree--minimum match length is 3 */
X  hufts = 0;                    /* initialze huft's malloc'ed */
X  if (lrec.general_purpose_bit_flag & 4)
X  `7B
X    bb = 9;                     /* base table size for literals */
X    if ((r = get_tree(l, 256)) != 0)
X      return r;
X    if ((r = huft_build(l, 256, 256, NULL, NULL, &tb, &bb)) != 0)
X    `7B
X      if (r == 1)
X        huft_free(tb);
X      return r;
X    `7D
X    if ((r = get_tree(l, 64)) != 0)
X      return r;
X    if ((r = huft_build(l, 64, 0, cplen3, extra, &tl, &bl)) != 0)
X    `7B
X      if (r == 1)
X        huft_free(tl);
X      huft_free(tb);
X      return r;
X    `7D
X    if ((r = get_tree(l, 64)) != 0)
X      return r;
X    if (lrec.general_purpose_bit_flag & 2)      /* true if 8K */
X    `7B
X      if ((r = huft_build(l, 64, 0, cpdist8, extra, &td, &bd)) != 0)
X      `7B
X        if (r == 1)
X          huft_free(td);
X        huft_free(tl);
X        huft_free(tb);
X        return r;
X      `7D
X      r = explode_lit8(tb, tl, td, bb, bl, bd);
X    `7D
X    else                                        /* else 4K */
X    `7B
X      if ((r = huft_build(l, 64, 0, cpdist4, extra, &td, &bd)) != 0)
X      `7B
X        if (r == 1)
X          huft_free(td);
X        huft_free(tl);
X        huft_free(tb);
X        return r;
X      `7D
X      r = explode_lit4(tb, tl, td, bb, bl, bd);
X    `7D
X    huft_free(td);
X    huft_free(tl);
X    huft_free(tb);
X  `7D
X  else
X
X
X  /* No literal tree--minimum match length is 2 */
X  `7B
X    if ((r = get_tree(l, 64)) != 0)
X      return r;
X    if ((r = huft_build(l, 64, 0, cplen2, extra, &tl, &bl)) != 0)
X    `7B
X      if (r == 1)
X        huft_free(tl);
X      return r;
X    `7D
X    if ((r = get_tree(l, 64)) != 0)
X      return r;
X    if (lrec.general_purpose_bit_flag & 2)      /* true if 8K */
X    `7B
X      if ((r = huft_build(l, 64, 0, cpdist8, extra, &td, &bd)) != 0)
X      `7B
X        if (r == 1)
X          huft_free(td);
X        huft_free(tl);
X        return r;
X      `7D
X      r = explode_nolit8(tl, td, bl, bd);
X    `7D
X    else                                        /* else 4K */
X    `7B
X      if ((r = huft_build(l, 64, 0, cpdist4, extra, &td, &bd)) != 0)
X      `7B
X        if (r == 1)
X          huft_free(td);
X        huft_free(tl);
X        return r;
X      `7D
X      r = explode_nolit4(tl, td, bl, bd);
X    `7D
X    huft_free(td);
X    huft_free(tl);
X  `7D
X#ifdef DEBUG
X  fprintf(stderr, "<%u > ", hufts);
X#endif /* DEBUG */
X  return r;
X`7D
$ CALL UNPACK [.UNZIP50]EXPLODE.C;1 811679058
$ create 'f'
X/*--------------------------------------------------------------------------
V-
X
X  extract.c
X
X  This file contains the high-level routines ("driver routines") for extrac-
X  ting and testing zipfile members.  It calls the low-level routines in file
Vs
X  inflate.c, unimplod.c, unreduce.c and unshrink.c.
X
X  --------------------------------------------------------------------------
V-*/
X
X
X#include "unzip.h"
X#ifdef  MSWIN
X#  include "wizunzip.h"
X#  include "replace.h"
X#endif /* MSWIN */
X
X
X/************************************/
X/*  Extract Local Prototypes, etc.  */
X/************************************/
X
Xstatic int store_info __((void));
Xstatic int extract_or_test_member __((void));
X#ifdef CRYPT
X   static int decrypt_member __((void));
X   static int testp __((byte *hdr));
X#endif
X
Xstatic byte *mem_i_buffer;
Xstatic byte *mem_o_buffer;
Xstatic ULONG mem_i_size, mem_i_offset;
Xstatic ULONG mem_o_size, mem_o_offset;
X
Xstatic char *VersionMsg =
X  " skipping: %-22s  need %s compat. v%u.%u (can do v%u.%u)\n";
Xstatic char *ComprMsg =
X  " skipping: %-22s  compression method %d\n";
Xstatic char *FilNamMsg =
X  "%s:  bad filename length (%s)\n";
Xstatic char *ExtFieldMsg =
X  "%s:  bad extra field length (%s)\n";
Xstatic char *OffsetMsg =
X  "file #%d:  bad zipfile offset (%s)\n";
X
X
X
X
X
X/**************************************/
X/*  Function extract_or_test_files()  */
X/**************************************/
X
Xint extract_or_test_files()    /* return PK-type error code */
X`7B
X    char **fnamev;
X    byte *cd_inptr;
X    int cd_incnt, error, error_in_archive=0;
X    int renamed, query, len, filnum=(-1), blknum=0;
X#ifdef OS2
X    extern int longname;  /* from os2unzip.c */
X#endif
X    UWORD i, j, members_remaining, num_skipped=0, num_bad_pwd=0;
X    longint cd_bufstart, bufstart, inbuf_offset, request;
X    min_info info`5BDIR_BLKSIZ`5D;
X
X
X/*--------------------------------------------------------------------------
V-
X    The basic idea of this function is as follows.  Since the central di-
X    rectory lies at the end of the zipfile and the member files lie at the
X    beginning or middle or wherever, it is not very desirable to simply
X    read a central directory entry, jump to the member and extract it, and
X    then jump back to the central directory.  In the case of a large zipfile
X    this would lead to a whole lot of disk-grinding, especially if each mem-
X    ber file is small.  Instead, we read from the central directory the per-
X    tinent information for a block of files, then go extract/test the whole
X    block.  Thus this routine contains two small(er) loops within a very
X    large outer loop:  the first of the small ones reads a block of files
X    from the central directory; the second extracts or tests each file; and
X    the outer one loops over blocks.  There's some file-pointer positioning
X    stuff in between, but that's about it.  Btw, it's because of this jump-
X    ing around that we can afford to be lenient if an error occurs in one of
X    the member files:  we should still be able to go find the other members,
X    since we know the offset of each from the beginning of the zipfile.
X
X    Begin main loop over blocks of member files.  We know the entire central
X    directory is on this disk:  we would not have any of this information un
V-
X    less the end-of-central-directory record was on this disk, and we would
X    not have gotten to this routine unless this is also the disk on which
X    the central directory starts.  In practice, this had better be the ONLY
X    disk in the archive, but maybe someday we'll add multi-disk support.
X  --------------------------------------------------------------------------
V-*/
X
X    pInfo = info;
X    members_remaining = ecrec.total_entries_central_dir;
X
X    while (members_remaining) `7B
X        j = 0;
X
X        /*
X         * Loop through files in central directory, storing offsets, file
X         * attributes, and case-conversion flags until block size is reached
V.
X         */
X
X        while (members_remaining && (j < DIR_BLKSIZ)) `7B
X            --members_remaining;
X            pInfo = &info`5Bj`5D;
X
X            if (readbuf(sig, 4) <= 0) `7B
X                error_in_archive = 51;  /* 51:  unexpected EOF */
X                members_remaining = 0;  /* ...so no more left to do */
X                break;
X            `7D
X            if (strncmp(sig, central_hdr_sig, 4)) `7B  /* just to make sure
V */
X                fprintf(stderr, CentSigMsg, j);  /* sig not found */
X                fprintf(stderr, ReportMsg);   /* check binary transfers */
X                error_in_archive = 3;   /* 3:  error in zipfile */
X                members_remaining = 0;  /* ...so no more left to do */
X                break;
X            `7D
X            /* process_cdir_file_hdr() sets pInfo->hostnum, pInfo->lcflag */
X            if ((error = process_cdir_file_hdr()) != 0) `7B
X                error_in_archive = error;   /* only 51 (EOF) defined */
X                members_remaining = 0;  /* ...so no more left to do */
X                break;
X            `7D
X            if ((error = do_string(crec.filename_length, FILENAME)) != 0) `7
VB
X                if (error > error_in_archive)
X                    error_in_archive = error;
X                if (error > 1) `7B  /* fatal:  no more left to do */
X                    fprintf(stderr, FilNamMsg, filename, "central");
X                    members_remaining = 0;
X                    break;
X                `7D
X            `7D
X            if ((error = do_string(crec.extra_field_length, EXTRA_FIELD)) !=
V 0)
X            `7B
X                if (error > error_in_archive)
X                    error_in_archive = error;
X                if (error > 1) `7B  /* fatal */
X                    fprintf(stderr, ExtFieldMsg, filename, "central");
X                    members_remaining = 0;
X                    break;
X                `7D
X            `7D
X            if ((error = do_string(crec.file_comment_length, SKIP)) != 0) `7
VB
X                if (error > error_in_archive)
X                    error_in_archive = error;
X                if (error > 1) `7B  /* fatal */
X                    fprintf(stderr, "\n%s:  bad file comment length\n",
X                            filename);
X                    members_remaining = 0;
X                    break;
X                `7D
X            `7D
X            if (process_all_files) `7B
X                if (store_info())
X                    ++num_skipped;
X                else
X                    ++j;  /* file is OK: save info`5B`5D and continue with n
Vext */
X            `7D else `7B
X                fnamev = fnv;   /* don't destroy permanent filename pointer
V */
X                for (--fnamev; *++fnamev;)
X                    if (match(filename, *fnamev)) `7B
X                        if (store_info())
X                            ++num_skipped;
X                        else
X                            ++j;   /* file is OK */
X                        break;  /* found match for filename, so stop looping
V */
X                    `7D /* end if (match), for-loop (fnamev) */
X            `7D /* end if (process_all_files) */
X
X        `7D /* end while-loop (adding files to current block) */
X
X        /* save position in central directory so can come back later */
X        cd_bufstart = cur_zipfile_bufstart;
X        cd_inptr = inptr;
X        cd_incnt = incnt;
X
X    /*----------------------------------------------------------------------
V-
X        Second loop:  process files in current block, extracting or testing
X        each one.
X      ----------------------------------------------------------------------
V-*/
X
X        for (i = 0; i < j; ++i) `7B
X            filnum = i + blknum*DIR_BLKSIZ;
X            pInfo = &info`5Bi`5D;
X            /*
X             * if the target position is not within the current input buffer
X             * (either haven't yet read far enough, or (maybe) skipping back
V-
X             * ward) skip to the target position and reset readbuf().
X             */
X            /* LSEEK(pInfo->offset):  */
X            request = pInfo->offset + extra_bytes;
X            inbuf_offset = request % INBUFSIZ;
X            bufstart = request - inbuf_offset;
X
X            if (request < 0) `7B
X                fprintf(stderr, SeekMsg, ReportMsg);
X                error_in_archive = 3;       /* 3:  severe error in zipfile,
V */
X                continue;                   /*  but can still go on */
X            `7D else if (bufstart != cur_zipfile_bufstart) `7B
X                cur_zipfile_bufstart = lseek(zipfd, bufstart, SEEK_SET);
X                if ((incnt = read(zipfd,(char *)inbuf,INBUFSIZ)) <= 0) `7B
X                    fprintf(stderr, OffsetMsg, filnum, "lseek");
X                    error_in_archive = 3;   /* 3:  error in zipfile, but */
X                    continue;               /*  can still do next file   */
X                `7D
X                inptr = inbuf + (int)inbuf_offset;
X                incnt -= (int)inbuf_offset;
X            `7D else `7B
X                incnt += (inptr-inbuf) - (int)inbuf_offset;
X                inptr = inbuf + (int)inbuf_offset;
X            `7D
X
X            /* should be in proper position now, so check for sig */
X            if (readbuf(sig, 4) <= 0) `7B  /* bad offset */
X                fprintf(stderr, OffsetMsg, filnum, "EOF");
X                error_in_archive = 3;    /* 3:  error in zipfile */
X                continue;       /* but can still try next one */
X            `7D
X            if (strncmp(sig, local_hdr_sig, 4)) `7B
X                fprintf(stderr, OffsetMsg, filnum,
X                        "can't find local header sig");   /* bad offset */
X                error_in_archive = 3;
X                continue;
X            `7D
X            if ((error = process_local_file_hdr()) != 0) `7B
X                fprintf(stderr, "\nfile #%d:  bad local header\n", filnum);
X                error_in_archive = error;       /* only 51 (EOF) defined */
X                continue;       /* can still try next one */
X            `7D
X            if ((error = do_string(lrec.filename_length, FILENAME)) != 0) `7
VB
X                if (error > error_in_archive)
X                    error_in_archive = error;
X                if (error > 1) `7B
X                    fprintf(stderr, FilNamMsg, filename, "local");
X                    continue;   /* go on to next one */
X                `7D
X            `7D
X            if (extra_field != (byte *)NULL)
X                free(extra_field);
X            extra_field = (byte *)NULL;
X            if ((error = do_string(lrec.extra_field_length, EXTRA_FIELD)) !=
V 0)
X            `7B
X                if (error > error_in_archive)
X                    error_in_archive = error;
X                if (error > 1) `7B
X                    fprintf(stderr, ExtFieldMsg, filename, "local");
X                    continue;   /* go on */
X                `7D
X            `7D
X
X            /*
X             * just about to extract file:  if extracting to disk, check if
X             * already exists, and if so, take appropriate action according
V to
X             * fflag/uflag/overwrite_all/etc. (we couldn't do this in upper
X             * loop because we don't store the possibly renamed filename`5B`
V5D in
X             * info`5B`5D)
X             */
X            if (!tflag && !cflag) `7B
X                renamed = FALSE;   /* user hasn't renamed output file yet */
X#ifdef OS2
X                longname = FALSE;  /* no long name has yet been stored */
X#endif
X
Xstartover:
X                query = FALSE;
X#ifdef MACOS
X                macflag = (pInfo->hostnum == MAC_);
X#endif
X                /* mapname can create dirs if not freshening or if renamed *
V/
X                if ((error = mapname(!fflag `7C`7C renamed)) > 1) `7B    /*
V skip */
X                    if ((error > 2) && (error_in_archive < 2))
X                        error_in_archive = 2;   /* (weak) error in zipfile *
V/
X                    continue;   /* go on to next file */
X                `7D
X
X                switch (check_for_newer(filename)) `7B
X                    case DOES_NOT_EXIST:
X                        if (fflag && !renamed)  /* don't skip if just rename
Vd */
X                            continue;   /* freshen (no new files):  skip */
X                        break;
X                    case EXISTS_AND_OLDER:
X                        if (overwrite_none)
X                            continue;   /* never overwrite:  skip file */
X                        if (!overwrite_all && !force_flag)
X                            query = TRUE;
X                        break;
X                    case EXISTS_AND_NEWER:             /* (or equal) */
X                        if (overwrite_none `7C`7C (uflag && !renamed))
X                            continue;  /* skip if update/freshen & orig name
V */
X                        if (!overwrite_all && !force_flag)
X                            query = TRUE;
X                        break;
X                `7D
X/*#ifndef VMS*/ /* VMS creates higher version number instead of overwriting
X                 * (will have to modify for VMS-style names with specific
X                 *  version numbers:  just check V_flag?  don't use stat?) *
V/
X                if (query) `7B
X#ifdef MSWIN
X                    FARPROC lpfnprocReplace;
X                    int ReplaceDlgRetVal;   /* replace dialog return value *
V/
X
X                    ShowCursor(FALSE);      /* turn off cursor */
X                    SetCursor(hSaveCursor); /* restore the cursor */
X                    lpfnprocReplace = MakeProcInstance(ReplaceProc, hInst);
X                    ReplaceDlgRetVal = DialogBoxParam(hInst, "Replace",
X                      hWndMain, lpfnprocReplace, (DWORD)(LPSTR)filename);
X                    FreeProcInstance(lpfnprocReplace);
X                    hSaveCursor = SetCursor(hHourGlass);
X                    ShowCursor(TRUE);
X                    switch (ReplaceDlgRetVal) `7B
X                        case IDM_REPLACE_RENAME:
X                            renamed = TRUE;
X                            goto startover;   /* sorry for a goto */
X                        case IDM_REPLACE_YES:
X                            break;
X                        case IDM_REPLACE_ALL:
X                            overwrite_all = TRUE;
X                            overwrite_none = FALSE;  /* just to make sure */
X                            break;
X                        case IDM_REPLACE_NONE:
X                            overwrite_none = TRUE;
X                            overwrite_all = FALSE;  /* make sure */
X                            force_flag = FALSE;     /* ditto */
X                            /* FALL THROUGH, skip */
X                        case IDM_REPLACE_NO:
X                            continue;
X                    `7D
X#else /* !MSWIN */
Xreprompt:
X                    fprintf(stderr,
X                      "replace %s? `5By`5Des, `5Bn`5Do, `5BA`5Dll, `5BN`5Don
Ve, `5Br`5Dename: ",
X                      filename);
X                    FFLUSH   /* for Amiga and Mac MPW */
X                    fgets(answerbuf, 9, stdin);
X                    switch (*answerbuf) `7B
X                        case 'A':   /* dangerous option:  force caps */
X                            overwrite_all = TRUE;
X                            overwrite_none = FALSE;  /* just to make sure */
X                            break;
X                        case 'r':
X                        case 'R':
X                            do `7B
X                                fprintf(stderr, "new name: ");
X                                FFLUSH   /* for AMIGA and Mac MPW */
X                                fgets(filename, FILNAMSIZ, stdin);
X                                /* usually get \n here:  better check for it
V */
X                                len = strlen(filename);
X                                if (filename`5Blen-1`5D == '\n')
X                                    filename`5B--len`5D = 0;
X                            `7D while (len == 0);
X                            renamed = TRUE;
X                            goto startover;   /* sorry for a goto */
X                        case 'y':
X                        case 'Y':
X                            break;
X                        case 'N':
X                            overwrite_none = TRUE;
X                            overwrite_all = FALSE;  /* make sure */
X                            force_flag = FALSE;     /* ditto */
X                            /* FALL THROUGH, skip */
X                        case 'n':
X                            continue;   /* skip file */
X                        default:
X                            fprintf(stderr, "error:  invalid response `5B%c`
V5D\n",
X                              *answerbuf);   /* warn the user */
X                            goto reprompt;   /* why not another goto? */
X                    `7D /* end switch (*answerbuf) */
X#endif /* ?MSWIN */
X                `7D /* end if (query) */
X/*#endif*/ /* !VMS */
X            `7D /* end if (extracting to disk) */
X
X#ifdef CRYPT
X            if (pInfo->encrypted && ((error = decrypt_member()) != 0)) `7B
X                if (error == 10) `7B
X                    if (error > error_in_archive)
X                        error_in_archive = error;
X                    fprintf(stderr,
X                      " skipping: %-22s  unable to get password\n", filename
V);
X                `7D else `7B  /* (error == 1) */
X                    fprintf(stderr,
X                      " skipping: %-22s  incorrect password\n", filename);
X                    ++num_bad_pwd;
X                `7D
X                continue;   /* go on to next file */
X            `7D
X#endif /* CRYPT */
X            disk_full = 0;
X            if ((error = extract_or_test_member()) != 0) `7B
X                if (error > error_in_archive)
X                    error_in_archive = error;       /* ...and keep going */
X                if (disk_full > 1)
X                    return error_in_archive;        /* (unless disk full) */
X            `7D
X        `7D /* end for-loop (i:  files in current block) */
X
X
X        /*
X         * Jump back to where we were in the central directory, then go and
V do
X         * the next batch of files.
X         */
X
X        cur_zipfile_bufstart = lseek(zipfd, cd_bufstart, SEEK_SET);
X        read(zipfd, (char *)inbuf, INBUFSIZ);  /* were there b4 ==> no error
V */
X        inptr = cd_inptr;
X        incnt = cd_incnt;
X        ++blknum;
X
X#ifdef TEST
X        printf("\ncd_bufstart = %ld (%.8lXh)\n", cd_bufstart, cd_bufstart);
X        printf("cur_zipfile_bufstart = %ld (%.8lXh)\n", cur_zipfile_bufstart
V,
X          cur_zipfile_bufstart);
X        printf("inptr-inbuf = %d\n", inptr-inbuf);
X        printf("incnt = %d\n\n", incnt);
X#endif
X
X    `7D /* end while-loop (blocks of files in central directory) */
X
X/*--------------------------------------------------------------------------
V-
X    Double-check that we're back at the end-of-central-directory record, and
X    print quick summary of results, if we were just testing the archive.  We
X    send the summary to stdout so that people doing the testing in the back-
X    ground and redirecting to a file can just do a "tail" on the output file
V.
X  --------------------------------------------------------------------------
V-*/
X
X    readbuf(sig, 4);
X    if (strncmp(sig, end_central_sig, 4)) `7B     /* just to make sure again
V */
X        fprintf(stderr, EndSigMsg);  /* didn't find end-of-central-dir sig *
V/
X        fprintf(stderr, ReportMsg);  /* check binary transfers */
X        if (!error_in_archive)       /* don't overwrite stronger error */
X            error_in_archive = 1;    /* 1:  warning error */
X    `7D
X    if (tflag && (quietflg == 1)) `7B
X        int num=filnum+1 - num_bad_pwd;
X
X        if (error_in_archive)
X            printf("At least one error was detected in %s.\n", zipfn);
X        else if (num == 0)
X            printf("Caution:  zero files tested in %s.\n", zipfn);
X        else if (process_all_files && (num_skipped+num_bad_pwd == 0))
X            printf("No errors detected in %s.\n", zipfn);
X        else
X            printf("No errors detected in %s for the %d file%s tested.\n",
X              zipfn, num, (num==1)? "":"s");
X        if (num_skipped > 0)
X            printf("%d file%s skipped because of unsupported compression or\
X encoding.\n",
X              num_skipped, (num_skipped==1)? "":"s");
X#ifdef CRYPT
X        if (num_bad_pwd > 0)
X            printf("%d file%s skipped because of incorrect password.\n",
X              num_bad_pwd, (num_bad_pwd==1)? "":"s");
X#endif /* CRYPT */
X    `7D
X    if ((num_skipped > 0) && !error_in_archive)   /* files not tested or  */
X        error_in_archive = 1;                     /*  extracted:  warning */
X#ifdef CRYPT
X    if ((num_bad_pwd > 0) && !error_in_archive)   /* files not tested or  */
X        error_in_archive = 1;                     /*  extracted:  warning */
X#endif /* CRYPT */
X
X    return (error_in_archive);
X
X`7D /* end function extract_or_test_files() */
X
X
X
X
X
X/***************************/
X/*  Function store_info()  */
X/***************************/
X
Xstatic int store_info()   /* return 1 if skipping, 0 if OK */
X`7B
X    ULONG tmp;
X
X#define UNKN_COMPR \
X   (crec.compression_method>IMPLODED && crec.compression_method!=DEFLATED)
X#if 0  /* old */
X#  define UNKN_COMPR   (crec.compression_method>IMPLODED)
X#endif
X
X
X/*--------------------------------------------------------------------------
V-
X    Check central directory info for version/compatibility requirements.
X  --------------------------------------------------------------------------
V-*/
X
X    pInfo->encrypted = crec.general_purpose_bit_flag & 1;    /* bit field */
X    pInfo->ExtLocHdr = (crec.general_purpose_bit_flag & 8) == 8;  /* bit */
X    pInfo->text = crec.internal_file_attributes & 1;         /* bit field */
X    pInfo->crc = crec.crc32;
X    pInfo->compr_size = crec.compressed_size;
X
X    if (crec.version_needed_to_extract`5B1`5D == VMS_) `7B
X        if (crec.version_needed_to_extract`5B0`5D > VMS_VERSION) `7B
X            fprintf(stderr, VersionMsg, filename, "VMS",
X              crec.version_needed_to_extract`5B0`5D / 10,
X              crec.version_needed_to_extract`5B0`5D % 10,
X              VMS_VERSION / 10, VMS_VERSION % 10);
X            return 1;
X        `7D
X#ifndef VMS   /* won't be able to use extra field, but still have data */
X        else if (!tflag && !force_flag) `7B  /* if forcing, extract regardle
Vss */
X            fprintf(stderr,
X              "\n%s:  stored in VMS format.  Extract anyway? (y/n) ",
X              filename);
X            FFLUSH   /* for Amiga and Mac MPW */
X            fgets(answerbuf, 9, stdin);
X            if ((*answerbuf != 'y') && (*answerbuf != 'Y'))
X                return 1;
X        `7D
X#endif /* !VMS */
X    /* usual file type:  don't need VMS to extract */
X    `7D else if (crec.version_needed_to_extract`5B0`5D > UNZIP_VERSION) `7B
X        fprintf(stderr, VersionMsg, filename, "PK",
X          crec.version_needed_to_extract`5B0`5D / 10,
X          crec.version_needed_to_extract`5B0`5D % 10,
X          UNZIP_VERSION / 10, UNZIP_VERSION % 10);
X        return 1;
X    `7D
X
X    if UNKN_COMPR `7B
X        fprintf(stderr, ComprMsg, filename, crec.compression_method);
X        return 1;
X    `7D
X#ifndef CRYPT
X    if (pInfo->encrypted) `7B
X        fprintf(stderr, " skipping: %-22s  encrypted (not supported)\n",
X          filename);
X        return 1;
X    `7D
X#endif /* !CRYPT */
X
X/*--------------------------------------------------------------------------
V-
X    Store some central-directory information (encryption, file attributes,
X    offsets) for later use.
X  --------------------------------------------------------------------------
V-*/
X
X    tmp = crec.external_file_attributes;
X
X    pInfo->dos_attr = 32;   /* set archive bit:  file is not backed up */
X    switch (pInfo->hostnum) `7B
X        case UNIX_:
X        case VMS_:
X            pInfo->unix_attr = (unsigned) (tmp >> 16);
X            break;
X        case DOS_OS2_FAT_:
X        case OS2_HPFS_:
X            pInfo->dos_attr = (unsigned) tmp;
X            tmp = (!(tmp & 1)) << 1;   /* read-only bit */
X            pInfo->unix_attr = (unsigned) (0444 `7C (tmp<<6) `7C (tmp<<3) `7
VC tmp);
X#ifdef UNIX
X            umask( (int)(tmp=umask(0)) );
X            pInfo->unix_attr &= `7Etmp;
X#endif
X            break;
X        case MAC_:
X            pInfo->unix_attr = (unsigned) (tmp & 1);   /* read-only bit */
X            break;
X        default:
X            pInfo->unix_attr = 0666;
X            break;
X    `7D /* end switch (host-OS-created-by) */
X
X    pInfo->offset = (longint) crec.relative_offset_local_header;
X    return 0;
X
X`7D /* end function store_info() */
X
X
X
X
X
X/***************************************/
X/*  Function extract_or_test_member()  */
X/***************************************/
X
Xstatic int extract_or_test_member()    /* return PK-type error code */
X`7B
X#ifdef S_IFLNK
X    int symlnk=FALSE;
X#endif /* S_IFLNK */
X    int error=0;
X    UWORD b;
X
X
X
X/*--------------------------------------------------------------------------
V-
X    Initialize variables, buffers, etc.
X  --------------------------------------------------------------------------
V-*/
X
X    bits_left = 0;
X    bitbuf = 0L;
X    outpos = 0L;
X    outcnt = 0;
X    outptr = outbuf;
X    zipeof = 0;
X    crc32val = 0xFFFFFFFFL;
X
X#ifdef S_IFLNK
X    if ((pInfo->unix_attr & S_IFMT) == S_IFLNK  &&  (pInfo->hostnum == UNIX_
V)
X        && !tflag && !cflag)
X        symlnk = TRUE;
X#endif /* S_IFLNK */
X
X    memset(outbuf, 0xaa, OUTBUFSIZ);
X#if (!defined(DOS_OS2) `7C`7C defined(MSWIN))
X    if (aflag)                  /* if we have a scratchpad, clear it out */
X#ifdef MSWIN
X        _fmemset(outout, 0xaa, OUTBUFSIZ);
X#else /* !MSWIN */
X        memset(outout, 0xaa, OUTBUFSIZ);
X#endif /* ?MSWIN */
X#endif /* !DOS_OS2 `7C`7C MSWIN */
X
X    if (tflag) `7B
X        if (!quietflg) `7B
X            fprintf(stdout, "  Testing: %-22s ", filename);
X            fflush(stdout);
X        `7D
X    `7D else `7B
X        if (cflag) `7B            /* output to stdout (copy of it) */
X#if (defined(MACOS) `7C`7C defined(AMIGA))
X            outfd = 1;
X#else /* !(MACOS `7C`7C AMIGA) */
X            outfd = dup(1);     /* GRR: change this to #define for Mac/Amiga
V */
X#endif /* ?(MACOS `7C`7C AMIGA) */
X#ifdef DOS_OS2
X            if (!aflag)
X                setmode(outfd, O_BINARY);
X#endif /* DOS_OS2 */
X#ifdef VMS
X            if (create_output_file())   /* VMS version required for stdout!
V */
X                return 50;      /* 50:  disk full (?) */
X#endif
X        `7D else
X#ifdef S_IFLNK
X        if (!symlnk)    /* symlink() takes care of file creation */
X#endif /* !S_IFLNK */
X        `7B
X            if (create_output_file())
X                return 50;      /* 50:  disk full (?) */
X        `7D
X    `7D /* endif (!tflag) */
X
X/*--------------------------------------------------------------------------
V-
X    Unpack the file.
X  --------------------------------------------------------------------------
V-*/
X
X    switch (lrec.compression_method) `7B
X
X    case STORED:
X        if (!tflag && (quietflg < 2)) `7B
X            fprintf(stdout, " Extracting: %-22s ", filename);
X            if (cflag)
X                fprintf(stdout, "\n");
X            fflush(stdout);
X        `7D
X#ifdef S_IFLNK
X        /*
X         * If file came from Unix and is a symbolic link and we are extracti
Vng
X         * to disk, allocate a storage area, put the data in it, and create
V the
X         * link.  Since we know it's a symbolic link to start with, shouldn'
Vt
X         * have to worry about overflowing unsigned ints with unsigned longs
V.
X         * (This doesn't do anything for compressed symlinks, but that can b
Ve
X         * added later...it also doesn't set the time or permissions of the
X         * link, but does anyone really care?)
X         */
X        if (symlnk) `7B
X#if (defined(MTS) `7C`7C defined(MACOS))
X            fprintf(stdout, "\n  warning:  symbolic link ignored\n");
X            error = 1;          /* 1:  warning error */
X#else /* !(MTS `7C`7C MACOS) */
X            char *orig = (char *)malloc((unsigned)lrec.uncompressed_size+1);
X            char *p = orig;
X
X            while (ReadByte(&b))
X                *p++ = b;
X            *p = 0;   /* terminate string */
X            UpdateCRC((unsigned char *)orig, p-orig);
X            if (symlink(orig, filename))
X                if ((errno == EEXIST) && overwrite_all) `7B  /* OK to overwr
Vite */
X                    unlink(filename);
X                    if (symlink(orig, filename))
X                        perror("symlink error");
X                `7D else
X                    perror("symlink error");
X            free(orig);
X#endif /* ?(MTS `7C`7C MACOS) */
X        `7D else
X#endif /* S_IFLNK */
X        while (ReadByte(&b) && !disk_full)
X            OUTB(b)
X        break;
X
X    case SHRUNK:
X        if (!tflag && (quietflg < 2)) `7B
X            fprintf(stdout, "UnShrinking: %-22s ", filename);
X            if (cflag)
X                fprintf(stdout, "\n");
X            fflush(stdout);
X        `7D
X#ifdef S_IFLNK   /* !!! This code needs to be added to unShrink, etc. !!! */
X        if (symlnk) `7B
X            fprintf(stdout, "\n  warning:  symbolic link ignored\n");
X            error = 1;          /* 1:  warning error */
X        `7D
X#endif /* S_IFLNK */
X        unShrink();
X        break;
X
X    case REDUCED1:
X    case REDUCED2:
X    case REDUCED3:
X    case REDUCED4:
X        if (!tflag && (quietflg < 2)) `7B
X            fprintf(stdout, "  Expanding: %-22s ", filename);
X            if (cflag)
X                fprintf(stdout, "\n");
X            fflush(stdout);
X        `7D
X#ifdef S_IFLNK   /* !!! This code needs to be added to unShrink, etc. !!! */
X        if (symlnk) `7B
X            fprintf(stdout, "\n  warning:  symbolic link ignored\n");
X            error = 1;          /* 1:  warning error */
X        `7D
X#endif /* S_IFLNK */
X        unReduce();
X        break;
X
X    case IMPLODED:
X        if (!tflag && (quietflg < 2)) `7B
X            fprintf(stdout, "  Exploding: %-22s ", filename);
X            if (cflag)
X                fprintf(stdout, "\n");
X            fflush(stdout);
X        `7D
X#ifdef S_IFLNK   /* !!! This code needs to be added to unShrink, etc. !!! */
X        if (symlnk) `7B
X            fprintf(stdout, "\n  warning:  symbolic link ignored\n");
X            error = 1;          /* 1:  warning error */
X        `7D
X#endif /* S_IFLNK */
X        explode();   /* ignore return code for now */
X        break;
X
X    case DEFLATED:
X        if (!tflag && (quietflg < 2)) `7B
X            fprintf(stdout, "  Inflating: %-22s ", filename);
X            if (cflag)
X                fprintf(stdout, "\n");
X            fflush(stdout);
X        `7D
X#ifdef S_IFLNK   /* !!! This code needs to be added to unShrink, etc. !!! */
X        if (symlnk) `7B
X            fprintf(stdout, "\n  warning:  symbolic link ignored\n");
X            error = 1;          /* 1:  warning error */
X        `7D
X#endif /* S_IFLNK */
X        inflate();
X        break;
X
X    default:   /* should never get to this point */
X        fprintf(stderr, "%s:  unknown compression method\n", filename);
X        /* close and delete file before return? */
X        return 1;               /* 1:  warning error */
X
X    `7D /* end switch (compression method) */
X
X    if (disk_full) `7B            /* set by FlushOutput()/OUTB() macro */
X        if (disk_full > 1)
X            return 50;          /* 50:  disk full */
X        error = 1;              /* 1:  warning error */
X    `7D
X
X/*--------------------------------------------------------------------------
V-
X    Write the last partial buffer, if any; set the file date and time; and
X    close the file (not necessarily in that order).  Then make sure CRC came
X    out OK and print result.
X  --------------------------------------------------------------------------
V-*/
X
X#ifdef S_IFLNK
X    if (!symlnk) `7B
X#endif /* S_IFLNK */
X    if (!disk_full && FlushOutput())
X        if (disk_full > 1)
X            return 50;          /* 50:  disk full */
X        else `7B                  /* disk_full == 1 */
X            fprintf(stderr, "%s:  probably corrupt\n", filename);
X            error = 1;          /* 1:  warning error */
X        `7D
X
X    if (!tflag)
X#ifdef VMS
X        CloseOutputFile();
X#else /* !VMS */
X#ifdef MTS                      /* MTS can't set file time */
X        close(outfd);
X#else /* !MTS */
X        set_file_time_and_close();
X#endif /* ?MTS */
X#endif /* ?VMS */
X
X#ifdef S_IFLNK
X    `7D /* endif (!symlnk) */
X#endif /* S_IFLNK */
X
X    /* logical-AND crc32val for 64-bit machines */
X    if ((crc32val = ((`7Ecrc32val) & 0xFFFFFFFFL)) != lrec.crc32) `7B
X        /* if quietflg is set, we haven't output the filename yet:  do it */
X        if (quietflg)
X            printf("%-22s: ", filename);
X        fprintf(stdout, " Bad CRC %08lx  (should be %08lx)\n", crc32val,
X                lrec.crc32);
X        error = 1;              /* 1:  warning error */
X    `7D else if (tflag) `7B
X        if (!quietflg)
X            fprintf(stdout, " OK\n");
X    `7D else `7B
X        if ((quietflg < 2) && !error)
X            fprintf(stdout, "\n");
X    `7D
X
X    return error;
X
X`7D       /* end function extract_or_test_member() */
X
X
X
X
X
X#ifdef CRYPT
X
X/*******************************/
X/*  Function decrypt_member()  */
X/*******************************/
X
Xstatic int decrypt_member()   /* return 10 if out of memory or can't get */
X`7B                             /*  tty; 1 if password bad; 0 if password OK
V */
X    UWORD b;
X    int n, r;
X    static int nopwd=FALSE;
X    char *m, *prompt;
X    byte h`5B12`5D;
X
X
X    /* get header once */
X    for (n = 0; n < 12; n++) `7B
X        ReadByte(&b);
X        h`5Bn`5D = (byte) b;
X    `7D
X
X    /* if have key already, test it; else allocate memory for it */
X    if (key) `7B
X        if (!testp(h))
X            return 0;      /* existing password OK (else prompt for new) */
X        else if (nopwd)
X            return 1;      /* user indicated no more prompting */
X    `7D else if ((key = (char *)malloc(PWLEN+1)) == (char *)NULL)
X        return 10;
X
X    if ((prompt = (char *)malloc(FILNAMSIZ+15)) != (char *)NULL) `7B
X        sprintf(prompt, "%s password: ", filename);
X        m = prompt;
X    `7D else
X        m = "Enter password: ";
X
X    /* try a few keys */
X    for (r = 0;  r < 3;  ++r) `7B
X        m = getp(m, key, PWLEN+1);
X        if (prompt != (char *)NULL) `7B
X            free(prompt);
X            prompt = (char *)NULL;
X        `7D
X        if (m == (char *)NULL)
X            return 10;
X        if (!testp(h))
X            return 0;
X        if (*key == '\0') `7B
X            nopwd = TRUE;
X            return 1;
X        `7D
X        m = "password incorrect--reenter: ";
X    `7D
X    return 1;
X`7D
X
X
X
X
X
X/**********************/
X/*  Function testp()  */
X/**********************/
X
Xstatic int testp(h)   /* return -1 if bad password; 0 if OK */
X    byte *h;
X`7B
X    UWORD b, c;
X    int n, t;
X    byte *p;
X
X    /* set keys */
X    init_keys(key);
X
X    /* check password */
X    for (n = 0; n < 11; n++)
X        c = DECRYPT(h`5Bn`5D);
X    b = DECRYPT(h`5B11`5D);
X
X#ifdef CRYPT_DEBUG
X    printf("   lrec.crc = %08lx  crec.crc = %08lx  pInfo->ExtLocHdr = %s\n",
X      lrec.crc32, pInfo->crc, pInfo->ExtLocHdr? "true":"false");
X    printf("   incnt = %d  unzip offset into zipfile = %ld\n", incnt,
X      cur_zipfile_bufstart+(inptr-inbuf));
X    printf("   (c `7C (b<<8)) = %04x  (crc >> 16) = %04x  lrec.time = %04x\n
V",
X      (UWORD)(c `7C (b<<8)), (UWORD)(lrec.crc32 >> 16), lrec.last_mod_file_t
Vime);
X#endif /* CRYPT_DEBUG */
X
+-+-+-+-+-+-+-+-  END  OF PART 2 +-+-+-+-+-+-+-+-
Technical Details
Message-ID: <8009582@MVB.SAIC.COM>
Path: arc1-ca.pugleaf.net!sparky!uunet!elroy.jpl.nasa.gov!ames!network.ucsd.edu!mvb.saic.com!vmsnet-sources

Show Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!sparky!uunet!elroy.jpl.nasa.gov!ames!network.ucsd.edu!mvb.saic.com!vmsnet-sources
From: goathunter@wkuvx1.bitnet
Newsgroups: vmsnet.sources
Subject: Zip v1.9 & UnZip v5.0, part 02/22
Message-ID: <8009582@MVB.SAIC.COM>
Date: Tue, 01 Sep 1992 22:48:37 GMT
Organization: Western Kentucky University, Bowling Green, KY
Lines: 1377
Approved: Mark.Berryman@Mvb.Saic.Com