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

#define version  "UnZip:  Zipfile Extract v1.1á of 03-06-89;  (C) 1989 S.H.Smith"

typedef unsigned char byte;
typedef long longint;
typedef unsigned word;
typedef char boolean;
#define STRSIZ  256

#include <stdio.h>
#include <io.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>



/* ----------------------------------------------------------- */ 
/*
 * 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  uinbufsize    512L   /* input buffer size */
   byte     inbuf[uinbufsize];

   boolean  zipeof;
   longint  csize;
   longint  cusize;
   int      cmethod;
   int      inpos;
   int      incnt;
   int      pc;
   int      pcbits;
   int      pcbitv;
   
   int      zipfd;
   char     zipfn[STRSIZ];
   local_file_header lrec;




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


   byte     outbuf[4096];   /* for rle look-back */
   longint  outpos;         /* absolute position in outfile */
   int      outcnt;

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



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


   int      factor;
   byte     followers[256][64];
   byte     Slen[256];
   int      ExState;
   int      C;
   int      V;
   int      Len;

   #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      cbits;
   int      maxcode;
   int      free_ent;
   int      maxcodemax;
   int      offset;
   int      sizex;


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

void         skip_csize(void)
{ 
   lseek(zipfd,csize,SEEK_CUR);
   zipeof = 1;
   csize = 0L; 
   incnt = 0; 
} 


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

void         ReadByte(int *       x)
{ 
   if (incnt == 0) 
   { 
      if (csize == 0L) 
      { 
         zipeof = 1;
         return;
      } 

      inpos = sizeof(inbuf);
      if (inpos > csize) 
         inpos = (int)csize;
      incnt = read(zipfd,inbuf,inpos);

      inpos = 1; 
      csize -= incnt; 
   } 

   *x = inbuf[inpos-1]; 
   inpos++; 
   incnt--; 
} 


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

void         ReadBits(int      bits,
                      int *    x)
     /* read the specified number of bits */ 
{ 
   int      bit;
   int      bitv;

   *x = 0;
   bitv = 1;

   for (bit = 0; bit <= bits-1; bit++)
   { 

      if (pcbits > 0) 
      { 
         pcbits--; 
         pcbitv = pcbitv << 1; 
      } 
      else 

      { 
         ReadByte(&pc); 
         pcbits = 7; 
         pcbitv = 1; 
      } 

      if ((pc & pcbitv) != 0) 
         *x = *x | bitv; 

      bitv = (int) (bitv << 1);
   } 

} 


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

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


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

void         OutByte(int      c)
   /* output each character from archive to screen */ 
{ 
   outbuf[outcnt /* outpos % sizeof(outbuf) */] = c;
   outpos++; 
   outcnt++;
 
   if (outcnt == sizeof(outbuf)) 
   { 
      write(outfd,outbuf,outcnt);
      outcnt = 0; 
      printf("."); 
   } 
} 


/* ----------------------------------------------------------- */
   
int         reduce_L(int         x)
   { 
      switch (factor) {
         case 1:   return x & 0x7f; 
         case 2:   return x & 0x3f; 
         case 3:   return x & 0x1f; 
         case 4:   return x & 0x0f; 
      } 
    return 0; /* error */
   } 

   
int         reduce_F(int         x)
   { 
      switch (factor) {
         case 1:   if (x == 127) return 2;  else return 3;
         case 2:   if (x == 63) return 2;   else return 3;
         case 3:   if (x == 31) return 2;   else return 3;
         case 4:   if (x == 15) return 2;   else return 3;
      } 
    return 0; /* error */
   } 

   
int         reduce_D(int         x,
                     int         y)
   { 
      switch (factor) {
         case 1:   return ((x >> 7) & 0x01) * 256 + y + 1; 
         case 2:   return ((x >> 6) & 0x03) * 256 + y + 1; 
         case 3:   return ((x >> 5) & 0x07) * 256 + y + 1; 
         case 4:   return ((x >> 4) & 0x0f) * 256 + y + 1; 
      } 
    return 0; /* error */
   } 


int         reduce_B(int         x)
        /* number of bits needed to encode the specified number */ 
   { 
      switch (x - 1) {
         
         case 0:   
         case 1:   return 1; 
         
         case 2:   
         case 3:   return 2; 
         
         case 4:
         case 5:   
         case 6:   
         case 7:   return 3; 
         
         case 8:   
         case 9:   
         case 10:   
         case 11:   
         case 12:   
         case 13:   
         case 14:   
         case 15:   return 4; 
        
         case 16:
         case 17:
         case 18:
         case 19:
         case 20:
         case 21:
         case 22:
         case 23:
         case 24:
         case 25:
         case 26:
         case 27:
         case 28:
         case 29:
         case 30:
         case 31:   return 5;
        
         case 32:
         case 33:
         case 34:
         case 35:
         case 36:
         case 37:
         case 38:
         case 39:
         case 40:
         case 41:
         case 42:
         case 43:
         case 44:
         case 45:
         case 46:
         case 47:
         case 48:
         case 49:
         case 50:
         case 51:
         case 52:
         case 53:
         case 54:
         case 55:
         case 56:
         case 57:
         case 58:
         case 59:
         case 60:
         case 61:
         case 62:
         case 63:   return 6;

         case 64:
         case 65:
         case 66:
         case 67:
         case 68:
         case 69:
         case 70:
         case 71:
         case 72:
         case 73:
         case 74:
         case 75:
         case 76:
         case 77:
         case 78:
         case 79:
         case 80:
         case 81:
         case 82:
         case 83:
         case 84:
         case 85:
         case 86:
         case 87:
         case 88:
         case 89:
         case 90:
         case 91:
         case 92:
         case 93:
         case 94:
         case 95:
         case 96:
         case 97:
         case 98:
         case 99:
         case 100:
         case 101:
         case 102:
         case 103:
         case 104:
         case 105:
         case 106:
         case 107:
         case 108:
         case 109:
         case 110:
         case 111:
         case 112:
         case 113:
         case 114:
         case 115:
         case 116:
         case 117:
         case 118:
         case 119:
         case 120:
         case 121:
         case 122:
         case 123:
         case 124:
         case 125:
         case 126:
         case 127:   return 7;
      
      default:       return 8;
      } 
   } 



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

void         Expand(int      c)
   { 
      #define DLE           144
   
      switch (ExState) {
           
           case 0:
               if (c != DLE)
                   OutByte(c);
               else 
                   ExState = 1; 
           break; 
           
           case 1:
               if (c != 0)
               { 
                   V = c; 
                   Len = reduce_L(V);
                   ExState = reduce_F(Len);
               } 
               else 
               { 
                   OutByte(DLE); 
                   ExState = 0; 
               } 
           break; 
           
           case 2:   { 
                  Len = Len + c; 
                  ExState = 3; 
               } 
           break; 
           
           case 3:   { 
                  int i;
                  longint offset = reduce_D(V,c);
                  longint op = outpos - offset;

                  for (i = 0; i <= Len + 2; i++) 
                  { 
                     if (op < 0L) 
                        OutByte(0);
                     else 
                        OutByte(outbuf[(int)(op % sizeof(outbuf))]);
                     op++; 
                  } 

                  ExState = 0; 
               } 
         break;
      } 
   } 


/* ----------------------------------------------------------- */
   
void         LoadFollowers(void)
   { 
      int      x;
      int      i;
      int      b;

      for (x = 255; x >= 0; x--) 
      { 
         ReadBits(6,&b); 
         Slen[x] = b;

         for (i = 0; i < Slen[x]; i++)
         { 
            ReadBits(8,&b); 
            followers[x][i] = b;
         } 
      } 
   } 


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

/*
 * 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.
 *
 */ 

void         unReduce(void)
     /* expand probablisticly reduced data */ 

   { 

   int    lchar;
   int    lout;
   int    I;

   factor = cmethod - 1; 
   if ((factor < 1) || (factor > 4)) 
   { 
      skip_csize(); 
      return;
   } 

   ExState = 0; 
   LoadFollowers(); 
   lchar =  0;

   while ((!zipeof) && (outpos < cusize))
   { 

      if (Slen[lchar] == 0)
         ReadBits(8,&lout);
      else 

      { 
         ReadBits(1,&lout); 
         if (lout != 0) 
            ReadBits(8,&lout);
         else 
         { 
            ReadBits(reduce_B(Slen[lchar]),&I);
            lout = followers[lchar][I];
         } 
      } 

      Expand(lout); 
      lchar = lout; 
   } 
} 


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

void         partial_clear(void)
{ 
   int      pr;
   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 */ 
   free_ent = first_ent; 
   while ((free_ent < maxcodemax) && (prefix_of[free_ent] != -1)) 
      free_ent++; 
} 


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

void         unShrink(void)

{ 
   int      stackp;
   int      finchar;
   int      code;
   int      oldcode;
   int      incode;


   /* decompress the file */ 
   maxcodemax = 1 << max_bits; 
   cbits = init_bits; 
   maxcode = (1 << cbits) - 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;
   } 

   ReadBits(cbits,&oldcode); 
   if (zipeof) return;
   finchar = oldcode; 

   OutByte(finchar); 

   stackp = 0; 

   while ((!zipeof)) 
   { 
      ReadBits(cbits,&code); 
      if (zipeof) return;

      while (code == clear)
      { 
         ReadBits(cbits,&code); 

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

            case 2:
                  partial_clear();
            break;
         } 

         ReadBits(cbits,&code); 
         if (zipeof) return;
      } 

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


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

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


      /* and put them out in forward order */
      while (stackp > 0)
      { 
         stackp--; 
         OutByte(stack[stackp]);
      } 


      /* generate new entry */
      code = free_ent;
      if (code < maxcodemax) 
      { 
         prefix_of[code] = oldcode;
         suffix_of[code] = finchar;
         while ((free_ent < maxcodemax) && (prefix_of[free_ent] != -1))
            free_ent++;
      } 


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

} 


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

void         extract_member(void)
{ 
   int    b;

   union {
        struct ftime ft;
        struct {
            word ztime;
            word zdate;
        } zt;
    } td;

for (b=0; b<sizeof(outbuf); b++) outbuf[b]=0;
   pcbits = 0; 
   incnt = 0; 
   outpos = 0L; 
   outcnt = 0; 
   zipeof = 0;

   outfd = creat(filename,S_IWRITE|S_IREAD);
   if (outfd < 1)
   { 
      printf("Can't create output: %s\n",filename); 
      exit(0);
   } 

   close(outfd);
   outfd = open(filename,O_RDWR|O_BINARY);


   switch (cmethod) {
      
      case 0:     /* stored */ 
            { 
               printf(" Extract: %s ...",filename); 
               while ((!zipeof)) 
               { 
                  ReadByte(&b); 
                  OutByte(b); 
               } 
            } 
      break; 
      
      case 1:   { 
               printf("UnShrink: %s ...",filename); 
               unShrink(); 
            } 
      break; 
      
      case 2:   
      case 3:   
      case 4:   
      case 5:   { 
               printf("  Expand: %s ...",filename); 
               unReduce(); 
            } 
      break; 
      
      default: printf("Unknown compression method."); 
   } 

   if (outcnt > 0) 
      write(outfd,outbuf,outcnt);


   /* set output file date and time */
   td.zt.ztime = lrec.last_mod_file_time;
   td.zt.zdate = lrec.last_mod_file_date;
   setftime(outfd,&td.ft);

   close(outfd);
   printf("  done.\n"); 
} 


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

void         process_local_file_header(void)
{ 
   read(zipfd,&lrec,sizeof(lrec));
   get_string(lrec.filename_length,filename);
   get_string(lrec.extra_field_length,extra);
   csize = lrec.compressed_size;
   cusize = lrec.uncompressed_size;
   cmethod = lrec.compression_method;
   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));
   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));
   get_string(rec.zipfile_comment_length,comment); 
} 


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

void         process_headers(void)
{ 
   longint sig;

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

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

   process_headers();
 
   close(zipfd);
} 


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

void main(int argc, char **argv)
{
   printf("\n"); 
   printf("%s\n",version); 
   printf("Courtesy of:  S.H.Smith  and  The Tool Shop BBS,  (602) 279-2673.\n"); 
   printf("\n");
 
   if (argc != 2)
   { 
      printf("Usage:  UnZip FILE[.zip]\n");
      exit(0);
   } 

   strcpy(zipfn,argv[1]);
   if (strchr(zipfn,'.') == NULL)
      strcat(zipfn,".ZIP");

   extract_zipfile(); 
   exit(0);
}

