[comp.sources.apple2] v001SRC032: Nulib - Archive Library Tools

jac@yoko.rutgers.edu (Jonathan A. Chandross) (05/02/91)

Submitted-by: Andy McFadden (fadden@cory.berkeley.edu)
Posting-number: Volume 1, Source:32
Archive-name: archive/unix/nulib/part01.10
Architecture: UNIX
Version-number: 3.03


Nulib allows handles many of the archiving formats used on the Apple.
Very handy indeed.

Enjoy.


=README
-Nulib v3.03 (February 1991)
-- Fixed XENIX problems with includes and libs (Ron Higgins).
-- Fixed bug in directory expansion (Larry W. Virden).
-NuLib v3.02
-- Silenced screaming about bad dates (Larry W. Virden).
-- Fixed glitches in nulib.lnk and nulib.mak (Bruce Kahn).
-NuLib v3.01
-- Fixed non-compression bug in ShrinkIt LZW (Scott Blackman).
-
-NuLib v3.0 README (September 1990)
-
-New items in 3.0:
-- ShrinkIt LZW compression
-- UNIX 12-bit and 16-bit compression
-- New archive listing format (similar to ARC and ZOO)
-
-Things work much better if you read the documentation file (now available
-on the archive site... sorry I didn't put it up sooner).  If you want to
-use UNIX compress to store files, READ THE DOCUMENTATION FIRST.  Not only
-does it tell you how to do it, but it has some warnings about compatibility.
-
-Note that v3.0 does *NOT* unpack archives compressed with GS/ShrinkIt.  The
-problem is that NuLib cannot at present decode LZW/II compression.  You can
-read comments and extract files that were added without compression, but
-that's about it.
-
-To compile this on a UNIX-type system, edit the file "nudefs.h".  Several
-systems are predefined; the default is a BSD UNIX system.  If you want
-to run this on something other than BSD, comment out the #define statements
-(using "/*" and "*/"), and uncomment the appropriate statements.  Then,
-type "make" to execute the Makefile.  If all goes well, you will be
-left with an executable file called "nulib".  If all does not go well,
-double check "nudefs.h".  Send some mail to me if you can't get it to work
-at all.
-
-Currently AT&T 3B2 machines seem to be very uncooperative.  If somebody gets
-NuLib to run on one, please let me know.  The problem is in the directory
-manipulation routines (struct dirent, opendir(), readdir(), etc).
-
-To make the MS-DOS version, use the nulib.mak and nulib.lnk files with MS C
-(supplied by Bruce Kahn).  To make the APW version, put all the files in
-one directory, and make a subdirectory called "OBJ".  Put the "linked.scr"
-and "linker.scr" files in OBJ, and then "make.apw".  This will compile all
-the files.  When it finishes, change to OBJ and "alink linked.scr" (standard
-linker) or "alink linker.scr" (ZapLink).  The makefile used to do this
-automatically, but with only 1.25MB of RAM you have to exit APW to purge
-all the memory used by the compiler, and and then restart APW and
-run the linker.
-
-In both cases, BE SURE to modify "nudefs.h" first.  If you don't, the
-compilation will halt in "numain.c" on a line which reminds you to change
-"nudefs.h" (I tended to forget about this).  Also, make sure all the file
-types are set to SRC, and the auxtype to CC.  These are changed with the
-commands "filetype" and "change", respectively.
-
-Please send bug reports, ideas, or gripes to fadden@cory.berkeley.edu, or to
-one of the addresses mentioned in the documentation or under the "-hw"
-option.
-
-Known bugs:
- - Under some systems, using UNIX compress on a file which does not compress
-will cause the archive's EOF to be larger than it should be.  This slack
-space will be used up if you add more files to the archive (with NuLib
-anyway; no guarantees about ShrinkIt or GS/ShrinkIt, but there's no reason
-why they shouldn't work).
-
-(just to make things clear: if the file being compressed doesn't get any
-smaller, the compression halts and the file is simply stored uncompressed.
-Because of the way compress works, on some systems the space that would
-have been occupied by more compressed data is left attached to the file.
-The ShrinkIt compression does not suffer from this problem.  If you add
-more stuff to the file, NuLib will fill the slack space first, NOT just
-append to the end of the file).
-
-Things to come:
- - LZW/II decompression (for GS/ShrinkIt archives)
- - Support for UNIX tar archives
- - CRLF translations while adding files
- - Better handling of comments
-
-Don't hold your breath waiting for these...
-
-I can be reached at:
- fadden@cory.berkeley.edu	(primary e-mail account)
- fadden@hermes.berkeley.edu	(secondary e-mail, since primary is buggy)
-
-All this stuff, and documentation as well, is available via anonymous FTP at:
- avalanche.berkeley.edu
- tornado.berkeley.edu
- headcrash.berkeley.edu
- ...
-in pub/Apple2
-
=nucomp.c
-/*
- * nucomp.c - code to perform UNIX style LZW compression
- *
- * NuLib v3.0  February 1991  Freeware (distribute, don't sell)
- * By Andy McFadden (fadden@cory.berkeley.edu)
- *
- * This is the main compression code from compress v4.3.  Modifications
- * have been made to integrate it with NuLib (primarily in that it no longer
- * uses stdin/stdout), but it's functionally the same.
- */
-#ifdef APW
-segment "Compress"
-#endif
-
-#include "nudefs.h"
-#include "nupak.h"
-#define MAIN		/* cause nucomp.h to alloc global vars */
-
-/*@H************************ < COMPRESS API    > ****************************
-*                                                                           *
-*   compress : compapi.c  <current version of compress algorithm>           *
-*                                                                           *
-*   port by  : Donald J. Gloistein                                          *
-*                                                                           *
-*   Source, Documentation, Object Code:                                     *
-*   released to Public Domain.  This code is based on code as documented    *
-*   below in release notes.                                                 *
-*                                                                           *
-*---------------------------  Module Description  --------------------------*
-*   Contains source code for modified Lempel-Ziv method (LZW) compression   *
-*   and decompression.                                                      *
-*                                                                           *
-*   This code module can be maintained to keep current on releases on the   *
-*   Unix system. The command shell and dos modules can remain the same.     *
-*                                                                           *
-*--------------------------- Implementation Notes --------------------------*
-*                                                                           *
-*   compiled with : compress.h compress.fns compress.c                      *
-*   linked with   : compress.obj compusi.obj                                *
-*                                                                           *
-*   problems:                                                               *
-*                                                                           *
-*                                                                           *
-*   CAUTION: Uses a number of defines for access and speed. If you change   *
-*            anything, make sure about side effects.                        *
-*                                                                           *
-* Compression:                                                              *
-* Algorithm:  use open addressing double hashing (no chaining) on the       *
-* prefix code / next character combination.  We do a variant of Knuth's     *
-* algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime     *
-* secondary probe.  Here, the modular division first probe is gives way     *
-* to a faster exclusive-or manipulation.                                    *
-* Also block compression with an adaptive reset was used in original code,  *
-* whereby the code table is cleared when the compression ration decreases   *
-* but after the table fills.  This was removed from this edition. The table *
-* is re-sized at this point when it is filled , and a special CLEAR code is *
-* generated for the decompressor. This results in some size difference from *
-* straight version 4.0 joe Release. But it is fully compatible in both v4.0 *
-* and v4.01                                                                 *
-*                                                                           *
-* Decompression:                                                            *
-* This routine adapts to the codes in the file building the "string" table  *
-* on-the-fly; requiring no table to be stored in the compressed file.  The  *
-* tables used herein are shared with those of the compress() routine.       *
-*                                                                           *
-*     Initials ---- Name ---------------------------------                  *
-*      DjG          Donald J. Gloistein, current port to MsDos 16 bit       *
-*                   Plus many others, see rev.hst file for full list        *
-*      LvR          Lyle V. Rains, many thanks for improved implementation  *
-*                   of the compression and decompression routines.          *
-*************************************************************************@H*/
-
-#include <stdio.h>
-#define assert(x)
-#include "nucomp.h" /* contains the rest of the include file declarations */
-
-FILE *nustdin, *nustdout;	/* NuLib: use these instead of stdin/stdout */
-long nubytes_read, nucomp_thread_eof;	/* NuLib: used in nextcode (decomp) */
-
-/* NuLib: pulled this out of nextcode() so we can initialize it every time */
-static int prevbits = 0;
-/* NuLib: pulled out of putcode() */
-static int oldbits = 0;
-
-static int offset;
-static long int in_count ;         /* length of input */
-static long int bytes_out;         /* length of compressed output */
-static long int max_bytes_out;	/* NuLib: max #of bytes to output */
-
-static CODE prefxcode, nextfree;
-static CODE highcode;
-static CODE maxcode;
-static HASH hashsize;
-static int  bits;
-
-
-/*
- * The following two parameter tables are the hash table sizes and
- * maximum code values for various code bit-lengths.  The requirements
- * are that Hashsize[n] must be a prime number and Maxcode[n] must be less
- * than Maxhash[n].  Table occupancy factor is (Maxcode - 256)/Maxhash.
- * Note:  I am using a lower Maxcode for 16-bit codes in order to
- * keep the hash table size less than 64k entries.
- */
-CONST HASH hs[] = {
-  0x13FF,       /* 12-bit codes, 75% occupancy */
-  0x26C3,       /* 13-bit codes, 80% occupancy */
-  0x4A1D,       /* 14-bit codes, 85% occupancy */
-  0x8D0D,       /* 15-bit codes, 90% occupancy */
-  0xFFD9        /* 16-bit codes, 94% occupancy, 6% of code values unused */
-};
-#define Hashsize(maxb) (hs[(maxb) -MINBITS])
-
-CONST CODE mc[] = {
-  0x0FFF,       /* 12-bit codes */
-  0x1FFF,       /* 13-bit codes */
-  0x3FFF,       /* 14-bit codes */
-  0x7FFF,       /* 15-bit codes */
-  0xEFFF        /* 16-bit codes, 6% of code values unused */
-};
-#define Maxcode(maxb) (mc[(maxb) -MINBITS])
-
-#define allocx(type,ptr,size) \
-    (((ptr) = (type FAR *) emalloc((unsigned int)(size),sizeof(type))) == NULLPTR(type) \
-    ? NOMEM : OK \
-    )
-
-#define free_array(type,ptr,offset) \
-    if (ptr != NULLPTR(type)) { \
-        efree((ALLOCTYPE FAR *)((ptr) + (offset))); \
-        (ptr) = NULLPTR(type); \
-    }
-
-  /*
-   * Macro to allocate new memory to a pointer with an offset value.
-   */
-#define alloc_array(type, ptr, size, offset) \
-    ( allocx(type, ptr, (size) - (offset)) != OK \
-      ? NOMEM \
-      : (((ptr) -= (offset)), OK) \
-    )
-
-static char FAR *sfx = NULLPTR(char) ;
-#define suffix(code)     sfx[code]
-
-
-#ifdef SPLIT_PFX
-  static CODE FAR *pfx[2] = {NULLPTR(CODE), NULLPTR(CODE)};
-#else
-  static CODE FAR *pfx = NULLPTR(CODE);
-#endif
-
-
-#ifdef SPLIT_HT
-  static CODE FAR *ht[2] = {NULLPTR(CODE),NULLPTR(CODE)};
-#else
-  static CODE FAR *ht = NULLPTR(CODE);
-#endif
-
-
-int alloc_tables(maxcode, hashsize)
-  CODE maxcode;
-  HASH hashsize;
-{
-  static CODE oldmaxcode = 0;
-  static HASH oldhashsize = 0;
-
-  if (hashsize > oldhashsize) {
-#ifdef SPLIT_HT
-      free_array(CODE,ht[1], 0);
-      free_array(CODE,ht[0], 0);
-#else
-      free_array(CODE,ht, 0);
-#endif
-    oldhashsize = 0;
-  }
-
-    if (maxcode > oldmaxcode) {
-#ifdef SPLIT_PFX
-        free_array(CODE,pfx[1], 128);
-        free_array(CODE,pfx[0], 128);
-#else
-        free_array(CODE,pfx, 256);
-#endif
-        free_array(char,sfx, 256);
-
-        if (   alloc_array(char, sfx, maxcode + 1, 256)
-#ifdef SPLIT_PFX
-            || alloc_array(CODE, pfx[0], (maxcode + 1) / 2, 128)
-            || alloc_array(CODE, pfx[1], (maxcode + 1) / 2, 128)
-#else
-            || alloc_array(CODE, pfx, (maxcode + 1), 256)
-#endif
-        ) {
-            oldmaxcode = 0;
-            exit_stat = NOMEM;
-            return(NOMEM);
-        }
-        oldmaxcode = maxcode;
-    }
-    if (hashsize > oldhashsize) {
-        if (
-#ifdef SPLIT_HT
-            alloc_array(CODE, ht[0], (hashsize / 2) + 1, 0)
-            || alloc_array(CODE, ht[1], hashsize / 2, 0)
-#else
-            alloc_array(CODE, ht, hashsize, 0)
-#endif
-        ) {
-            oldhashsize = 0;
-            exit_stat = NOMEM;
-            return(NOMEM);
-        }
-        oldhashsize = hashsize;
-    }
-    return (OK);
-}
-
-# ifdef SPLIT_PFX
-    /*
-     * We have to split pfx[] table in half,
-     * because it's potentially larger than 64k bytes.
-     */
-#   define prefix(code)   (pfx[(code) & 1][(code) >> 1])
-# else
-    /*
-     * Then pfx[] can't be larger than 64k bytes,
-     * or we don't care if it is, so we don't split.
-     */
-#   define prefix(code) (pfx[code])
-# endif
-
-
-/* The initializing of the tables can be done quicker with memset() */
-/* but this way is portable through out the memory models.          */
-/* If you use Microsoft halloc() to allocate the arrays, then       */
-/* include the pragma #pragma function(memset)  and make sure that  */
-/* the length of the memory block is not greater than 64K.          */
-/* This also means that you MUST compile in a model that makes the  */
-/* default pointers to be far pointers (compact or large models).   */
-/* See the file COMPUSI.DOS to modify function emalloc().           */
-
-# ifdef SPLIT_HT
-    /*
-     * We have to split ht[] hash table in half,
-     * because it's potentially larger than 64k bytes.
-     */
-#   define probe(hash)    (ht[(hash) & 1][(hash) >> 1])
-#   define init_tables() \
-    { \
-      hash = hashsize >> 1; \
-      ht[0][hash] = 0; \
-      while (hash--) ht[0][hash] = ht[1][hash] = 0; \
-      highcode = ~(~(CODE)0 << (bits = INITBITS)); \
-      nextfree = (block_compress ? FIRSTFREE : 256); \
-    }
-
-# else
-
-    /*
-     * Then ht[] can't be larger than 64k bytes,
-     * or we don't care if it is, so we don't split.
-     */
-#   define probe(hash) (ht[hash])
-#   define init_tables() \
-    { \
-      hash = hashsize; \
-      while (hash--) ht[hash] = 0; \
-      highcode = ~(~(CODE)0 << (bits = INITBITS)); \
-      nextfree = (block_compress ? FIRSTFREE : 256); \
-    }
-
-# endif
-
-#ifdef COMP40
-/* table clear for block compress */
-/* this is for adaptive reset present in version 4.0 joe release */
-/* DjG, sets it up and returns TRUE to compress and FALSE to not compress */
-int cl_block ()     
-{
-    register long int rat;
-
-    checkpoint = in_count + CHECK_GAP;
-#ifndef NDEBUG
-	if ( debug ) {
-        fprintf ( stderr, "count: %ld, ratio: ", in_count );
-        prratio ( stderr, in_count, bytes_out );
-		fprintf ( stderr, "\n");
-	}
-#endif
-
-    if(in_count > 0x007fffff) {	/* shift will overflow */
-        rat = bytes_out >> 8;
-        if(rat == 0)       /* Don't divide by zero */
-            rat = 0x7fffffff;
-        else
-            rat = in_count / rat;
-    }
-    else
-        rat = (in_count << 8) / bytes_out;  /* 8 fractional bits */
-
-    if ( rat > ratio ){
-        ratio = rat;
-        return FALSE;
-    }
-    else {
-        ratio = 0;
-#ifndef NDEBUG
-        if(debug)
-    		fprintf ( stderr, "clear\n" );
-#endif
-        return TRUE;    /* clear the table */
-    }
-    return FALSE; /* don't clear the table */
-}
-#endif
-
-/*
- * compress stdin to stdout	<-- nope
- * NuLib: compress thread_eof bytes from srcfd, writing to dstfd
- *        Sets up a few things and then calls u_compress.
- */
-int u_compress(srcfd, dstfd, thread_eof)
-int srcfd, dstfd;
-long thread_eof;
-{
-    int src2, dst2;
-    long srcposn, dstposn;
-    static char *procName = "u_compress";
-
-    if ((srcposn = lseek(srcfd, 0L, S_REL)) < 0)
-	Fatal("Bad posn lseek(1)", procName);
-    if ((dstposn = lseek(dstfd, 0L, S_REL)) < 0)
-	Fatal("Bad posn lseek(2)", procName);
-
-    src2 = dup(srcfd);
-    dst2 = dup(dstfd);
-
-    /* NuLib: open new stdin/stdout, and seek */
-    if ((nustdin = fdopen(src2, FREAD_STR)) == NULL)
-        Fatal("can't fdopen() nustdin", procName);
-    if ((nustdout = fdopen(dst2, FWRITE_STR)) == NULL)
-        Fatal("can't fdopen() nustdout", procName);
-    setvbuf(nustdin,xbuf,_IOFBF,XBUFSIZE);  /* make the buffers larger */
-    setvbuf(nustdout,zbuf,_IOFBF,ZBUFSIZE);	/* (note setvbuf is a macro) */
-    if (fseek(nustdin, srcposn, S_ABS) < 0)	/* seek may not be needed */
-	Fatal("Bad stream posn lseek(1)", procName);
-    if (fseek(nustdout, dstposn, S_ABS) < 0)
-	Fatal("Bad stream posn lseek(2)", procName);
-
-    oldbits = 0;	/* init for putcode() */
-    compress(thread_eof);
-    check_error();
-
-    fclose(nustdin);		/* note this closes the duped fd */
-    fclose(nustdout);
-    return (exit_stat);
-}
-
-void compress(thread_eof)
-long thread_eof;
-{
-    int c,adjbits;
-    register HASH hash;
-    register CODE code;
-    HASH hashf[256];
-
-    max_bytes_out = thread_eof;		/* NuLib: don't exceed original size */
-    maxcode = Maxcode(maxbits);
-    hashsize = Hashsize(maxbits);
-
-#ifdef COMP40
-/* Only needed for adaptive reset */
-    checkpoint = CHECK_GAP;
-    ratio = 0;
-#endif
-
-    adjbits = maxbits -10;
-    for (c = 256; --c >= 0; ){
-        hashf[c] = ((( c &0x7) << 7) ^ c) << adjbits;
-    }
-    exit_stat = OK;
-    if (alloc_tables(maxcode, hashsize))  /* exit_stat already set */
-        return;
-    init_tables();
-    /* if not zcat or filter (NuLib: never happens) */
-    if(is_list && !zcat_flg) {  /* Open output file */
-        if (freopen(ofname, WRITE_FILE_TYPE, nustdout) == NULL) {
-            exit_stat = NOTOPENED;
-            return;
-        }
-        if (!quiet)
-            fprintf(stderr, "%s: ",ifname);
-        setvbuf(nustdout,zbuf,_IOFBF,ZBUFSIZE);
-    }
- /*
-   * Check the input stream for previously seen strings.  We keep
-   * adding characters to the previously seen prefix string until we
-   * get a character which forms a new (unseen) string.  We then send
-   * the code for the previously seen prefix string, and add the new
-   * string to our tables.  The check for previous strings is done by
-   * hashing.  If the code for the hash value is unused, then we have
-   * a new string.  If the code is used, we check to see if the prefix
-   * and suffix values match the current input; if so, we have found
-   * a previously seen string.  Otherwise, we have a hash collision,
-   * and we try secondary hash probes until we either find the current
-   * string, or we find an unused entry (which indicates a new string).
-   */
-    if (!nomagic) {
-        putc(magic_header[0], nustdout);	/* was putchar() */
-	putc(magic_header[1], nustdout);	/* was putchar() */
-        putc((char)(maxbits | block_compress), nustdout);	/*was putchar*/
-        if(ferror(nustdout)){  /* check it on entry */
-            exit_stat = WRITEERR;
-            return;
-        }
-        bytes_out = 3L;     /* includes 3-byte header mojo */
-    }
-    else
-        bytes_out = 0L;      /* no 3-byte header mojo */
-    in_count = 1L;
-    offset = 0;
-
-    if ((c = getc(nustdin)) == EOF) {	/* NuLib: was getchar() */
-        exit_stat = ferror(nustdin) ? READERR : OK;
-        return;
-    }
-    prefxcode = (CODE)c;
-
-    while ((c = getc(nustdin)) != EOF) {	/* NuLib: was getchar() */
-        in_count++;
-
-/* NuLib : May not be compressing entire file, so can't rely on EOF for end */
-	if (in_count > thread_eof) break;
-
-        hash = prefxcode ^ hashf[c];
-        /* I need to check that my hash value is within range
-        * because my 16-bit hash table is smaller than 64k.
-        */
-        if (hash >= hashsize)
-            hash -= hashsize;
-        if ((code = probe(hash)) != UNUSED) {
-            if (suffix(code) != (char)c || prefix(code) != prefxcode) {
-            /* hashdelta is subtracted from hash on each iteration of
-            * the following hash table search loop.  I compute it once
-            * here to remove it from the loop.
-            */
-                HASH hashdelta = (0x120 - c) << (adjbits);
-                do  {
-                    /* rehash and keep looking */
-                    assert(code >= FIRSTFREE && code <= maxcode);
-                    if (hash >= hashdelta) hash -= hashdelta;
-                        else hash += (hashsize - hashdelta);
-                    assert(hash < hashsize);
-                    if ((code = probe(hash)) == UNUSED)
-                        goto newcode;
-                } while (suffix(code) != (char)c || prefix(code) != prefxcode);
-            }
-            prefxcode = code;
-        }
-        else {
-            newcode: {
-                putcode(prefxcode, bits);
-                code = nextfree;
-                assert(hash < hashsize);
-                assert(code >= FIRSTFREE);
-                assert(code <= maxcode + 1);
-                if (code <= maxcode) {
-                    probe(hash) = code;
-                    prefix(code) = prefxcode;
-                    suffix(code) = (char)c;
-                    if (code > highcode) {
-                        highcode += code;
-                        ++bits;
-                    }
-                    nextfree = code + 1;
-                }
-#ifdef COMP40
-                else if (in_count >= checkpoint && block_compress ) {
-                    if (cl_block()){
-#else
-                else if (block_compress){
-#endif
-                        putcode((CODE)c, bits);
-                        putcode((CODE)CLEAR,bits);
-                        init_tables();
-                        if ((c = getc(nustdin)) == EOF)	/* NuLib: was getchar*/
-                            break;
-                        in_count++;
-#ifdef COMP40
-                    }
-#endif
-                }
-                prefxcode = (CODE)c;
-            }
-        }
-    }
-    putcode(prefxcode, bits);
-    putcode((CODE)CLEAR, 0);
-    if (ferror(nustdout)){ /* check it on exit */
-        exit_stat = WRITEERR;
-        return;
-    }
-    /*
-     * Print out stats on stderr
-     */
-    if(zcat_flg == 0 && !quiet) {
-#ifndef NDEBUG
-        fprintf( stderr,
-            "%ld chars in, (%ld bytes) out, compression factor: ",
-            in_count, bytes_out );
-        prratio( stderr, in_count, bytes_out );
-        fprintf( stderr, "\n");
-        fprintf( stderr, "\tCompression as in compact: " );
-        prratio( stderr, in_count-bytes_out, in_count );
-        fprintf( stderr, "\n");
-        fprintf( stderr, "\tLargest code (of last block) was %d (%d bits)\n",
-            prefxcode - 1, bits );
-#else
-        fprintf( stderr, "Compression: " );
-        prratio( stderr, in_count-bytes_out, in_count );
-#endif /* NDEBUG */
-    }
-    if(bytes_out > in_count)      /*  if no savings */
-        exit_stat = NOSAVING;
-
-    packedSize = bytes_out;	/* NuLib : return packed size in global */
-
-    return ;
-}
-
-CONST UCHAR rmask[9] = {0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff};
-
-void putcode(code,bits)
-CODE code;
-register int bits;
-{
-  static UCHAR outbuf[MAXBITS];
-  register UCHAR *buf;
-  register int shift;
-  register int ok_to_write;	/* NuLib (kludge... sorry) */
-
-  ok_to_write = (exit_stat != NOSAVING);
-
-  if (bits != oldbits) {
-    if (bits == 0) {
-      /* bits == 0 means EOF, write the rest of the buffer. */
-      if (offset > 0) {
-	int x = ((offset+7) >> 3);	/* NuLib */
-
-	if ((bytes_out + x) > max_bytes_out) {	/* NuLib */
-	    /* compression failed.  There's no clean way of bailing out
-	    /* (could use setjmp/longjmp, but that may not be supported
-	    /* on all systems), so just don't write anything.
-	     */
-	    exit_stat = NOSAVING;
-	    ok_to_write = FALSE;
-	} else {
-/*          fwrite(outbuf,1,(offset +7) >> 3, nustdout);*/
-	    fwrite(outbuf,1, x, nustdout);
-	}
-/*        bytes_out += ((offset +7) >> 3);*/
-	bytes_out += x;
-      }
-      offset = 0;
-      oldbits = 0;
-      fflush(nustdout);
-      return;
-    }
-    else {
-      /* Change the code size.  We must write the whole buffer,
-       * because the expand side won't discover the size change
-       * until after it has read a buffer full.
-       */
-      if (offset > 0) {
-        if (ok_to_write) fwrite(outbuf, 1, oldbits, nustdout);
-        bytes_out += oldbits;
-        offset = 0;
-      }
-      oldbits = bits;
-#ifndef NDEBUG
-      if ( debug )
-        fprintf( stderr, "\nChange to %d bits\n", bits );
-#endif /* !NDEBUG */
-    }
-  }
-  /*  Get to the first byte. */
-  buf = outbuf + ((shift = offset) >> 3);
-  if ((shift &= 7) != 0) {
-    *(buf) |= (*buf & rmask[shift]) | (UCHAR)(code << shift);
-    *(++buf) = (UCHAR)(code >> (8 - shift));
-    if (bits + shift > 16)
-        *(++buf) = (UCHAR)(code >> (16 - shift));
-  }
-  else {
-    /* Special case for fast execution */
-    *(buf) = (UCHAR)code;
-    *(++buf) = (UCHAR)(code >> 8);
-  }
-  if ((offset += bits) == (bits << 3)) {
-    bytes_out += bits;
-    if (ok_to_write) fwrite(outbuf,1,bits,nustdout);
-    offset = 0;
-  }
-  return;
-}
-
-int nextcode(codeptr)
-CODE *codeptr;
-/* Get the next code from input and put it in *codeptr.
- * Return (TRUE) on success, or return (FALSE) on end-of-file.
- * Adapted from COMPRESS V4.0.
- */
-{
-  register CODE code;
-  static int size;
-  static UCHAR inbuf[MAXBITS];
-  register int shift;
-  UCHAR *bp;
-
-  /* If the next entry is a different bit-size than the preceeding one
-   * then we must adjust the size and scrap the old buffer.
-   */
-  if (prevbits != bits) {
-    prevbits = bits;
-    size = 0;
-  }
-  /* If we can't read another code from the buffer, then refill it.
-   */
-  if (size - (shift = offset) < bits) {
-    static int bytesize;	/* NuLib: sigh */
-    /* Read more input and convert size from # of bytes to # of bits */
-
-    /* NuLib: stop after comp_thread_eof bytes */
-    if (nubytes_read >= nucomp_thread_eof)
-      return(NO);
-
-    /* NuLib: replace old fread command with... */
-    /*
-    if ((size = fread(inbuf, 1, bits, nustdin) << 3) <= 0 || ferror(nustdin))
-      return(NO);
-    */
-
-    bytesize = fread(inbuf, 1, bits, nustdin);
-    if (nubytes_read + bits > nucomp_thread_eof) {
-	bytesize = nucomp_thread_eof - nubytes_read;
-    }
-    size = bytesize << 3;
-    if (size <= 0 || ferror(nustdin))
-	return (NO);
-
-    /* NuLib: increment nubytes_read */
-    nubytes_read += (long) bytesize;
-
-    offset = shift = 0;
-  }
-  /* Get to the first byte. */
-  bp = inbuf + (shift >> 3);
-  /* Get first part (low order bits) */
-  code = (*bp++ >> (shift &= 7));
-  /* high order bits. */
-  code |= *bp++ << (shift = 8 - shift);
-  if ((shift += 8) < bits) code |= *bp << shift;
-  *codeptr = code & highcode;
-  offset += bits;
-  return (TRUE);
-}
-
-/*
- * NuLib: uncompress comp_thread_eof bytes from srcfd, writing to dstfd
- *        Sets up a few things and then calls compress.
- */
-int u_decompress(srcfd, dstfd, comp_thread_eof)
-int srcfd, dstfd;
-long comp_thread_eof;
-{
-    int src2, dst2;
-    long srcposn, dstposn;
-    static char *procName = "u_decompress";
-
-    if ((srcposn = lseek(srcfd, 0L, S_REL)) < 0)
-	Fatal("Bad posn lseek(1)", procName);
-    if ((dstposn = lseek(dstfd, 0L, S_REL)) < 0)
-	Fatal("Bad posn lseek(2)", procName);
-
-    src2 = dup(srcfd);
-    dst2 = dup(dstfd);
-
-    /* NuLib: open new stdin/stdout, and seek */
-    if ((nustdin = fdopen(src2, FREAD_STR)) == NULL)
-        Fatal("can't fdopen() nustdin", procName);
-    if ((nustdout = fdopen(dst2, FWRITE_STR)) == NULL)
-        Fatal("can't fdopen() nustdout", procName);
-    setvbuf(nustdin,zbuf,_IOFBF,ZBUFSIZE);  /* make the buffers larger */
-    setvbuf(nustdout,xbuf,_IOFBF,XBUFSIZE); /* (note order diff from comp) */
-    if (fseek(nustdin, srcposn, S_ABS) < 0)	/* seek may not be needed */
-	Fatal("Bad stream posn lseek(1)", procName);
-    if (fseek(nustdout, dstposn, S_ABS) < 0)
-	Fatal("Bad stream posn lseek(2)", procName);
-
-    /* Check the magic number */
-    if (!nomagic) {
-	if ((getc(nustdin)!=(magic_header[0] & 0xFF))	/* NuLib: was getchar*/
-	 || (getc(nustdin)!=(magic_header[1] & 0xFF))) {/* NuLib: was getchar*/
-	    fprintf(stderr, "decompress: not in compressed format\n");
-	    return(-1);	/* NuLib: was exit(ERROR) */
-	}
-	maxbits = getc(nustdin);    /* set -b from file (NuLib: was getchar) */
-	block_compress = maxbits & BLOCK_MASK;
-	maxbits &= BIT_MASK;
-	if(maxbits > MAXBITS) {
-	    fprintf(stderr,
-	    "decompress: compressed with %d bits, can only handle %d bits\n",
-	    maxbits, MAXBITS);
-	    return(-1);	/* NuLib: was exit(ERROR) */
-	}
-	nubytes_read = 3L;
-    } else {
-	nubytes_read = 0L;
-    }
-
-    nucomp_thread_eof = comp_thread_eof;
-/*    printf("src file posn = %ld\n", ftell(nustdin));*/
-    prevbits = 0;	/* init for nextcode() */
-    decompress();
-    check_error();
-
-    fclose(nustdin);		/* note this closes the duped fd */
-    fclose(nustdout);
-    return (exit_stat);
-}
-
-void decompress()
-{
-  register int i;
-  register CODE code;
-  char sufxchar;
-  CODE savecode;
-  FLAG fulltable, cleartable;
-  static char token[MAXTOKLEN];         /* String buffer to build token */
-
-  exit_stat = OK;
-
-  if (alloc_tables(maxcode = ~(~(CODE)0 << maxbits),0)) /* exit_stat already set */
-     return;
-
-    /* if not zcat or filter (NuLib: never) */
-    if(is_list && !zcat_flg) {  /* Open output file */
-        if (freopen(ofname, WRITE_FILE_TYPE, nustdout) == NULL) {
-            exit_stat = NOTOPENED;
-            return;
-        }
-        if (!quiet)
-            fprintf(stderr, "%s: ",ifname);
-        setvbuf(nustdout,xbuf,_IOFBF,XBUFSIZE);
-    }
-  cleartable = TRUE;
-  savecode = CLEAR;
-  offset = 0;
-  do {
-    if ((code = savecode) == CLEAR && cleartable) {
-      highcode = ~(~(CODE)0 << (bits = INITBITS));
-      fulltable = FALSE;
-      nextfree = (cleartable = block_compress) == FALSE ? 256 : FIRSTFREE;
-      if (!nextcode(&prefxcode))
-        break;
-      putc((sufxchar = (char)prefxcode), nustdout);
-      continue;
-    }
-    i = 0;
-    if (code >= nextfree && !fulltable) {
-      if (code != nextfree){
-        exit_stat = CODEBAD;
-/*	fprintf(stderr, "Bad code; nubytes_read = %ld\n", nubytes_read); */
-	/* CDEBUG */
-        return ;     /* Non-existant code */
-    }
-      /* Special case for sequence KwKwK (see text of article)         */
-      code = prefxcode;
-      token[i++] = sufxchar;
-    }
-    /* Build the token string in reverse order by chasing down through
-     * successive prefix tokens of the current token.  Then output it.
-     */
-    while (code >= 256) {
-#ifndef NDEBUG
-        /* These are checks to ease paranoia. Prefix codes must decrease
-         * monotonically, otherwise we must have corrupt tables.  We can
-         * also check that we haven't overrun the token buffer.
-         */
-        if (code <= prefix(code)){
-            exit_stat= TABLEBAD;
-            return;
-        }
-        if (i >= MAXTOKLEN){
-            exit_stat = TOKTOOBIG;
-            return;
-        }
-#endif
-      token[i++] = suffix(code);
-      code = prefix(code);
-    }
-    putc(sufxchar = (char)code, nustdout);
-    while (--i >= 0)
-        putc(token[i], nustdout);
-    if (ferror(nustdout)) {
-        exit_stat = WRITEERR;
-        return;
-    }
-    /* If table isn't full, add new token code to the table with
-     * codeprefix and codesuffix, and remember current code.
-     */
-    if (!fulltable) {
-      code = nextfree;
-      assert(256 <= code && code <= maxcode);
-      prefix(code) = prefxcode;
-      suffix(code) = sufxchar;
-      prefxcode = savecode;
-      if (code++ == highcode) {
-        if (highcode >= maxcode) {
-          fulltable = TRUE;
-          --code;
-        }
-        else {
-          ++bits;
-          highcode += code;           /* nextfree == highcode + 1 */
-        }
-      }
-      nextfree = code;
-    }
-
-  } while (nextcode(&savecode));
-  exit_stat = (ferror(nustdin))? READERR : OK;
-
-  return ;
-}
-
-
-/*
- * These are routines pulled out of "compress.c" from compress v4.3.
- */
-void prratio(stream, num, den)
-FILE *stream;
-long int num, den;
-{
-    register int q;         /* Doesn't need to be long */
-
-    if(num > 214748L) {     /* 2147483647/10000 */
-        q = (int) (num / (den / 10000L));
-    }
-    else {
-        q = (int) (10000L * num / den);     /* Long calculations, though */
-    }
-    if (q < 0) {
-        putc('-', stream);
-        q = -q;
-    }
-    fprintf(stream, "%d.%02d%%", q / 100, q % 100);
-}
-
-/*
- * Check exit status from compress() and decompress()
- *
- * exit_stat is a global var.  Either returns something interesting or
- * bails out completely.
- */
-int check_error()     /* returning OK continues with processing next file */
-{
-    prog_name = prgName;	/* NuLib: set prog_name to "nulib" */
-
-    switch(exit_stat) {
-  case OK:
-    return (OK);
-  case NOMEM:
-    if (do_decomp)
-        fprintf(stderr,"%s: not enough memory to decompress '%s'.\n", prog_name, ifname);
-    else
-        fprintf(stderr,"%s: not enough memory to compress '%s'.\n", prog_name, ifname);
-    return(OK);
-  case SIGNAL_ERROR:
-    fprintf(stderr,"%s: error setting signal interupt.\n",prog_name);
-    exit(ERROR);
-    break;
-  case READERR:
-    fprintf(stderr,"%s: read error on input '%s'.\n", prog_name, ifname);
-    break;
-  case WRITEERR:
-    fprintf(stderr,"%s: write error on output '%s'.\n", prog_name, ofname);
-    break;
-   case TOKTOOBIG:
-    fprintf(stderr,"%s: token too long in '%s'.\n", prog_name, ifname);
-    break;
-  case INFILEBAD:
-    fprintf(stderr, "%s: '%s' in unknown compressed format.\n", prog_name, ifname);
-    break;
- case CODEBAD:
-    fprintf(stderr,"%s: file token bad in '%s'.\n", prog_name,ifname);
-    break;
- case TABLEBAD:
-    fprintf(stderr,"%s: internal error -- tables corrupted.\n", prog_name);
-    break;
-  case NOTOPENED:
-    fprintf(stderr,"%s: could not open output file %s\n",prog_name,ofname);
-    exit(ERROR);
-    break;
-  case NOSAVING:
-    if (force)
-        exit_stat = OK;
-    return (OK);
-  default:
-    fprintf(stderr,"%s: internal error -- illegal return value = %d.\n", prog_name,exit_stat);
-  }
-  if (!zcat_flg && !keep_error){
-        fclose(nustdout);         /* won't get here without an error */
-        unlink ( ofname );
-    }
-  exit(exit_stat);
-  return(ERROR);
-}
-
-/*
- * These are routines from "compusi.c"
- */
-void version()
-{
-#ifdef XENIX
-#ifndef NDEBUG
-    fprintf(stderr, "%s\nOptions: Xenix %s MAXBITS = %d\n", rcs_ident,
-        "DEBUG",MAXBITS);
-#else
-    fprintf(stderr, "%s\nOptions: Xenix MAXBITS = %d\n", rcs_ident,MAXBITS);
-#endif
-#else
-#ifndef NDEBUG
-    fprintf(stderr, "%s\nOptions: Unix %s MAXBITS = %d\n", rcs_ident,
-        "DEBUG",MAXBITS);
-#else
-    fprintf(stderr, "%s\nOptions: Unix MAXBITS = %d\n", rcs_ident,MAXBITS);
-#endif
-#endif
-}
-
-ALLOCTYPE FAR *emalloc(x,y)
-unsigned int x;
-int y;
-{
-    ALLOCTYPE FAR *p;
-    p = (ALLOCTYPE FAR *)ALLOCATE(x,y);
-    return(p);
-}
-
-void efree(ptr)
-ALLOCTYPE FAR *ptr;
-{
-    FREEIT(ptr);
-}
-
+ END OF ARCHIVE