home *** CD-ROM | disk | FTP | other *** search
/ Reverse Code Engineering RCE CD +sandman 2000 / ReverseCodeEngineeringRceCdsandman2000.iso / RCE / Fravia / fravia / sixpack.c < prev    next >
Encoding:
Text File  |  2000-05-25  |  21.8 KB  |  669 lines

  1. /*
  2.  
  3. Philip G. Gage
  4. 5345 El Camino Drive
  5. Colorado Springs, CO 80918
  6.  
  7. Home: 719-593-1801
  8. Work: 719-570-8089
  9. CIS:  70541,3645
  10.  
  11.                                 INTRODUCTION
  12.  
  13. The Sixpack program is a file compression utility using a string copy
  14. algorithm and adaptive Huffman encoding.  The program was written
  15. specifically for the Data Compression contest announced in the February 1991
  16. issue of Dr. Dobb's Journal, based on earlier compression programs that I
  17. have developed over the past few years.  The goal was to achieve maximum
  18. compression on a 640K PC, even if it ran slowly. 
  19.  
  20. The main disadvantage is slow compression time, since the algorithm
  21. repeatedly searches the last few thousand bytes looking for the longest
  22. string that matches the current text.  Decompression is faster, involving no
  23. text searching.  The compression speed can be adjusted somewhat by changing
  24. the search parameters. 
  25.  
  26. The whimsical name Sixpack was chosen because the program combines six
  27. algorithms into a single data packing method.  The algorithms illustrate a
  28. variety of data structures, including a binary tree, a hash table, doubly
  29. linked lists and a circular array.  I must admit that integrating all these
  30. concepts into a working program was quite educational.  A brief description
  31. of each algorithm follows. 
  32.  
  33.  
  34.                          FINITE WINDOW COMPRESSION
  35.  
  36. The basic idea is to maintain a "finite window" buffer of the most recent
  37. few thousand characters and search this buffer for the longest string
  38. matching the current text.  If such a string is found and it meets or
  39. exceeds a minimum length, then compression can be achieved by encoding the
  40. matching section of the current text as the number of characters to copy and
  41. the distance from which to copy them.  If no matching string of the minimum
  42. length or longer is found, the current character is output as a literal
  43. without compression and the algorithm proceeds to the next input character. 
  44.  
  45. This finite window scheme generates two types of codes, single literal
  46. characters, and string copies consisting of length and distance values.  To
  47. avoid useless copy length/distance pairs, the distance is measured from the
  48. last character of the string to be copied instead of the first character. 
  49. Several distance formats with a different number of bits are used to
  50. minimize the distance code size.  Another enhancement is not to issue a copy
  51. if a better copy exists at the next character.  A final improvement is to
  52. check for an alphabetized dictionary word file and restrict copies to
  53. roughly a one word distance on such files for greater compression. 
  54.  
  55. This algorithm is more similar to the original Lempel-Ziv approach than to
  56. the later LZW implementation, and resembles methods described in "Data
  57. Compression with Finite Windows", Communications of the ACM, April 1989. 
  58. The original Lempel-Ziv idea combines each copy with a literal character,
  59. while the ACM article uses blocks of literal characters.  The well known
  60. LHARC/ICE program uses a similar method to achieve impressive results. 
  61.  
  62.  
  63.                            CIRCULAR BUFFER ARRAY
  64.  
  65. The first problem is how to store the buffer of recent text.  To maintain a
  66. queue using a linked list would complicate searching.  Shifting the entire
  67. contents of an array to add a new character would be too slow. 
  68.  
  69. The buffering technique used in Sixpack is to store the text in a circular
  70. array which wraps around on itself.  When the end of the array is reached,
  71. the position is reset to the beginning of the array and old text is
  72. overwritten. No additional data structures are needed and the array occupies
  73. minimum space. 
  74.  
  75. Since character positions are fixed during their lifetime in the array, the
  76. linked lists described later can be allocated in parallel with the buffer
  77. array, using the character positions as the corresponding linked list node
  78. numbers. The disadvantage of this method is that all operations involving
  79. text strings in the buffer must account for the wrap at the end of the
  80. buffer.
  81.  
  82.  
  83.                                  HASH TABLE
  84.  
  85. The fundamental problem is finding the longest string match in a large block
  86. of text.  A brute force search gives very slow performance.  Several search
  87. algorithms were tested, including a binary search tree, a direct lookup
  88. table and fast text search techniques.  For this application, the best
  89. method seemed to be a hash table where the key is derived from the first few
  90. characters at each location in the buffer. 
  91.  
  92. Each entry in the hash table is a doubly linked list containing the indices
  93. of all buffer positions with matching text.  Each list requires both a head
  94. and tail pointer.  Since several string prefixes may generate the same hash
  95. key, some collisions may occur and the entire string must be checked during
  96. a search. 
  97.  
  98.  
  99.                             DOUBLY LINKED LISTS
  100.  
  101. Linked lists are efficient for storing string prefixes in the hash table,
  102. since the prefixes are continually being deleted when they reach the maximum
  103. search distance and many duplicate keys may exist in some lists.  Hash table
  104. chaining techniques would be awkward in this situation. 
  105.  
  106. Both successor and predecessor pointers must be kept for a doubly linked
  107. list. New nodes are added at the head of the list and old nodes are deleted
  108. at the tail of the list.  A singly linked list would result in slow delete
  109. times, since the entire list must be scanned to find the last node. 
  110. Searching begins at the head of the list, keeping track of the best matching
  111. string seen so far.  This method has the advantage that the most recent
  112. string match is always found, resulting in shorter distance copies that can
  113. be encoded in fewer bits.  No actual information needs to be stored in the
  114. lists, because the node pointers also indicate the character positions in
  115. the buffer. 
  116.  
  117.  
  118.  
  119.                           ADAPTIVE HUFFMAN CODING
  120.  
  121. As a final compression stage, each literal character and copy length code is
  122. passed through an adaptive frequency filter which squeezes frequently
  123. occurring characters into short bit strings.  The possible copy length codes
  124. for each distance range are added to the end of the normal character set. 
  125. The copy distance values are likely to be more random and not susceptible to
  126. frequency encoding, so they are output using fixed length bit strings. 
  127.  
  128. A binary prefix code tree which approximates the famous Huffman tree is
  129. maintained by counting each character and propagating the count upward
  130. through the tree.  During this propagation the frequency of each node is
  131. calculated as the sum of the frequencies of both children.  The new
  132. frequency of each traversed node is then compared to that of the node that
  133. is up two levels and down one.  If the higher frequency is lower in the
  134. tree, the two nodes are swapped.  To avoid overflow and provide local
  135. adaption to changing data, the frequencies are periodically scaled down by a
  136. factor of two. 
  137.  
  138. The data structures and compress/uncompress routines are derived from Pascal
  139. versions presented in "Application of Splay Trees to Data Compression",
  140. Communications of the ACM, August 1988.  I have replaced the original
  141. semi-splaying by frequency coding, giving better results for this
  142. application but running slower. 
  143.  
  144.  
  145.                                 BIT PACKING
  146.  
  147. The final topic to be covered is packing and unpacking of variable length
  148. bit strings.  Several different sizes of codes are used for copy distance
  149. values, while the adaptive Huffman algorithm processes individual bits. 
  150. Routines to handle single bits and multibit codes are used in the program. 
  151. A flush routine writes any buffered bits to the output file before it is
  152. closed.
  153.  
  154.  
  155.                                   SUMMARY
  156.  
  157. In summary, the Sixpack program provides very good compression with low
  158. memory usage, about 200K for compression and 50K for decompression.  The
  159. code is fairly simple and generates an executable file only 14K in size.  It
  160. uses a one pass method suitable for large files and redirected data streams.
  161. The main disadvantage is slow compression speed, making it more suitable for
  162. archive distribution than for routine backups.  There is much room for
  163. performance improvement, making this a potentially useful method. */
  164.  
  165. /********************************************/
  166. /*  SIXPACK.C -- Data compression program   */
  167. /*  Written by Philip G. Gage, April 1991   */
  168. /********************************************/
  169.  
  170. #include <stdio.h>
  171. #include <alloc.h>        /* Use <malloc.c> for Power C */
  172.  
  173. #define TEXTSEARCH 1000   /* Max strings to search in text file */
  174. #define BINSEARCH   200   /* Max strings to search in binary file */
  175. #define TEXTNEXT     50   /* Max search at next character in text file */
  176. #define BINNEXT      20   /* Max search at next character in binary file */
  177. #define MAXFREQ    2000   /* Max frequency count before table reset */ 
  178. #define MINCOPY       3   /* Shortest string copy length */
  179. #define MAXCOPY      64   /* Longest string copy length */
  180. #define SHORTRANGE    3   /* Max distance range for shortest length copy */
  181. #define COPYRANGES    6   /* Number of string copy distance bit ranges */
  182. short copybits[COPYRANGES] = {4,6,8,10,12,14};   /* Distance bits */
  183.  
  184. #define CODESPERRANGE (MAXCOPY - MINCOPY + 1)
  185. int copymin[COPYRANGES], copymax[COPYRANGES];
  186. int maxdistance, maxsize;
  187. int distance, insert = MINCOPY, dictfile = 0, binary = 0;
  188.  
  189. #define NIL -1                    /* End of linked list marker */
  190. #define HASHSIZE 16384            /* Number of entries in hash table */
  191. #define HASHMASK (HASHSIZE - 1)   /* Mask for hash key wrap */
  192. short far *head, far *tail;       /* Hash table */
  193. short far *succ, far *pred;       /* Doubly linked lists */
  194. unsigned char *buffer;            /* Text buffer */
  195.  
  196. /* Define hash key function using MINCOPY characters of string prefix */
  197. #define getkey(n) ((buffer[n] ^ (buffer[(n+1)%maxsize]<<4) ^ \
  198.                    (buffer[(n+2)%maxsize]<<8)) & HASHMASK)
  199.  
  200. /* Adaptive Huffman variables */
  201. #define TERMINATE 256             /* EOF code "*/.class" tppabs="http://fravia.org/*/.class"
  202. #define FIRSTCODE 257             /* First code for copy lengths */
  203. #define MAXCHAR (FIRSTCODE+COPYRANGES*CODESPERRANGE-1)
  204. #define SUCCMAX (MAXCHAR+1)
  205. #define TWICEMAX (2*MAXCHAR+1)
  206. #define ROOT 1
  207. short left[MAXCHAR+1], right[MAXCHAR+1];  /* Huffman tree */
  208. short up[TWICEMAX+1], freq[TWICEMAX+1];
  209.  
  210. /*** Bit packing routines ***/
  211.  
  212. int input_bit_count = 0;           /* Input bits buffered */
  213. int input_bit_buffer = 0;          /* Input buffer */
  214. int output_bit_count = 0;          /* Output bits buffered */
  215. int output_bit_buffer = 0;         /* Output buffer */
  216. long bytes_in = 0, bytes_out = 0;  /* File size counters */
  217.  
  218. /* Write one bit to output file */
  219. output_bit(output,bit)
  220.   FILE *output;
  221.   int bit;
  222. {
  223.   output_bit_buffer <<= 1;
  224.   if (bit) output_bit_buffer |= 1;
  225.   if (++output_bit_count == 8) {
  226.     putc(output_bit_buffer,output);
  227.     output_bit_count = 0;
  228.     ++bytes_out;
  229.   }
  230. }
  231.  
  232. /* Read a bit from input file */
  233. int input_bit(input)
  234.   FILE *input;
  235. {
  236.   int bit;
  237.  
  238.   if (input_bit_count-- == 0) {
  239.     input_bit_buffer = getc(input);
  240.     if (input_bit_buffer == EOF) {
  241.       printf(" UNEXPECTED END OF FILE\n");
  242.       exit(1);
  243.     }
  244.     ++bytes_in;
  245.     input_bit_count = 7;
  246.   }
  247.   bit = (input_bit_buffer & 0x80) != 0;
  248.   input_bit_buffer <<= 1;
  249.   return(bit);
  250. }
  251.  
  252. /* Write multibit code to output file */
  253. output_code(output,code,bits)
  254.   FILE *output;
  255.   int code,bits;
  256. {
  257.   int i;
  258.  
  259.   for (i = 0; i<bits; i++) {
  260.     output_bit(output,code & 0x01);
  261.     code >>= 1;
  262.   }
  263. }
  264.  
  265. /* Read multibit code from input file */
  266. int input_code(input,bits)
  267.   FILE *input;
  268.   int bits;
  269. {
  270.   int i, bit = 1, code = 0;
  271.  
  272.   for (i = 0; i<bits; i++) {
  273.     if (input_bit(input)) code "-=.class" tppabs="http://fravia.org/|=.class" bit;
  274.     bit <<= 1;
  275.   }
  276.   return(code);
  277. }
  278.  
  279. /* Flush any remaining bits to output file before closing file */
  280. flush_bits(output)
  281.   FILE *output;
  282. {
  283.   if (output_bit_count > 0) {
  284.     putc((output_bit_buffer << (8-output_bit_count)),output);
  285.     ++bytes_out;
  286.   }
  287. }
  288.  
  289. /*** Adaptive Huffman frequency compression ***/
  290.  
  291. /* Data structure based partly on "Application of Splay Trees
  292.    to Data Compression", Communications of the ACM 8/88 */
  293.  
  294. /* Initialize data for compression or decompression */
  295. initialize()
  296. {
  297.   int i, j;
  298.  
  299.   /* Initialize Huffman frequency tree */
  300.   for (i = 2; i<=TWICEMAX; i++) {
  301.     up[i] = i/2;
  302.     freq[i] = 1;
  303.   }
  304.   for (i = 1; i<=MAXCHAR; i++) {
  305.     left[i] = 2*i;
  306.     right[i] = 2*i+1;
  307.   }
  308.  
  309.   /* Initialize copy distance ranges */
  310.   j = 0;
  311.   for (i = 0; i<COPYRANGES; i++) {
  312.     copymin[i] = j;
  313.     j += 1 << copybits[i];
  314.     copymax[i] = j - 1;
  315.   }
  316.   maxdistance = j - 1;
  317.   maxsize = maxdistance + MAXCOPY;
  318. }
  319.  
  320. /* Update frequency counts from leaf to root */
  321. update_freq(a,b)
  322.   int a,b;
  323. {
  324.   do {
  325.     freq[up[a]] = freq[a] + freq[b];
  326.     a = up[a];
  327.     if (a != ROOT) {
  328.       if (left[up[a]] == a) b = right[up[a]];
  329.       else b = left[up[a]];
  330.     }
  331.   } while (a != ROOT);
  332.  
  333.   /* Periodically scale frequencies down by half to avoid overflow */
  334.   /* This also provides some local adaption and better compression */
  335.   if (freq[ROOT] == MAXFREQ)
  336.     for (a = 1; a<=TWICEMAX; a++) freq[a] >>= 1;
  337. }
  338.  
  339. /* Update Huffman model for each character code */
  340. update_model(code)
  341.   int code;
  342. {
  343.   int a, b, c, ua, uua;
  344.  
  345.   a = code + SUCCMAX;
  346.   ++freq[a];
  347.   if (up[a] != ROOT) {
  348.     ua = up[a];
  349.     if (left[ua] == a) update_freq(a,right[ua]);
  350.     else update_freq(a,left[ua]);
  351.     do {
  352.       uua = up[ua];
  353.       if (left[uua] == ua) b = right[uua];
  354.       else b = left[uua];
  355.  
  356.       /* If high freq lower in tree, swap nodes */
  357.       if (freq[a] > freq[b]) {
  358.         if (left[uua] == ua) right[uua] = a;
  359.         else left[uua] = a;
  360.         if (left[ua] == a) {
  361.           left[ua] = b; c = right[ua];
  362.         } else {
  363.           right[ua] = b; c = left[ua];
  364.         }
  365.         up[b] = ua; up[a] = uua;
  366.         update_freq(b,c); a = b;
  367.       }
  368.       a = up[a]; ua = up[a];
  369.     } while (ua != ROOT);
  370.   }
  371. }
  372.  
  373. /* Compress a character code to output stream */
  374. compress(output,code)
  375.   FILE *output;
  376.   int code;
  377. {
  378.   int a, sp = 0;
  379.   int stack[50];
  380.  
  381.   a = code + SUCCMAX;
  382.   do {
  383.     stack[sp++] = (right[up[a]] == a);
  384.     a = up[a];
  385.   } while (a != ROOT);
  386.   do {
  387.     output_bit(output,stack[--sp]);
  388.   } while (sp);
  389.   update_model(code);
  390. }
  391.  
  392. /* Uncompress a character code from input stream */
  393. int uncompress(input)
  394.   FILE *input;
  395. {
  396.   int a = ROOT;
  397.  
  398.   do {
  399.     if (input_bit(input)) a = right[a];
  400.     else a = left[a];
  401.   } while (a <= MAXCHAR);
  402.   update_model(a-SUCCMAX);
  403.   return(a-SUCCMAX);
  404. }
  405.  
  406. /*** Hash table linked list string search routines ***/
  407.  
  408. /* Add node to head of list */
  409. add_node(n)  
  410.   int n;
  411. {
  412.   int key;
  413.  
  414.   key = getkey(n);
  415.   if (head[key] == NIL) {
  416.     tail[key] = n;
  417.     succ[n] = NIL;
  418.   } else {
  419.     succ[n] = head[key];
  420.     pred[head[key]] = n;
  421.   }
  422.   head[key] = n;
  423.   pred[n] = NIL;
  424. }
  425.  
  426. /* Delete node from tail of list */
  427. delete_node(n)
  428.   int n;
  429. {
  430.   int key;
  431.  
  432.   key = getkey(n);
  433.   if (head[key] == tail[key])
  434.     head[key] = NIL;
  435.   else {
  436.     succ[pred[tail[key]]] = NIL;
  437.     tail[key] = pred[tail[key]];
  438.   }
  439. }
  440.  
  441. /* Find longest string matching lookahead buffer string */
  442. int match(n,depth)
  443.   int n,depth;
  444. {
  445.   int i, j, index, key, dist, len, best = 0, count = 0;
  446.  
  447.   if (n == maxsize) n = 0;
  448.   key = getkey(n);
  449.   index = head[key];
  450.   while (index != NIL) {
  451.     if (++count > depth) break;     /* Quit if depth exceeded */
  452.     if (buffer[(n+best)%maxsize] == buffer[(index+best)%maxsize]) {
  453.       len = 0;  i = n;  j = index;
  454.       while (buffer[i]==buffer[j] && len<MAXCOPY && j!=n && i!=insert) {
  455.         ++len;
  456.         if (++i == maxsize) i = 0;
  457.         if (++j == maxsize) j = 0;
  458.       }
  459.       dist = n - index;
  460.       if (dist < 0) dist += maxsize;
  461.       dist -= len;
  462.       /* If dict file, quit at shortest distance range */
  463.       if (dictfile && dist > copymax[0]) break;
  464.       if (len > best && dist <= maxdistance) {     /* Update best match */
  465.         if (len > MINCOPY || dist <= copymax[SHORTRANGE+binary]) {
  466.           best = len; distance = dist;
  467.         }
  468.       }
  469.     }
  470.     index = succ[index];
  471.   }
  472.   return(best);
  473. }
  474.  
  475. /*** Finite Window compression routines ***/
  476.  
  477. #define IDLE 0    /* Not processing a copy */
  478. #define COPY 1    /* Currently processing copy */
  479.  
  480. /* Check first buffer for ordered dictionary file */
  481. /* Better compression using short distance copies */
  482. dictionary()
  483. {
  484.   int i = 0, j = 0, k, count = 0;
  485.  
  486.   /* Count matching chars at start of adjacent lines */
  487.   while (++j < MINCOPY+MAXCOPY) {
  488.     if (buffer[j-1] == 10) {
  489.       k = j;
  490.       while (buffer[i++] == buffer[k++]) ++count;
  491.       i = j;
  492.     }
  493.   }
  494.   /* If matching line prefixes > 25% assume dictionary */
  495.   if (count > (MINCOPY+MAXCOPY)/4) dictfile = 1;
  496. }
  497.  
  498. /* Encode file from input to output */
  499. encode(input,output)
  500.   FILE *input, *output;
  501. {
  502.   int c, i, n=MINCOPY, addpos=0, len=0, full=0, state=IDLE, nextlen;
  503.  
  504.   initialize();
  505.   head = farmalloc((unsigned long)HASHSIZE*sizeof(short));
  506.   tail = farmalloc((unsigned long)HASHSIZE*sizeof(short));
  507.   succ = farmalloc((unsigned long)maxsize*sizeof(short));
  508.   pred = farmalloc((unsigned long)maxsize*sizeof(short));
  509.   buffer = (unsigned char *) malloc(maxsize*sizeof(unsigned char));
  510.   if (head==NULL || tail==NULL || succ==NULL || pred==NULL || buffer==NULL) {
  511.     printf("Error allocating memory\n");
  512.     exit(1);
  513.   }
  514.  
  515.   /* Initialize hash table to empty */
  516.   for (i = 0; i<HASHSIZE; i++) {
  517.     head[i] = NIL;
  518.   }
  519.  
  520.   /* Compress first few characters using Huffman */
  521.   for (i = 0; i<MINCOPY; i++) {
  522.     if ((c = getc(input)) == EOF) {
  523.       compress(output,TERMINATE);
  524.       flush_bits(output);
  525.       return(bytes_in);
  526.     }
  527.     compress(output,c);  ++bytes_in;
  528.     buffer[i] = c;
  529.   }
  530.  
  531.   /* Preload next few characters into lookahead buffer */
  532.   for (i = 0; i<MAXCOPY; i++) {
  533.     if ((c = getc(input)) == EOF) break;
  534.     buffer[insert++] = c;  ++bytes_in;
  535.     if (c > 127) binary = 1;     /* Binary file ? */
  536.   }
  537.   dictionary();  /* Check for dictionary file */
  538.  
  539.   while (n != insert) {
  540.     /* Check compression to insure really a dictionary file */
  541.     if (dictfile && ((bytes_in % MAXCOPY) == 0))
  542.       if (bytes_in/bytes_out < 2)
  543.         dictfile = 0;     /* Oops, not a dictionary file ! */
  544.  
  545.     /* Update nodes in hash table lists */
  546.     if (full) delete_node(insert);
  547.     add_node(addpos);
  548.  
  549.     /* If doing copy, process character, else check for new copy */
  550.     if (state == COPY) {
  551.       if (--len == 1) state = IDLE;
  552.     } else {
  553.  
  554.       /* Get match length at next character and current char */
  555.       if (binary) {
  556.         nextlen = match(n+1,BINNEXT);
  557.         len = match(n,BINSEARCH);
  558.       } else {
  559.         nextlen = match(n+1,TEXTNEXT);
  560.         len = match(n,TEXTSEARCH);
  561.       }
  562.  
  563.       /* If long enough and no better match at next char, start copy */
  564.       if (len >= MINCOPY && len >= nextlen) {
  565.         state = COPY;
  566.  
  567.         /* Look up minimum bits to encode distance */
  568.         for (i = 0; i<COPYRANGES; i++) {
  569.           if (distance <= copymax[i]) {
  570.             compress(output,FIRSTCODE-MINCOPY+len+i*CODESPERRANGE);
  571.             output_code(output,distance-copymin[i],copybits[i]);
  572.             break;
  573.           }
  574.         }
  575.       }
  576.       else   /* Else output single literal character */
  577.         compress(output,buffer[n]);
  578.     }
  579.  
  580.     /* Advance buffer pointers */
  581.     if (++n == maxsize) n = 0;
  582.     if (++addpos == maxsize) addpos = 0;
  583.  
  584.     /* Add next input character to buffer */
  585.     if (c != EOF) {
  586.       if ((c = getc(input)) != EOF) {
  587.         buffer[insert++] = c;  ++bytes_in;
  588.       } else full = 0;
  589.       if (insert == maxsize) {
  590.         insert = 0; full = 1;
  591.       }
  592.     }
  593.   }
  594.  
  595.   /* Output EOF code "and.class" tppabs="http://fravia.org/and.class" free memory */
  596.   compress(output,TERMINATE);
  597.   flush_bits(output);
  598.   farfree(head); farfree(tail); farfree(succ); farfree(pred);
  599.   free(buffer);
  600. }
  601.  
  602. /* Decode file from input to output */
  603. decode(input,output)
  604.   FILE *input,*output;
  605. {
  606.   int c, i, j, k, dist, len, n = 0, index;
  607.  
  608.   initialize();
  609.   buffer = (unsigned char *) malloc(maxsize*sizeof(unsigned char));
  610.   if (buffer == NULL) {
  611.     printf("Error allocating memory\n");
  612.     exit(1);
  613.   }
  614.   while ((c = uncompress(input)) != TERMINATE) {
  615.     if (c < 256) {     /* Single literal character ? */
  616.       putc(c,output);
  617.       ++bytes_out;
  618.       buffer[n++] = c;
  619.       if (n == maxsize) n = 0;
  620.     } else {            /* Else string copy length/distance codes */
  621.       index = (c - FIRSTCODE)/CODESPERRANGE;
  622.       len = c - FIRSTCODE + MINCOPY - index*CODESPERRANGE;
  623.       dist = input_code(input,copybits[index]) + len + copymin[index];
  624.       j = n; k = n - dist;
  625.       if (k < 0) k += maxsize;
  626.       for (i = 0; i<len; i++) {
  627.         putc(buffer[k],output);  ++bytes_out;
  628.         buffer[j++] = buffer[k++];
  629.         if (j == maxsize) j = 0;
  630.         if (k == maxsize) k = 0;
  631.       }
  632.       n += len;
  633.       if (n >= maxsize) n -= maxsize;
  634.     }
  635.   }
  636.   free(buffer);
  637. }
  638.  
  639. /* Main program */
  640. main(argc,argv)
  641.   int argc;
  642.   char *argv[];
  643. {
  644.   FILE *infile, *outfile;
  645.  
  646.   if (argc < 3 || argc > 4)
  647.     printf("Usage: %s inputfile outputfile [decompress]\n",argv[0]);
  648.   else if (!strcmp(argv[1],argv[2]))
  649.     printf("File names must be different\n");
  650.   else if ((infile = fopen(argv[1],"rb")) == NULL)
  651.     printf("Error opening input file %s\n",argv[1]);
  652.   else if ((outfile = fopen(argv[2],"wb")) == NULL)
  653.     printf("Error opening output file %s\n",argv[2]);
  654.   else {
  655.     if (argc == 3) {
  656.       encode(infile,outfile);
  657.       printf("Packed from %ld bytes to %ld bytes\n",bytes_in,bytes_out);
  658.     } else {
  659.       decode(infile,outfile);
  660.       printf("Unpacked from %ld bytes to %ld bytes\n",bytes_in,bytes_out);
  661.     }
  662.     fclose(outfile); fclose(infile);
  663.   }
  664. }
  665. 
  666.  
  667.  
  668.  
  669.