AOH :: UNZIPSRC.TXT

Unzip source code and documentation



This file contians UNZIP source. The source files, in text form, were 
appended together to form this file.  Each file will need to be separated 
out of this text file.  Files are delinitated by the text line:

                    +---+ Start of file FILNAM.EXT +---+

Both C and Turbo Pascal versions of the source are included here (Please
see the authors notes in UNZIP.DOC re: Pascal source).  The source, as
presented here, was downloaded from CompuServe as an ARC file, deARCed and
stuffed (unmodified in any way) into this text file.  The whole purpose
being to give those of us who run "funny" machines or operating systems a      
starting point for porting UNZIP to our enviroments.

Have fun!

                                        June 24, 1990
                                        CAE3 aka Land'o'Oz


+---+ Start of file UNZIP.DOC +---+
  
                                   UnZip
  
                           Version 1.2, 03-15-89
  
                           Zipfile Extract Utility
  
                                 UnZip12.ZIP
  
          Copyright (C) 1989 Samuel H. Smith;  ALL RIGHTS RESERVED
  
  
'UnZip' is a small Zipfile extract utility.   It is written to be as
small and portable as possible -- ideal as a starting point for handling
.ZIP files in non-IBM environments.

Source code is provided in C and Turbo Pascal.  If you port this program
to a non-IBM system, I would appreciate a copy of the ported source and
exec files.


Usage
-----
   unzip filename[.zip]


Revision history
----------------

3-3-89
   Initial alpha test release.

3-5-89
   First fully operational release.  Does not implement CRC verification,
   but should correctly unzip all compression methods.

3-6-89
   Corrected end-of-file detection in both shrink and reduce expanders.
   Resulting files should now always have the correct size.  Added ".ZIP"
   default to unzip.c to match calling conventions of unz.pas.

3-8-89
   Moved machine and operating-system specific code to a block starting
   around line 180.  Added code to swap bytes on host machines that
   store the high order bytes in lower address locations than the low
   order bytes.

3-15-89
   Added CRC checking in UNZIP.C.  Speeded operation by about 150%.

   I'm spending my time on the C version now and don't plan to do any
   further work on the pascal.  If you're using the pascal version
   please contact me.


LICENSE
=======

You may copy and distribute this program freely, provided that:
    1)   No fee is charged for such copying and distribution, and
    2)   It is distributed ONLY in its original, unmodified state.

If you wish to distribute a modified version of this program, you MUST
include the source code.

If you modify this program, I would appreciate a copy of the new source
code.  I am holding the copyright on the source code, so please don't
delete my name from the program files or from the documentation.


SUPPORT
=======

I work very hard to produce a software package of the highest quality
and functionality.  I try to look into all reported bugs, and will
generally fix reported problems within a few days.

IN NO EVENT WILL I BE LIABLE TO YOU FOR ANY DAMAGES, INCLUDING ANY LOST
PROFITS, LOST SAVINGS OR OTHER INCIDENTAL OR CONSEQUENTIAL DAMAGES
ARISING OUT OF YOUR USE OR INABILITY TO USE THE PROGRAM, OR FOR ANY
CLAIM BY ANY OTHER PARTY.

If you have any questions, bugs, or suggestions, please contact me at:

                           The Tool Shop BBS
                       (602) 264-3969 (HST 9600)
                       (602) 279-2673 (HAYES 9600)

The latest version is always available for downloading.

Enjoy!     Samuel H. Smith
           Author and Sysop of The Tool Shop.
   
+---+ Start of file CC.BAT +---+
tcc -B -y -mc -c -f- crc32.c
tcc -O -Z -G -M -y -mc -f- %1 unzip crc32.obj
csc unzip

+---+ Start of file CRC32.C +---+

  /* ============================================================= */
  /*  COPYRIGHT (C) 1986 Gary S. Brown.  You may use this program, or       */
  /*  code or tables extracted from it, as desired without restriction.     */
  /*                                                                        */
  /*  First, the polynomial itself and its table of feedback terms.  The    */
  /*  polynomial is                                                         */
  /*  X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0   */
  /*                                                                        */
  /*  Note that we take it "backwards" and put the highest-order term in    */
  /*  the lowest-order bit.  The X^32 term is "implied"; the LSB is the     */
  /*  X^31 term, etc.  The X^0 term (usually shown as "+1") results in      */
  /*  the MSB being 1.                                                      */
  /*                                                                        */
  /*  Note that the usual hardware shift register implementation, which     */
  /*  is what we're using (we're merely optimizing it by doing eight-bit    */
  /*  chunks at a time) shifts bits into the lowest-order term.  In our     */
  /*  implementation, that means shifting towards the right.  Why do we     */
  /*  do it this way?  Because the calculated CRC must be transmitted in    */
  /*  order from highest-order term to lowest-order term.  UARTs transmit   */
  /*  characters in order from LSB to MSB.  By storing the CRC this way,    */
  /*  we hand it to the UART in the order low-byte to high-byte; the UART   */
  /*  sends each low-bit to hight-bit; and the result is transmission bit   */
  /*  by bit from highest- to lowest-order term without requiring any bit   */
  /*  shuffling on our part.  Reception works similarly.                    */
  /*                                                                        */
  /*  The feedback terms table consists of 256, 32-bit entries.  Notes:     */
  /*                                                                        */
  /*      The table can be generated at runtime if desired; code to do so   */
  /*      is shown later.  It might not be obvious, but the feedback        */
  /*      terms simply represent the results of eight shift/xor opera-      */
  /*      tions for all combinations of data and CRC register values.       */
  /*                                                                        */
  /*      The values must be right-shifted by eight bits by the "updcrc"    */
  /*      logic; the shift must be unsigned (bring in zeroes).  On some     */
  /*      hardware you could probably optimize the shift in assembler by    */
  /*      using byte-swap instructions.                                     */
  /*      polynomial $edb88320                                              */
  /*                                                                        */
  /*  --------------------------------------------------------------------  */

long crc_32_tab[] = {
      0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
      0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
      0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
      0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
      0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
      0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
      0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
      0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
      0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
      0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
      0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
      0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
      0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
      0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
      0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
      0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
      0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
      0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
      0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
      0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
      0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
      0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
      0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
      0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
      0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
      0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
      0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
      0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
      0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
      0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
      0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
      0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
      0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
      0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
      0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
      0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
      0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
      0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
      0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
      0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
      0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
      0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
      0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
      0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
      0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
      0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
      0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
      0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
      0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
      0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
      0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
      0x2d02ef8dL
   };

#define UPDCRC32(res,oct) res=crc_32_tab[(byte)res^(byte)oct] ^ ((res>>8) & 0x00FFFFFFL)

/*
 * macro UPDCRC32(res,oct)
 *  res=crc_32_tab[(byte)res ^ (byte)oct] ^ ((res >> 8) & 0x00FFFFFFL)
 *
 */

/* ------------------------------------------------------------- */

/*
 * Inline assembly version of 32 bit CRC calculation
 * Copyright 1989 Samuel H. Smith
 *
 * You may use this program, or code extracted from it,
 * as desired without restriction.
 *
 */

extern long crc32val;

void UpdateCRC(unsigned char *s,
               register int len)
 /* update running CRC calculation with contents of a buffer */
{

#ifdef HIGH_LOW
        while (len--) {
                UPDCRC32(crc32val, *s++);
        }
#else
        asm     push cx
        asm     push si

        asm     mov cx,len

        asm     les si,s

        asm     mov dx,crc32val+2
        asm     mov ax,crc32val

crcNext:
        asm     mov bh,al       /* save crc32val[0] */

        asm     mov al,ah       /* (crc32 >> 8) & 0x00ffffff */
        asm     mov ah,dl
        asm     mov dl,dh
        asm     xor dh,dh

        asm     mov bl,es:[si]
        asm     inc si

        asm     xor bl,bh       /* crcval[0] */

        asm     xor bh,bh
        asm     shl bx,1
        asm     shl bx,1
        asm     xor ax,crc_32_tab[bx]
        asm     xor dx,crc_32_tab[bx+2]

        asm     loop crcNext

        asm     mov crc32val+2,dx
        asm     mov crc32val,ax
        asm     pop si
        asm     pop cx
#endif
}


+---+ Start of file CRC32.H +---+

long crc32val;

void UpdateCRC(unsigned char *s,
               register int len);

 /* update running CRC calculation with contents of a buffer */

+---+ Start of file UNZIP.C +---+

/*
 * Copyright 1989 Samuel H. Smith;  All rights reserved
 *
 * Do not distribute modified versions without my permission.
 * Do not remove or alter this notice or any other copyright notice.
 * If you use this in your own program you must distribute source code.
 * Do not use any of this in a commercial product.
 *
 */

/*
 * UnZip - A simple zipfile extract utility
 *
 * To compile:
 *      tcc -B -O -Z -G -mc unzip.c        ;turbo C 2.0, compact model
 *
 */

#define VERSION  "UnZip:  Zipfile Extract v1.2 of 03-15-89;  (C) 1989 Samuel H. Smith"

typedef unsigned char byte;   /* code assumes UNSIGNED bytes */
typedef long longint;
typedef unsigned word;
typedef char boolean;

#define STRSIZ 256

#include <stdio.h>
 /* this is your standard header for all C compiles */

#include <stdlib.h>
 /* this include defines various standard library prototypes */


/*
 * SEE HOST OPERATING SYSTEM SPECIFICS SECTION STARTING NEAR LINE 180
 *
 */


/* ----------------------------------------------------------- */
/*
 * Zipfile layout declarations
 *
 */

typedef longint signature_type;


#define LOCAL_FILE_HEADER_SIGNATURE  0x04034b50L


typedef struct local_file_header {
     word version_needed_to_extract;
     word general_purpose_bit_flag;
     word compression_method;
     word last_mod_file_time;
     word last_mod_file_date;
     longint crc32;
     longint compressed_size;
     longint uncompressed_size;
     word filename_length;
     word extra_field_length;
} local_file_header;


#define CENTRAL_FILE_HEADER_SIGNATURE  0x02014b50L


typedef struct central_directory_file_header {
     word version_made_by;
     word version_needed_to_extract;
     word general_purpose_bit_flag;
     word compression_method;
     word last_mod_file_time;
     word last_mod_file_date;
     longint crc32;
     longint compressed_size;
     longint uncompressed_size;
     word filename_length;
     word extra_field_length;
     word file_comment_length;
     word disk_number_start;
     word internal_file_attributes;
     longint external_file_attributes;
     longint relative_offset_local_header;
} central_directory_file_header;


#define END_CENTRAL_DIR_SIGNATURE  0x06054b50L


typedef struct end_central_dir_record {
     word number_this_disk;
     word number_disk_with_start_central_directory;
     word total_entries_central_dir_on_this_disk;
     word total_entries_central_dir;
     longint size_central_directory;
     longint offset_start_central_directory;
     word zipfile_comment_length;
} end_central_dir_record;



/* ----------------------------------------------------------- */
/*
 * input file variables
 *
 */

#define INBUFSIZ 0x2000
byte *inbuf;             /* input file buffer - any size is legal */
byte *inptr;

int incnt;
unsigned bitbuf;
int bits_left;
boolean zipeof;

int zipfd;
char zipfn[STRSIZ];
local_file_header lrec;


/* ----------------------------------------------------------- */
/*
 * output stream variables
 *
 */

#define OUTBUFSIZ 0x6000
byte *outbuf;                   /* buffer for rle look-back */
byte *outptr;

longint outpos;               /* absolute position in outfile */
int outcnt;              /* current position in outbuf */

int outfd;
char filename[STRSIZ];
char extra[STRSIZ];

#define DLE 144


/* ----------------------------------------------------------- */
/*
 * shrink/reduce working storage
 *
 */

int factor;
byte followers[256][64];
byte Slen[256];

#define max_bits 13
#define init_bits 9
#define hsize 8192
#define first_ent 257
#define clear 256

typedef int hsize_array_integer[hsize+1];
typedef byte hsize_array_byte[hsize+1];

hsize_array_integer prefix_of;
hsize_array_byte suffix_of;
hsize_array_byte stack;

int codesize;
int maxcode;
int free_ent;
int maxcodemax;
int offset;
int sizex;



/* ============================================================= */
/*
 * Host operating system details
 *
 */

#include <string.h>
 /* this include defines strcpy, strcmp, etc. */

#include <io.h>
 /*
  * this include file defines
  *             struct ftime ...        (* file time/date stamp info *)
  *             int setftime (int handle, struct ftime *ftimep);
  *             #define SEEK_CUR  1     (* lseek() modes *)
  *             #define SEEK_END  2
  *             #define SEEK_SET  0
  */

#include <fcntl.h>
 /*
  * this include file defines
  *             #define O_BINARY 0x8000  (* no cr-lf translation *)
  * as used in the open() standard function
  */

#include <sys/stat.h>
 /*
  * this include file defines
  *             #define S_IREAD 0x0100  (* owner may read *)
  *             #define S_IWRITE 0x0080 (* owner may write *)
  * as used in the creat() standard function
  */

#undef HIGH_LOW
 /*
  * change 'undef' to 'define' if your machine stores high order bytes in
  * lower addresses.
  */

void set_file_time(void)
 /*
  * set the output file date/time stamp according to information from the
  * zipfile directory record for this file 
  */
{
     union {
                struct ftime ft;        /* system file time record */
          struct {
                        word ztime;     /* date and time words */
                        word zdate;     /* .. same format as in .ZIP file */
          } zt;
     } td;

     /*
      * set output file date and time - this is optional and can be
      * deleted if your compiler does not easily support setftime() 
      */

     td.zt.ztime = lrec.last_mod_file_time;
     td.zt.zdate = lrec.last_mod_file_date;

     setftime(outfd, &td.ft);
}


int create_output_file(void)
 /* return non-0 if creat failed */
{
     /* create the output file with READ and WRITE permissions */
     outfd = creat(filename, S_IWRITE | S_IREAD);
     if (outfd < 1) {
          printf("Can't create output: %s\n", filename);
          return 1;
     }

     /*
      * close the newly created file and reopen it in BINARY mode to
      * disable all CR/LF translations 
      */
     close(outfd);
     outfd = open(filename, O_RDWR | O_BINARY);

     /* write a single byte at EOF to pre-allocate the file */
        lseek(outfd, lrec.uncompressed_size - 1L, SEEK_SET);
     write(outfd, "?", 1);
     lseek(outfd, 0L, SEEK_SET);
     return 0;
}


int open_input_file(void)
 /* return non-0 if creat failed */
{
     /*
      * open the zipfile for reading and in BINARY mode to prevent cr/lf
      * translation, which would corrupt the bitstreams 
      */

     zipfd = open(zipfn, O_RDONLY | O_BINARY);
     if (zipfd < 1) {
          printf("Can't open input file: %s\n", zipfn);
          return (1);
     }
     return 0;
}


#ifdef HIGH_LOW

void swap_bytes(word *wordp)
 /* convert intel style 'short int' variable to host format */
{
     char *charp = (char *) wordp;
     char temp;

     temp = charp[0];
     charp[0] = charp[1];
     charp[1] = temp;
}

void swap_lbytes(longint *longp)
 /* convert intel style 'long' variable to host format */
{
     char *charp = (char *) longp;
     char temp[4];

     temp[3] = charp[0];
     temp[2] = charp[1];
     temp[1] = charp[2];
     temp[0] = charp[3];

     charp[0] = temp[0];
     charp[1] = temp[1];
     charp[2] = temp[2];
     charp[3] = temp[3];
}

#endif



/* ============================================================= */

int FillBuffer(void)
 /* fill input buffer if possible */
{
     int readsize;

        if (lrec.compressed_size <= 0)
          return incnt = 0;

        if (lrec.compressed_size > INBUFSIZ)
          readsize = INBUFSIZ;
     else
                readsize = (int) lrec.compressed_size;
     incnt = read(zipfd, inbuf, readsize);

        lrec.compressed_size -= incnt;
     inptr = inbuf;
     return incnt--;
}

int ReadByte(unsigned *x)
 /* read a byte; return 8 if byte available, 0 if not */
{
     if (incnt-- == 0)
          if (FillBuffer() == 0)
               return 0;

     *x = *inptr++;
     return 8;
}


/* ------------------------------------------------------------- */
static unsigned mask_bits[] =
        {0,     0x0001, 0x0003, 0x0007, 0x000f,
                0x001f, 0x003f, 0x007f, 0x00ff,
                0x01ff, 0x03ff, 0x07ff, 0x0fff,
                0x1fff, 0x3fff, 0x7fff, 0xffff
        };


int FillBitBuffer(register int bits)
{
     /* get the bits that are left and read the next word */
     unsigned temp;
        register int result = bitbuf;
     int sbits = bits_left;
     bits -= bits_left;

     /* read next word of input */
     bits_left = ReadByte(&bitbuf);
     bits_left += ReadByte(&temp);
     bitbuf |= (temp << 8);
     if (bits_left == 0)
          zipeof = 1;

     /* get the remaining bits */
        result = result | (int) ((bitbuf & mask_bits[bits]) << sbits);
        bitbuf >>= bits;
        bits_left -= bits;
        return result;
}

#define READBIT(nbits,zdest) { if (nbits <= bits_left) { zdest = (int)(bitbuf & mask_bits[nbits]); bitbuf >>= nbits; bits_left -= nbits; } else zdest = FillBitBuffer(nbits);}

/*
 * macro READBIT(nbits,zdest)
 *  {
 *      if (nbits <= bits_left) {
 *          zdest = (int)(bitbuf & mask_bits[nbits]);
 *          bitbuf >>= nbits;
 *          bits_left -= nbits;
 *      } else
 *          zdest = FillBitBuffer(nbits);
 *  }
 *
 */


/* ------------------------------------------------------------- */

#include "crc32.h"


/* ------------------------------------------------------------- */

void FlushOutput(void)
 /* flush contents of output buffer */
{
     UpdateCRC(outbuf, outcnt);
     write(outfd, outbuf, outcnt);
     outpos += outcnt;
     outcnt = 0;
     outptr = outbuf;
}

#define OUTB(intc) { *outptr++=intc; if (++outcnt==OUTBUFSIZ) FlushOutput(); }

/*
 *  macro OUTB(intc)
 *  {
 *      *outptr++=intc;
 *      if (++outcnt==OUTBUFSIZ)
 *          FlushOutput();
 *  }
 *
 */


/* ----------------------------------------------------------- */

void LoadFollowers(void)
{
        register int x;
        register int i;

     for (x = 255; x >= 0; x--) {
                READBIT(6,Slen[x]);
          for (i = 0; i < Slen[x]; i++) {
                        READBIT(8,followers[x][i]);
          }
     }
}


/* ----------------------------------------------------------- */
/*
 * The Reducing algorithm is actually a combination of two
 * distinct algorithms.  The first algorithm compresses repeated
 * byte sequences, and the second algorithm takes the compressed
 * stream from the first algorithm and applies a probabilistic
 * compression method.
 */

int L_table[] = {0, 0x7f, 0x3f, 0x1f, 0x0f};

int D_shift[] = {0, 0x07, 0x06, 0x05, 0x04};
int D_mask[]  = {0, 0x01, 0x03, 0x07, 0x0f};

int B_table[] = {8, 1, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5,
           5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6,
           6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
           6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7,
           7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
           7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
           7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
           7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
           8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
           8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
           8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
           8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
           8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
           8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
           8, 8, 8, 8};

/* ----------------------------------------------------------- */

void unReduce(void)
 /* expand probablisticly reduced data */
{
        register int lchar;
        int nchar;
        int ExState;
        int V;
        int Len;

        factor = lrec.compression_method - 1;
     ExState = 0;
     lchar = 0;
     LoadFollowers();

        while (((outpos + outcnt) < lrec.uncompressed_size) && (!zipeof)) {
          if (Slen[lchar] == 0)
                        READBIT(8,nchar)      /* ; */
                else
          {
                        READBIT(1,nchar);
                        if (nchar != 0)
                                READBIT(8,nchar)      /* ; */
                        else
               {
                                int follower;
                                int bitsneeded = B_table[Slen[lchar]];
                                READBIT(bitsneeded,follower);
                                nchar = followers[lchar][follower];
               }
          }

          /* expand the resulting byte */
          switch (ExState) {

          case 0:
                        if (nchar != DLE)
                                OUTB(nchar) /*;*/
               else
                    ExState = 1;
               break;

          case 1:
                        if (nchar != 0) {
                                V = nchar;
                    Len = V & L_table[factor];
                    if (Len == L_table[factor])
                         ExState = 2;
                    else
                         ExState = 3;
               }
               else {
                                OUTB(DLE);
                    ExState = 0;
               }
               break;

                case 2: {
                                Len += nchar;
                    ExState = 3;
               }
               break;

                case 3: {
                    register int i = Len + 3;
                    int offset = (((V >> D_shift[factor]) &
                                          D_mask[factor]) << 8) + nchar + 1;
                    longint op = outpos + outcnt - offset;

                    /* special case- before start of file */
                    while ((op < 0L) && (i > 0)) {
                         OUTB(0);
                         op++;
                         i--;
                    }

                    /* normal copy of data from output buffer */
                    {
                         register int ix = (int) (op % OUTBUFSIZ);

                                        /* do a block memory copy if possible */
                                        if ( ((ix    +i) < OUTBUFSIZ) &&
                                             ((outcnt+i) < OUTBUFSIZ) ) {
                                                memcpy(outptr,&outbuf[ix],i);
                                                outptr += i;
                                                outcnt += i;
                                        }

                                        /* otherwise copy byte by byte */
                                        else while (i--) {
                                                OUTB(outbuf[ix]);
                                                if (++ix >= OUTBUFSIZ)
                                                        ix = 0;
                                        }
                                }

                    ExState = 0;
               }
               break;
          }

                /* store character for next iteration */
                lchar = nchar;
        }
}


/* ------------------------------------------------------------- */
/*
 * Shrinking is a Dynamic Ziv-Lempel-Welch compression algorithm
 * with partial clearing.
 *
 */

void partial_clear(void)
{
        register int pr;
        register int cd;

     /* mark all nodes as potentially unused */
     for (cd = first_ent; cd < free_ent; cd++)
          prefix_of[cd] |= 0x8000;

     /* unmark those that are used by other nodes */
     for (cd = first_ent; cd < free_ent; cd++) {
          pr = prefix_of[cd] & 0x7fff;  /* reference to another node? */
                if (pr >= first_ent)            /* flag node as referenced */
               prefix_of[pr] &= 0x7fff;
     }

     /* clear the ones that are still marked */
     for (cd = first_ent; cd < free_ent; cd++)
          if ((prefix_of[cd] & 0x8000) != 0)
               prefix_of[cd] = -1;

     /* find first cleared node as next free_ent */
        cd = first_ent;
        while ((cd < maxcodemax) && (prefix_of[cd] != -1))
                cd++;
        free_ent = cd;
}


/* ------------------------------------------------------------- */

void unShrink(void)
{
        #define  GetCode(dest) READBIT(codesize,dest)

     register int code;
     register int stackp;
     int finchar;
     int oldcode;
     int incode;


     /* decompress the file */
     maxcodemax = 1 << max_bits;
     codesize = init_bits;
     maxcode = (1 << codesize) - 1;
     free_ent = first_ent;
     offset = 0;
     sizex = 0;

     for (code = maxcodemax; code > 255; code--)
          prefix_of[code] = -1;

     for (code = 255; code >= 0; code--) {
          prefix_of[code] = 0;
          suffix_of[code] = code;
     }

     GetCode(oldcode);
     if (zipeof)
          return;
     finchar = oldcode;

        OUTB(finchar);

        stackp = hsize;

     while (!zipeof) {
          GetCode(code);
          if (zipeof)
               return;

          while (code == clear) {
               GetCode(code);
               switch (code) {

               case 1:{
                         codesize++;
                         if (codesize == max_bits)
                              maxcode = maxcodemax;
                         else
                              maxcode = (1 << codesize) - 1;
                    }
                    break;

               case 2:
                    partial_clear();
                    break;
               }

               GetCode(code);
               if (zipeof)
                    return;
          }


          /* special case for KwKwK string */
          incode = code;
          if (prefix_of[code] == -1) {
                        stack[--stackp] = finchar;
               code = oldcode;
          }


          /* generate output characters in reverse order */
          while (code >= first_ent) {
                        stack[--stackp] = suffix_of[code];
               code = prefix_of[code];
          }

          finchar = suffix_of[code];
                stack[--stackp] = finchar;


                /* and put them out in forward order, block copy */
                if ((hsize-stackp+outcnt) < OUTBUFSIZ) {
                        memcpy(outptr,&stack[stackp],hsize-stackp);
                        outptr += hsize-stackp;
                        outcnt += hsize-stackp;
                        stackp = hsize;
                }

                /* output byte by byte if we can't go by blocks */
                else while (stackp < hsize)
                        OUTB(stack[stackp++]);


          /* generate new entry */
          code = free_ent;
          if (code < maxcodemax) {
               prefix_of[code] = oldcode;
               suffix_of[code] = finchar;

               do
                    code++;
               while ((code < maxcodemax) && (prefix_of[code] != -1));

               free_ent = code;
          }

          /* remember previous code */
          oldcode = incode;
     }

}


/* ---------------------------------------------------------- */

void extract_member(void)
{
     unsigned b;

     bits_left = 0;
     bitbuf = 0;
     incnt = 0;
     outpos = 0L;
     outcnt = 0;
     outptr = outbuf;
     zipeof = 0;
     crc32val = 0xFFFFFFFFL;


     /* create the output file with READ and WRITE permissions */
     if (create_output_file())
          exit(1);

        switch (lrec.compression_method) {

     case 0:        /* stored */
          {
               printf(" Extracting: %-12s ", filename);
               while (ReadByte(&b))
                    OUTB(b);
          }
          break;

        case 1: {
               printf("UnShrinking: %-12s ", filename);
               unShrink();
          }
          break;

     case 2:
     case 3:
     case 4:
        case 5: {
               printf("  Expanding: %-12s ", filename);
               unReduce();
          }
          break;

     default:
          printf("Unknown compression method.");
     }


     /* write the last partial buffer, if any */
     if (outcnt > 0) {
          UpdateCRC(outbuf, outcnt);
          write(outfd, outbuf, outcnt);
     }

     /* set output file date and time */
     set_file_time();

     close(outfd);

     crc32val = -1 - crc32val;
        if (crc32val != lrec.crc32)
                printf(" Bad CRC %08lx  (should be %08lx)", lrec.crc32, crc32val);

     printf("\n");
}


/* ---------------------------------------------------------- */

void get_string(int len,
                char *s)
{
     read(zipfd, s, len);
     s[len] = 0;
}


/* ---------------------------------------------------------- */

void process_local_file_header(void)
{
     read(zipfd, &lrec, sizeof(lrec));

#ifdef HIGH_LOW
     swap_bytes(&lrec.filename_length);
     swap_bytes(&lrec.extra_field_length);
     swap_lbytes(&lrec.compressed_size);
     swap_lbytes(&lrec.uncompressed_size);
     swap_bytes(&lrec.compression_method);
#endif

     get_string(lrec.filename_length, filename);
     get_string(lrec.extra_field_length, extra);
     extract_member();
}


/* ---------------------------------------------------------- */

void process_central_file_header(void)
{
     central_directory_file_header rec;
     char filename[STRSIZ];
     char extra[STRSIZ];
     char comment[STRSIZ];

     read(zipfd, &rec, sizeof(rec));

#ifdef HIGH_LOW
     swap_bytes(&rec.filename_length);
     swap_bytes(&rec.extra_field_length);
     swap_bytes(&rec.file_comment_length);
#endif

        get_string(rec.filename_length, filename);
     get_string(rec.extra_field_length, extra);
     get_string(rec.file_comment_length, comment);
}


/* ---------------------------------------------------------- */

void process_end_central_dir(void)
{
     end_central_dir_record rec;
     char comment[STRSIZ];

     read(zipfd, &rec, sizeof(rec));

#ifdef HIGH_LOW
     swap_bytes(&rec.zipfile_comment_length);
#endif

     get_string(rec.zipfile_comment_length, comment);
}


/* ---------------------------------------------------------- */

void process_headers(void)
{
     longint sig;

     while (1) {
          if (read(zipfd, &sig, sizeof(sig)) != sizeof(sig))
               return;

#ifdef HIGH_LOW
          swap_lbytes(&sig);
#endif

                if (sig == LOCAL_FILE_HEADER_SIGNATURE)
               process_local_file_header();
                else if (sig == CENTRAL_FILE_HEADER_SIGNATURE)
               process_central_file_header();
                else if (sig == END_CENTRAL_DIR_SIGNATURE) {
               process_end_central_dir();
               return;
          }
                else {
               printf("Invalid Zipfile Header\n");
               return;
          }
     }

}


/* ---------------------------------------------------------- */

void extract_zipfile(void)
{
     /*
      * open the zipfile for reading and in BINARY mode to prevent cr/lf
      * translation, which would corrupt the bitstreams 
      */

     if (open_input_file())
          exit(1);

     process_headers();

     close(zipfd);
}


/* ---------------------------------------------------------- */
/*
 * main program
 *
 */

void main(int argc, char **argv)
{
     if (argc != 2) {
                printf("\n%s\nCourtesy of:  S.H.Smith  and  The Tool Shop BBS,  (602) 279-2673.\n\n",VERSION);
          printf("You may copy and distribute this program freely, provided that:\n");
          printf("    1)   No fee is charged for such copying and distribution, and\n");
          printf("    2)   It is distributed ONLY in its original, unmodified state.\n\n");
          printf("If you wish to distribute a modified version of this program, you MUST\n");
          printf("include the source code.\n\n");
          printf("If you modify this program, I would appreciate a copy of the  new source\n");
          printf("code.   I am holding the copyright on the source code, so please don't\n");
          printf("delete my name from the program files or from the documentation.\n\n");
                printf("IN NO EVENT WILL I BE LIABLE TO YOU FOR ANY DAMAGES, INCLUDING ANY LOST\n");
                printf("PROFITS, LOST SAVINGS OR OTHER INCIDENTAL OR CONSEQUENTIAL DAMAGES\n");
                printf("ARISING OUT OF YOUR USE OR INABILITY TO USE THE PROGRAM, OR FOR ANY\n");
                printf("CLAIM BY ANY OTHER PARTY.\n\n");
                printf("Usage:  UnZip FILE[.zip]\n");
                exit(1);
     }

     /* .ZIP default if none provided by user */
     strcpy(zipfn, argv[1]);
     if (strchr(zipfn, '.') == NULL)
          strcat(zipfn, ".ZIP");

        /* allocate i/o buffers */
     inbuf = (byte *) (malloc(INBUFSIZ));
     outbuf = (byte *) (malloc(OUTBUFSIZ));
     if ((inbuf == NULL) || (outbuf == NULL)) {
          printf("Can't allocate buffers!\n");
          exit(1);
     }

        /* do the job... */
        extract_zipfile();
     exit(0);
}

+---+ Start of file MDOSIO.PAS +---+

(*
 * Copyright 1987, 1989 Samuel H. Smith;  All rights reserved
 *
 * This is a component of the ProDoor System.
 * Do not distribute modified versions without my permission.
 * Do not remove or alter this notice or any other copyright notice.
 * If you use this in your own program you must distribute source code.
 * Do not use any of this in a commercial product.
 *
 *)

(*
 * mdosio - Mini library for interface to DOS v3 file access functions
 *
 *)

{$i prodef.inc}

unit MDosIO;

interface

   uses Dos;

   type
      dos_filename = string[64];
      dos_handle   = word;

      long_integer = record
         lsw: word;
         msw: word;
      end;

      seek_modes = (seek_start {0},
                    seek_cur   {1},
                    seek_end   {2});

      open_modes = (open_read  {h40},     {deny_nothing, allow_read}
                    open_write {h41},     {deny_nothing, allow_write}
                    open_update{h42});    {deny_nothing, allow_read+write}

      dos_time_functions = (time_get,
                            time_set);

   const
      dos_error    = $FFFF; {file handle after an error}

   var
      dos_regs:     registers;
      dos_name:     dos_filename;


   procedure dos_call;

   function dos_open(name:      dos_filename;
                     mode:      open_modes):  dos_handle;

   function dos_create(name:    dos_filename): dos_handle;

   function dos_read( handle:   dos_handle;
                      var       buffer;
                      bytes:    word): word;

   procedure dos_write(handle:  dos_handle;
                       var      buffer;
                       bytes:   word);

   function dos_write_failed:   boolean;

   procedure dos_lseek(handle:  dos_handle;
                       offset:  longint;
                       method:  seek_modes);

   procedure dos_rseek(handle:  dos_handle;
                       recnum:  word;
                       recsiz:  word;
                       method:  seek_modes);

   function dos_tell: longint;

   procedure dos_find_eof(fd:   dos_handle);

   procedure dos_close(handle:  dos_handle);

   procedure dos_unlink(name:   dos_filename);

   procedure dos_file_times(fd:       dos_handle;
                            func:     dos_time_functions;
                            var time: word;
                            var date: word);

   function dos_jdate(time,date: word): longint;

   function dos_exists(name: dos_filename): boolean;


implementation

(* -------------------------------------------------------- *)
procedure dos_call;
var
   msg:  string;
begin
   msdos(dos_regs);

   if (dos_regs.flags and Fcarry) <> 0 then
   begin
      case dos_regs.ax of
         2:   msg := 'file not found';
         3:   msg := 'dir not found';
         4:   msg := 'too many open files';
         5:   msg := 'access denied';
         else str(dos_regs.ax,msg);
      end;
{$I-}
      writeln(' DOS error [',msg,'] on file [',dos_name,'] ');
{$i+}
      dos_regs.ax := dos_error;
   end;
end;


(* -------------------------------------------------------- *)
procedure prepare_dos_name(name: dos_filename);
begin
   while (name <> '') and (name[length(name)] <= ' ') do
      dec(name[0]);
   if name = '' then
      name := 'Nul';
   dos_name := name;
   dos_name[length(dos_name)+1] := #0;
   dos_regs.ds := seg(dos_name);
   dos_regs.dx := ofs(dos_name)+1;
end;


(* -------------------------------------------------------- *)
function dos_open(name:    dos_filename;
                  mode:    open_modes):  dos_handle;
var
   try: integer;
const
   retry_count = 3;

begin
   for try := 1 to retry_count do
   begin
      dos_regs.ax := $3d40 + ord(mode);
      prepare_dos_name(name);
      msdos(dos_regs);

      if (dos_regs.flags and Fcarry) = 0 then
      begin
         dos_open := dos_regs.ax;
         exit;
      end;
   end;

   dos_open := dos_error;
end;


(* -------------------------------------------------------- *)
function dos_create(name:    dos_filename): dos_handle;
begin
   dos_regs.ax := $3c00;
   prepare_dos_name(name);
   dos_regs.cx := 0;   {attrib}
   dos_call;
   dos_create := dos_regs.ax;
end;


(* -------------------------------------------------------- *)
function dos_read( handle:  dos_handle;
                   var      buffer;
                   bytes:   word): word;
begin
   dos_regs.ax := $3f00;
   dos_regs.bx := handle;
   dos_regs.cx := bytes;
   dos_regs.ds := seg(buffer);
   dos_regs.dx := ofs(buffer);
   dos_call;
   dos_read := dos_regs.ax;
end;


(* -------------------------------------------------------- *)
procedure dos_write(handle:  dos_handle;
                    var      buffer;
                    bytes:   word);
begin
   dos_regs.ax := $4000;
   dos_regs.bx := handle;
   dos_regs.cx := bytes;
   dos_regs.ds := seg(buffer);
   dos_regs.dx := ofs(buffer);
   dos_call;
   dos_regs.cx := bytes;
end;

function dos_write_failed: boolean;
begin
   dos_write_failed := dos_regs.ax <> dos_regs.cx;
end;


(* -------------------------------------------------------- *)
procedure dos_lseek(handle:  dos_handle;
                    offset:  longint;
                    method:  seek_modes);
var
   pos:  long_integer absolute offset;

begin
   dos_regs.ax := $4200 + ord(method);
   dos_regs.bx := handle;
   dos_regs.cx := pos.msw;
   dos_regs.dx := pos.lsw;
   dos_call;
end;


(* -------------------------------------------------------- *)
procedure dos_rseek(handle:  dos_handle;
                    recnum:  word;
                    recsiz:  word;
                    method:  seek_modes);
var
   offset: longint;
   pos:    long_integer absolute offset;

begin
   offset := longint(recnum) * longint(recsiz);
   dos_regs.ax := $4200 + ord(method);
   dos_regs.bx := handle;
   dos_regs.cx := pos.msw;
   dos_regs.dx := pos.lsw;
   dos_call;
end;


(* -------------------------------------------------------- *)
function dos_tell: longint;
  {call immediately after dos_lseek or dos_rseek}
var
   pos:  long_integer;
   li:   longint absolute pos;
begin
   pos.lsw := dos_regs.ax;
   pos.msw := dos_regs.dx;
   dos_tell := li;
end;


(* -------------------------------------------------------- *)
procedure dos_find_eof(fd: dos_handle);
   {find end of file, skip backward over ^Z eof markers}
var
   b: char;
   n: word;
   i: word;
   p: longint;
   temp: array[1..128] of char;

begin
   dos_lseek(fd,0,seek_end);
   p := dos_tell-1;
   if p < 0 then
      exit;

   p := p and $FFFF80;   {round to last 'sector'}
   {search forward for the eof marker}
   dos_lseek(fd,p,seek_start);
   n := dos_read(fd,temp,sizeof(temp));
   i := 1;

   while (i <= n) and (temp[i] <> ^Z) do
   begin
      inc(i);
      inc(p);
   end;

   {backup to overwrite the eof marker}
   dos_lseek(fd,p,seek_start);
end;


(* -------------------------------------------------------- *)
procedure dos_close(handle:  dos_handle);
begin
   dos_regs.ax := $3e00;
   dos_regs.bx := handle;
   msdos(dos_regs);  {dos_call;}
end;


(* -------------------------------------------------------- *)
procedure dos_unlink(name:    dos_filename);
   {delete a file, no error message if file doesn't exist}
begin
   dos_regs.ax := $4100;
   prepare_dos_name(name);
   msdos(dos_regs);
end;


(* -------------------------------------------------------- *)
procedure dos_file_times(fd:       dos_handle;
                         func:     dos_time_functions;
                         var time: word;
                         var date: word);
begin
   dos_regs.ax := $5700 + ord(func);
   dos_regs.bx := fd;
   dos_regs.cx := time;
   dos_regs.dx := date;
   dos_call;
   time := dos_regs.cx;
   date := dos_regs.dx;
end;


(* -------------------------------------------------------- *)
function dos_jdate(time,date: word): longint;
begin

(***
     write(' d=',date:5,' t=',time:5,' ');
     write('8',   (date shr 9) and 127:1); {year}
     write('/',   (date shr 5) and  15:2); {month}
     write('/',   (date      ) and  31:2); {day}
     write(' ',   (time shr 11) and 31:2); {hour}
     write(':',   (time shr  5) and 63:2); {minute}
     write(':',   (time shl  1) and 63:2); {second}
     writeln(' j=', (longint(date) shl 16) + longint(time));
 ***)

   dos_jdate := (longint(date) shl 16) + longint(time);
end;


(* -------------------------------------------------------- *)
function dos_exists(name: dos_filename): boolean;
var
   DirInfo:     SearchRec;

begin
   prepare_dos_name(name);
   FindFirst(dos_name,$21,DirInfo);
   if (DosError <> 0) then
      dos_exists := false
   else
      dos_exists := true;
end;

end.

+---+ Start of file PRODEF.INC +---+

{$undef DEBUGGING}             (* define to enable test code *)

{$I+}    {I/O checking}
{$N-}    {Numeric coprocessor}
{$V-}    {Relaxes string typing}
{$B-}    {Boolean complete evaluation}
{$S-}    {Stack checking}
{$R-}    {Range checking}
{$D+}    {Global debug information}
{$L+}    {Local debug information}

+---+ Start of file UNZ.PAS +---+

(*
 * Copyright 1987, 1989 Samuel H. Smith;  All rights reserved
 *
 * This is a component of the ProDoor System.
 * Do not distribute modified versions without my permission.
 * Do not remove or alter this notice or any other copyright notice.
 * If you use this in your own program you must distribute source code.
 * Do not use any of this in a commercial product.
 *
 *)

(*
 * UnZip - A simple zipfile extract utility
 *
 *)

{$I+}    {I/O checking}
{$N-}    {Numeric coprocessor}
{$V-}    {Relaxes string typing}
{$B-}    {Boolean complete evaluation}
{$S-}    {Stack checking}
{$R-}    {Range checking}
{$D+}    {Global debug information}
{$L+}    {Local debug information}

{$M 5000,0,0} {minstack,minheap,maxheap}

program UnZip;

Uses
   Dos, Mdosio;

const
   version = 'UnZip:  Zipfile Extract v1.1 of 03-06-89;  (C) 1989 S.H.Smith';



(*
 * ProZip2.int - ZIP file interface library      (2-15-89 shs)
 *
 * Data declarations for the archive text-view functions.
 *
 *)

(* ----------------------------------------------------------- *)
(*
 * ZIPfile layout declarations
 *
 *)

type
   signature_type = longint;

const
   local_file_header_signature = $04034b50;

type
   local_file_header = record
      version_needed_to_extract:    word;
      general_purpose_bit_flag:     word;
      compression_method:           word;
      last_mod_file_time:           word;
      last_mod_file_date:           word;
      crc32:                        longint;
      compressed_size:              longint;
      uncompressed_size:            longint;
      filename_length:              word;
      extra_field_length:           word;
   end;

const
   central_file_header_signature = $02014b50;

type
   central_directory_file_header = record
      version_made_by:                 word;
      version_needed_to_extract:       word;
      general_purpose_bit_flag:        word;
      compression_method:              word;
      last_mod_file_time:              word;
      last_mod_file_date:              word;
      crc32:                           longint;
      compressed_size:                 longint;
      uncompressed_size:               longint;
      filename_length:                 word;
      extra_field_length:              word;
      file_comment_length:             word;
      disk_number_start:               word;
      internal_file_attributes:        word;
      external_file_attributes:        longint;
      relative_offset_local_header:    longint;
   end;

const
   end_central_dir_signature = $06054b50;

type
   end_central_dir_record = record
      number_this_disk:                         word;
      number_disk_with_start_central_directory: word;
      total_entries_central_dir_on_this_disk:   word;
      total_entries_central_dir:                word;
      size_central_directory:                   longint;
      offset_start_central_directory:           longint;
      zipfile_comment_length:                   word;
   end;



(* ----------------------------------------------------------- *)
(*
 * input file variables
 *
 *)

const
   uinbufsize = 512;    {input buffer size}
var
   zipeof:      boolean;
   csize:       longint;
   cusize:      longint;
   cmethod:     integer;
   ctime:       word;
   cdate:       word;
   inbuf:       array[1..uinbufsize] of byte;
   inpos:       integer;
   incnt:       integer;
   pc:          byte;
   pcbits:      byte;
   pcbitv:      byte;
   zipfd:       dos_handle;
   zipfn:       dos_filename;



(* ----------------------------------------------------------- *)
(*
 * output stream variables
 *
 *)

var
   outbuf:      array[0..4096] of byte; {for rle look-back}
   outpos:      longint;                {absolute position in outfile}
   outcnt:      integer;
   outfd:       dos_handle;
   filename:    string;
   extra:       string;



(* ----------------------------------------------------------- *)

type
   Sarray = array[0..255] of string[64];

var
   factor:     integer;
   followers:  Sarray;
   ExState:    integer;
   C:          integer;
   V:          integer;
   Len:        integer;

const
   hsize =     8192;

type
   hsize_array_integer = array[0..hsize] of integer;
   hsize_array_byte    = array[0..hsize] of byte;

var
   prefix_of:  hsize_array_integer;
   suffix_of:  hsize_array_byte;
   stack:      hsize_array_byte;
   stackp:     integer;



(*
 * Zipfile input/output handlers
 *
 *)


(* ------------------------------------------------------------- *)
procedure skip_csize;
begin
   dos_lseek(zipfd,csize,seek_cur);
   zipeof := true;
   csize := 0;
   incnt := 0;
end;


(* ------------------------------------------------------------- *)
procedure ReadByte(var x: byte);
begin
   if incnt = 0 then
   begin
      if csize = 0 then
      begin
         zipeof := true;
         exit;
      end;

      inpos := sizeof(inbuf);
      if inpos > csize then
         inpos := csize;
      incnt := dos_read(zipfd,inbuf,inpos);

      inpos := 1;
      dec(csize,incnt);
   end;

   x := inbuf[inpos];
   inc(inpos);
   dec(incnt);
end;


(*
 * Copyright 1987, 1989 Samuel H. Smith;  All rights reserved
 *
 * This is a component of the ProDoor System.
 * Do not distribute modified versions without my permission.
 * Do not remove or alter this notice or any other copyright notice.
 * If you use this in your own program you must distribute source code.
 * Do not use any of this in a commercial product.
 *
 *)

(******************************************************
 *
 * Procedure:  itoh
 *
 * Purpose:    converts an integer into a string of hex digits
 *
 * Example:    s := itoh(i);
 *
 *)

function itoh(i: longint): string;   {integer to hex conversion}
var
   h:   string;
   w:   word;

   procedure digit(ix: integer; ii: word);
   begin
      ii := ii and 15;
      if ii > 9 then 
         ii := ii + 7 + ord('a') - ord('A');
      h[ix] := chr(ii + ord('0'));
   end;

begin
   w := i and $FFFF;
   h[0] := chr(4);
   digit(1,w shr 12);
   digit(2,w shr 8);
   digit(3,w shr 4);
   digit(4,w);
   itoh := h;   
end;


(* ------------------------------------------------------------- *)
procedure ReadBits(bits: integer; var result: integer);
   {read the specified number of bits}
const
   bit:     integer = 0;
   bitv:    integer = 0;
   x:       integer = 0;
begin
   x := 0;
   bitv := 1;

   for bit := 0 to bits-1 do
   begin

      if pcbits > 0 then
      begin
         dec(pcbits);
         pcbitv := pcbitv shl 1;
      end
      else

      begin
         ReadByte(pc);
         pcbits := 7;
         pcbitv := 1;
      end;

      if (pc and pcbitv) <> 0 then
         x := x or bitv;

      bitv := bitv shl 1;
   end;

(* writeln(bits,'-',itoh(x)); *)
   result := x;
end;


(* ---------------------------------------------------------- *)
procedure get_string(ln: word; var s: string);
var
   n: word;
begin
   if ln > 255 then
      ln := 255;
   n := dos_read(zipfd,s[1],ln);
   s[0] := chr(ln);
end;


(* ------------------------------------------------------------- *)
procedure OutByte (c: integer);
   (* output each character from archive to screen *)
begin
   outbuf[outcnt {outpos mod sizeof(outbuf)} ] := c;
   inc(outpos);
   inc(outcnt);

   if outcnt = sizeof(outbuf) then
   begin
      dos_write(outfd,outbuf,outcnt);
      outcnt := 0;
      write('.');
   end;
end;


(*
 * expand 'reduced' members of a zipfile
 *
 *)

(*
 * The Reducing algorithm is actually a combination of two
 * distinct algorithms.  The first algorithm compresses repeated
 * byte sequences, and the second algorithm takes the compressed
 * stream from the first algorithm and applies a probabilistic
 * compression method.
 *
 *)

function reduce_L(x: byte): byte;
begin
   case factor of
      1: reduce_L := x and $7f;
      2: reduce_L := x and $3f;
      3: reduce_L := x and $1f;
      4: reduce_L := x and $0f;
   end;
end;

function reduce_F(x: byte): byte;
begin
   case factor of
      1: if x = 127 then reduce_F := 2 else reduce_F := 3;
      2: if x = 63  then reduce_F := 2 else reduce_F := 3;
      3: if x = 31  then reduce_F := 2 else reduce_F := 3;
      4: if x = 15  then reduce_F := 2 else reduce_F := 3;
   end;
end;

function reduce_D(x,y: byte): word;
begin
   case factor of
      1: reduce_D := ((x shr 7) and $01) * 256 + Y + 1;
      2: reduce_D := ((x shr 6) and $03) * 256 + Y + 1;
      3: reduce_D := ((x shr 5) and $07) * 256 + Y + 1;
      4: reduce_D := ((x shr 4) and $0f) * 256 + Y + 1;
   end;
end;

function reduce_B(x: byte): word;
   {number of bits needed to encode the specified number}
begin
   case x-1 of
      0..1:    reduce_B := 1;
      2..3:    reduce_B := 2;
      4..7:    reduce_B := 3;
      8..15:   reduce_B := 4;
     16..31:   reduce_B := 5;
     32..63:   reduce_B := 6;
     64..127:  reduce_B := 7;
   else        reduce_B := 8;
   end;
end;

procedure Expand(c: byte);
const
   DLE = 144;
var
   op:   longint;
   i:    integer;

begin

   case ExState of
        0:  if C <> DLE then
                outbyte(C)
            else
                ExState := 1;

        1:  if C <> 0 then
            begin
                V := C;
                Len := reduce_L(V);
                ExState := reduce_F(Len);
            end
            else
            begin
                outbyte(DLE);
                ExState := 0;
            end;

        2:  begin
               Len := Len + C;
               ExState := 3;
            end;

        3:  begin
               op := outpos-reduce_D(V,C);
               for i := 0 to Len+2 do
               begin
                  if op < 0 then
                     outbyte(0)
                  else
                     outbyte(outbuf[op mod sizeof(outbuf)]);
                  inc(op);
               end;

               ExState := 0;
            end;
   end;
end;


procedure LoadFollowers;
var
   x: integer;
   i: integer;
   b: integer;
begin
   for x := 255 downto 0 do
   begin
      ReadBits(6,b);
      followers[x][0] := chr(b);

      for i := 1 to length(followers[x]) do
      begin
         ReadBits(8,b);
         followers[x][i] := chr(b);
      end;
   end;
end;


(* ----------------------------------------------------------- *)
procedure unReduce;
   {expand probablisticly reduced data}

var
   lchar:   integer;
   lout:    integer;
   I:       integer;

begin
   factor := cmethod - 1;
   if (factor < 1) or (factor > 4) then
   begin
      skip_csize;
      exit;
   end;

   ExState := 0;
   LoadFollowers;
   lchar := 0;

   while (not zipeof) and (outpos < cusize) do
   begin

      if followers[lchar] = '' then
         ReadBits( 8,lout )
      else

      begin
         ReadBits(1,lout);
         if lout <> 0 then
            ReadBits( 8,lout )
         else
         begin
            ReadBits( reduce_B(length(followers[lchar])), I );
            lout := ord( followers[lchar][I+1] );
         end;
      end;

      if zipeof then
         exit;

      Expand( lout );
      lchar := lout;
   end;

end;



(*
 * expand 'shrunk' members of a zipfile
 *
 *)

(*
 * UnShrinking
 * -----------
 *
 * Shrinking is a Dynamic Ziv-Lempel-Welch compression algorithm
 * with partial clearing.  The initial code size is 9 bits, and
 * the maximum code size is 13 bits.  Shrinking differs from
 * conventional Dynamic Ziv-lempel-Welch implementations in several
 * respects:
 *
 * 1)  The code size is controlled by the compressor, and is not
 *     automatically increased when codes larger than the current
 *     code size are created (but not necessarily used).  When
 *     the decompressor encounters the code sequence 256
 *     (decimal) followed by 1, it should increase the code size
 *     read from the input stream to the next bit size.  No
 *     blocking of the codes is performed, so the next code at
 *     the increased size should be read from the input stream
 *     immediately after where the previous code at the smaller
 *     bit size was read.  Again, the decompressor should not
 *     increase the code size used until the sequence 256,1 is
 *     encountered.
 *
 * 2)  When the table becomes full, total clearing is not
 *     performed.  Rather, when the compresser emits the code
 *     sequence 256,2 (decimal), the decompressor should clear
 *     all leaf nodes from the Ziv-Lempel tree, and continue to
 *     use the current code size.  The nodes that are cleared
 *     from the Ziv-Lempel tree are then re-used, with the lowest
 *     code value re-used first, and the highest code value
 *     re-used last.  The compressor can emit the sequence 256,2
 *     at any time.
 *
 *)

procedure unShrink;

const
   max_bits =  13;
   init_bits = 9;
   first_ent = 257;
   clear =     256;
   
var
   cbits:      integer;
   maxcode:    integer;
   free_ent:   integer;
   maxcodemax: integer;
   offset:     integer;
   sizex:      integer;
   finchar:    integer;
   code:       integer;
   oldcode:    integer;
   incode:     integer;


(* ------------------------------------------------------------- *)
procedure partial_clear;
var
   pr:   integer;
   cd:   integer;

begin
   {mark all nodes as potentially unused}
   for cd := first_ent to free_ent-1 do
      word(prefix_of[cd]) := prefix_of[cd] or $8000;


   {unmark those that are used by other nodes}
   for cd := first_ent to free_ent-1 do
   begin
      pr := prefix_of[cd] and $7fff;    {reference to another node?}
      if pr >= first_ent then           {flag node as referenced}
         prefix_of[pr] := prefix_of[pr] and $7fff;
   end;


   {clear the ones that are still marked}
   for cd := first_ent to free_ent-1 do
      if (prefix_of[cd] and $8000) <> 0 then
         prefix_of[cd] := -1;


   {find first cleared node as next free_ent}
   free_ent := first_ent;
   while (free_ent < maxcodemax) and (prefix_of[free_ent] <> -1) do
      inc(free_ent);
end;


(* ------------------------------------------------------------- *)
begin
   (* decompress the file *)
   maxcodemax := 1 shl max_bits;
   cbits := init_bits;
   maxcode := (1 shl cbits)- 1;
   free_ent := first_ent;
   offset := 0;
   sizex := 0;

   fillchar(prefix_of,sizeof(prefix_of),$FF);
   for code := 255 downto 0 do
   begin
      prefix_of[code] := 0;
      suffix_of[code] := code;
   end;

   ReadBits(cbits,oldcode);
   if zipeof then
      exit;
   finchar := oldcode;

   OutByte(finchar);

   stackp := 0;

   while (not zipeof) do
   begin
      ReadBits(cbits,code);
      if zipeof then
         exit;

      while (code = clear) do
      begin
         ReadBits(cbits,code);

         case code of
            1: begin
                  inc(cbits);
                  if cbits = max_bits then
                     maxcode := maxcodemax
                  else
                     maxcode := (1 shl cbits) - 1;
               end;

            2: partial_clear;
         end;

         ReadBits(cbits,code);
         if zipeof then
            exit;
      end;


      {special case for KwKwK string}
      incode := code;
      if prefix_of[code] = -1 then
      begin
         stack[stackp] := finchar;
         inc(stackp);
         code := oldcode;
      end;


      {generate output characters in reverse order}
      while (code >= first_ent) do
      begin
         stack[stackp] := suffix_of[code];
         inc(stackp);
         code := prefix_of[code];
      end;

      finchar := suffix_of[code];
      stack[stackp] := finchar;
      inc(stackp);


      {and put them out in forward order}
      while (stackp > 0) do
      begin
         dec(stackp);
         OutByte(stack[stackp]);
      end;


      {generate new entry}
      code := free_ent;
      if code < maxcodemax then
      begin
         prefix_of[code] := oldcode;
         suffix_of[code] := finchar;
         while (free_ent < maxcodemax) and (prefix_of[free_ent] <> -1) do
            inc(free_ent);
      end;


      {remember previous code}
      oldcode := incode;
   end;

end;



(*
 * ProZip2.int - ZIP file interface library      (2-15-89 shs)
 *
 * This procedure displays the text contents of a specified archive
 * file.  The filename must be fully specified and verified.
 *
 *)


(* ---------------------------------------------------------- *)
procedure extract_member;
var
   b: byte;

begin
   pcbits := 0;
   incnt := 0;
   outpos := 0;
   outcnt := 0;
   zipeof := false;

   outfd := dos_create(filename);
   if outfd = dos_error then
   begin
      writeln('Can''t create output: ', filename);
      halt;
   end;

   case cmethod of
      0:    {stored}
            begin
               write(' Extract: ',filename,' ...');
               while (not zipeof) do
               begin
                  ReadByte(b);
                  OutByte(b);
               end;
            end;

      1:    begin
               write('UnShrink: ',filename,' ...');
               UnShrink;
            end;

      2..5: begin
               write('  Expand: ',filename,' ...');
               UnReduce;
            end;

      else  write('Unknown compression method.');
   end;

   if outcnt > 0 then
      dos_write(outfd,outbuf,outcnt);

   dos_file_times(outfd,time_set,ctime,cdate);
   dos_close(outfd);

   writeln('  done.');
end;


(* ---------------------------------------------------------- *)
procedure process_local_file_header;
var
   n:             word;
   rec:           local_file_header;

begin
   n := dos_read(zipfd,rec,sizeof(rec));
   get_string(rec.filename_length,filename);
   get_string(rec.extra_field_length,extra);
   csize := rec.compressed_size;
   cusize := rec.uncompressed_size;
   cmethod := rec.compression_method;
   ctime := rec.last_mod_file_time;
   cdate := rec.last_mod_file_date;
   extract_member;
end;


(* ---------------------------------------------------------- *)
procedure process_central_file_header;
var
   n:             word;
   rec:           central_directory_file_header;
   filename:      string;
   extra:         string;
   comment:       string;

begin
   n := dos_read(zipfd,rec,sizeof(rec));
   get_string(rec.filename_length,filename);
   get_string(rec.extra_field_length,extra);
   get_string(rec.file_comment_length,comment);
end;


(* ---------------------------------------------------------- *)
procedure process_end_central_dir;
var
   n:             word;
   rec:           end_central_dir_record;
   comment:       string;

begin
   n := dos_read(zipfd,rec,sizeof(rec));
   get_string(rec.zipfile_comment_length,comment);
end;


(* ---------------------------------------------------------- *)
procedure process_headers;
var
   sig:  longint;

begin
   dos_lseek(zipfd,0,seek_start);

   while true do
   begin
      if dos_read(zipfd,sig,sizeof(sig)) <> sizeof(sig) then
         exit
      else

      if sig = local_file_header_signature then
         process_local_file_header
      else

      if sig = central_file_header_signature then
         process_central_file_header
      else

      if sig = end_central_dir_signature then
      begin
         process_end_central_dir;
         exit;
      end

      else
      begin
         writeln('Invalid Zipfile Header');
         exit;
      end;
   end;

end;


(* ---------------------------------------------------------- *)
procedure extract_zipfile;
begin
   zipfd := dos_open(zipfn,open_read);
   if zipfd = dos_error then
      exit;

   process_headers;

   dos_close(zipfd);
end;


(*
 * main program
 *
 *)

begin
   writeln;
   writeln(version);
   writeln('Courtesy of:  S.H.Smith  and  The Tool Shop BBS,  (602) 279-2673.');
   writeln;
   if paramcount <> 1 then
   begin
      writeln('Usage:  UnZip FILE[.zip]');
      halt;
   end;

   zipfn := paramstr(1);
   if pos('.',zipfn) = 0 then
      zipfn := zipfn + '.ZIP';

   extract_zipfile;
end.



 
 
 
  Another file downloaded from:   
 
         !
        -$-             & the Temple of the Screaming Electron
         !    *                    Walnut Creek, CA
   +    /^\   |
   !    | |/\/^\  _^_     2400/1200/300 baud  (415) 935-5845  
  /^\  /   @ |  \/_-_\            Jeff Hunter, Sysop
  |@ \_| @     @|- - -|                                  \   
  |  | |    /^\ |  _  |                  - - - - - - - - - * 
  |___/_\___|_|_|_(_)_|       Aaaaaeeeeeeeeeeeeeeeeee!   /   
 
       Specializing in conversations, E-Mail, obscure information,
   entertainment, the arts, politics, futurism, thoughtful discussion, 
          insane speculation, and wild rumours. An ALL-TEXT BBS.
 
                         "Raw Data for Raw Nerves." 
 


Make REAL money with your website!

The entire AOH site is optimized to look best in Firefox® 2.0 on a widescreen monitor (1440x900 or better).
Site design & layout copyright © 1986-2008 AOH
We do not send spam. If you have received spam bearing an artofhacking.com email address, please forward it with full headers to abuse@artofhacking.com.