[comp.binaries.ibm.pc] ARC for SYS V: Part 2 of 5

boneill@hawk.ulowell.edu (SoftXc Coordinator) (03/26/88)

Article 3346 of net.sources:
Path: brl-smoke!brl-adm!rutgers!clyde!burl!codas!cpsc6a!cpsc6b!crs
From: crs@cpsc6b.cpsc6a.ATT.COM (C. R. Seaman)
Newsgroups: net.sources
Subject: System V ARC Ver. 1.1, Part 2 of 3
Message-ID: <248@cpsc6b.cpsc6a.ATT.COM>
Date: 30 Mar 87 23:45:34 GMT
Distribution: usa
Organization: AT&T (CPSC), Oakland, CA
Lines: 1719
Keywords: arc


[Line eater fodder........................]

This is Part 2 of System V ARC.  See Part 1 for details.

---------------------------- CUT HERE!! ---------------------------
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
#	arclst.c
#	arclzw.c
#	arcmatch.c
#	arcmisc.c
#	arcpack.c
# This archive created: Sun Mar 22 10:46:29 1987
# By:	C. R. Seaman ()
export PATH; PATH=/bin:/usr/bin:$PATH
echo shar: "extracting 'arclst.c'" '(5322 characters)'
if test -f 'arclst.c'
then
	echo shar: "will not over-write existing file 'arclst.c'"
else
sed 's/^	X//' << \SHAR_EOF > 'arclst.c'
	X/*
	X *	arclst.c	1.1
	X *
	X *	Author: Thom Henderson
	X *	Original System V port: Mike Stump
	X *	Enhancements, Bug fixes, and cleanup: Chris Seaman
	X *	Date: Fri Mar 20 09:57:02 1987
	X *	Last Mod.	3/21/87
	X *
	X */
	X
	X/*
	X * ARC - Archive utility - ARCLST
	X * 
	X * Version 2.34, created on 02/03/86 at 22:56:57
	X * 
	X * (C) COPYRIGHT 1985 by System Enhancement Associates; ALL RIGHTS RESERVED
	X * 
	X *     Description:
	X *          This file contains the routines used to list the contents
	X *          of an archive.
	X */
	X 
	X#include "arc.h"
	X
	XINT lstarc(argc,argv)                  /* list files in archive */
	XINT argc;                              /* number of arguments */
	Xchar *argv[];                          /* pointers to arguments */
	X{
	X    struct heads hdr;                  /* header data */
	X    INT list;                          /* true to list a file */
	X    INT did[MAXARG];                   /* true when argument was used */
	X    long tnum, tlen, tsize;            /* totals */
	X    INT n;                             /* index */
	X    INT lstfile();
	X
	X    tnum = tlen = tsize = 0;           /* reset totals */
	X
	X    for(n=0; n<argc; n++)              /* for each argument */
	X        did[n] = 0;                    /* reset usage flag */
	X    rempath(argc,argv);                /* strip off paths */
	X
	X    printf("Name            Length  ");
	X    if(bose)
	X        printf("  Stowage    SF   Size now");
	X    printf("  Date     ");
	X    if(bose)
	X        printf("  Time    CRC");
	X    printf("\n");
	X
	X    printf("==============  ========");
	X    if(bose)
	X        printf("  ========  ====  ========");
	X    printf("  =========");
	X    if(bose)
	X         printf("  ======  ====");
	X    printf("\n");
	X
	X    openarc(0);                        /* open archive for reading */
	X
	X    if(argc)                           /* if files were named */
	X    {
	X        while(readhdr(&hdr,arc))       /* process all archive files */
	X        {
	X            list = 0;                  /* reset list flag */
	X            for(n=0; n<argc; n++)      /* for each template given */
	X            {
	X                if(match(hdr.name,argv[n]))
	X                {
	X                    list = 1;          /* turn on list flag */
	X                    did[n] = 1;        /* turn on usage flag */
	X                    break;             /* stop looking */
	X                }
	X            }
	X
	X            if(list)                   /* if this file is wanted */
	X            {
	X                lstfile(&hdr);         /* then tell about it */
	X                tnum++;                /* update totals */
	X                tlen += hdr.length;
	X                tsize += hdr.size;
	X            }
	X
	X            fseek(arc,hdr.size,1);     /* move to next header */
	X        }
	X    }
	X
	X    else while(readhdr(&hdr,arc))      /* else report on all files */
	X    {
	X        lstfile(&hdr);
	X        tnum++;                        /* update totals */
	X        tlen += hdr.length;
	X        tsize += hdr.size;
	X        fseek(arc,hdr.size,1);         /* skip to next header */
	X    }
	X
	X    closearc(0);                       /* close archive after reading */
	X
	X    printf("        ======  ========");
	X    if (bose)
	X        printf("            ====  ========");
	X    printf("\n");
	X    printf("Total   %6ld  %8ld  ",tnum,tlen);
	X    if (bose)
	X    {
	X        if (tlen > 0)
	X            printf("          %3ld%%",100L - (100L*tsize)/tlen);
	X        else
	X            printf("            0%%");
	X        printf("  %8ld  ",tsize);
	X    }
	X    printf("\n");
	X
	X    if(note)
	X    {
	X        for(n=0; n<argc; n++)          /* report unused args */
	X        {
	X            if(!did[n])
	X            {
	X                printf("File not found: %s\n",argv[n]);
	X                nerrs++;
	X            }
	X        }
	X    }
	X}
	X
	Xstatic INT lstfile(hdr)                /* tell about a file */
	Xstruct heads *hdr;                     /* pointer to header data */
	X{
	X    INT yr, mo, dy;                    /* parts of a date */
	X    INT hh, mm;                        /* parts of a time */
	X
	X    static char *mon[] =               /* month abbreviations */
	X    {    "Jan",    "Feb",    "Mar",    "Apr",
	X         "May",    "Jun",    "Jul",    "Aug",
	X         "Sep",    "Oct",    "Nov",    "Dec"
	X    };
	X
	X    yr = (hdr->date >> 9) & 0x7f;      /* dissect the date */
	X    mo = (hdr->date >> 5) & 0x0f;
	X    dy = hdr->date & 0x1f;
	X
	X    hh = (hdr->time >> 11) & 0x1f;     /* dissect the time */
	X    mm = (hdr->time >> 5) & 0x3f;
	X
	X    printf("%-14s  %8ld  ",hdr->name,hdr->length);
	X
	X    if(bose)
	X    {
	X        switch(hdrver)
	X        {
	X        case 1:
	X        case 2:
	X            printf("   --   ");
	X            break;
	X        case 3:
	X            printf(" Packed ");
	X            break;
	X        case 4:
	X            printf("Squeezed");
	X            break;
	X        case 5:
	X        case 6:
	X        case 7:
	X            printf("crunched");
	X            break;
	X        case 8:
	X            printf("Crunched");
	X            break;
	X        default:
	X            printf("Unknown!");
	X        }
	X
	X        if (hdr->length > 0)
	X            printf("  %3d%%",100L - (100L*hdr->size)/hdr->length);
	X        else
	X            printf("    0%%");
	X        printf("  %8ld  ",hdr->size);
	X    }
	X
	X    printf("%2d %3s %02d", dy, mon[mo-1], (yr+80)%100);
	X
	X    if(bose)
	X        printf("  %2d:%02d%c  %04x",
	X              ((hh)?(hh>12?hh-12:hh):12), mm, (hh>11?'p':'a'),
	X              (unsigned INT)hdr->crc);
	X
	X    printf("\n");
	X}
SHAR_EOF
if test 5322 -ne "`wc -c < 'arclst.c'`"
then
	echo shar: "error transmitting 'arclst.c'" '(should have been 5322 characters)'
fi
fi
echo shar: "extracting 'arclzw.c'" '(25402 characters)'
if test -f 'arclzw.c'
then
	echo shar: "will not over-write existing file 'arclzw.c'"
else
sed 's/^	X//' << \SHAR_EOF > 'arclzw.c'
	X/*
	X *	arclzw.c	1.1
	X *
	X *	Author: Thom Henderson
	X *	Original System V port: Mike Stump
	X *	Enhancements, Bug fixes, and cleanup: Chris Seaman
	X *	Date: Fri Mar 20 09:57:02 1987
	X *	Last Mod.	3/21/87
	X *
	X */
	X
	X/*
	X * ARC - Archive utility - ARCLZW
	X * 
	X * Version 1.88, created on 01/20/86 at 16:47:04
	X * 
	X * (C) COPYRIGHT 1985 by System Enhancement Associates; ALL RIGHTS RESERVED
	X * 
	X *     Description:
	X *          This file contains the routines used to implement Lempel-Zev
	X *          data compression, which calls for building a coding table on
	X *          the fly.  This form of compression is especially good for encoding
	X *          files which contain repeated strings, and can often give dramatic
	X *          improvements over traditional Huffman SQueezing.
	X * 
	X *     Programming notes:
	X *          In this section I am drawing heavily on the COMPRESS program
	X *          from UNIX.  The basic method is taken from "A Technique for High
	X *          Performance Data Compression", Terry A. Welch, IEEE Computer
	X *          Vol 17, No 6 (June 1984), pp 8-19.  Also see "Knuth's Fundamental
	X *          Algorithms", Donald Knuth, Vol 3, Section 6.4.
	X * 
	X *          As best as I can tell, this method works by tracing down a hash
	X *          table of code strings where each entry has the property:
	X * 
	X *               if <string> <char> is in the table
	X *               then <string> is in the table.
	X */
	X
	X#include "arc.h"
	X
	X/* definitions for older style crunching */
	X
	X#define FALSE    0
	X#define TRUE     !FALSE
	X#define TABSIZE  4096
	X#define NO_PRED  0xFFFF
	X#define EMPTY    0xFFFF
	X#define NOT_FND  0xFFFF
	X
	Xstatic unsigned INT inbuf;             /* partial input code storage */
	Xstatic INT sp;                         /* current stack pointer */
	X
	Xstatic struct entry {                  /* string table entry format */
	X    char used;                         /* true when this entry is in use */
	X    unsigned INT next;                 /* ptr to next in collision list */
	X    unsigned INT predecessor;          /* code for preceeding string */
	X    unsigned char follower;            /* char following string */
	X}   string_tab[TABSIZE];               /* the code string table */
	X
	X
	X/* definitions for the new dynamic Lempel-Zev crunching */
	X
	X#define BITS   12                      /* maximum bits per code */
	X#define HSIZE  5003                    /* 80% occupancy */
	X#define INIT_BITS 9                    /* initial number of bits/code */
	X
	Xstatic INT n_bits;                     /* number of bits/code */
	Xstatic INT maxcode;                    /* maximum code, given n_bits */
	X#define MAXCODE(n)      ((1<<(n)) - 1) /* maximum code calculation */
	Xstatic INT maxcodemax =  1 << BITS;    /* largest possible code (+1) */
	X
	Xstatic unsigned char buf[BITS];        /* input/output buffer */
	X
	Xstatic unsigned char lmask[9] = {      /* left side masks */
	X    0xff, 0xfe, 0xfc,
	X    0xf8, 0xf0, 0xe0,
	X    0xc0, 0x80, 0x00
	X};
	Xstatic unsigned char rmask[9] = {      /* right side masks */
	X    0x00, 0x01, 0x03,
	X    0x07, 0x0f, 0x1f,
	X    0x3f, 0x7f, 0xff
	X};
	X
	Xstatic INT offset;                     /* byte offset for code output */
	Xstatic long in_count;                  /* length of input */
	Xstatic long bytes_out;                 /* length of compressed output */
	Xstatic unsigned INT ent;
	X
	X/*
	X * To save much memory (which we badly need at this point), we overlay
	X * the table used by the previous version of Lempel-Zev with those used
	X * by the new version.  Since no two of these routines will be used
	X * together, we can safely do this.  Note that the tables used for Huffman
	X * squeezing may NOT overlay these, since squeezing and crunching are done
	X * in parallel.
	X */
	X
	Xstatic long htab[HSIZE];               /* hash code table   (crunch) */
	Xstatic unsigned INT codetab[HSIZE];    /* string code table (crunch) */
	X
	Xstatic unsigned INT *prefix = codetab; /* prefix code table (uncrunch) */
	X
	Xstatic unsigned char suffix[HSIZE];    /* suffix table (uncrunch) */
	Xstatic INT free_ent;                   /* first unused entry */
	Xstatic INT firstcmp;                   /* true at start of compression */
	Xstatic unsigned char stack[HSIZE];     /* local push/pop stack */
	X
	X/*
	X * block compression parameters -- after all codes are used up,
	X * and compression rate changes, start over.
	X */
	X
	Xstatic INT clear_flg;
	Xstatic long ratio;
	X#define CHECK_GAP 10000                /* ratio check interval */
	Xstatic long checkpoint;
	X
	X/*
	X * the next two codes should not be changed lightly, as they must not
	X * lie within the contiguous general code space.
	X */
	X
	X#define FIRST   257                    /* first free entry */
	X#define CLEAR   256                    /* table clear output code */
	X
	Xstatic INT cl_block(t)                 /* table clear for block compress */
	XFILE *t;                               /* our output file */
	X{
	X    long rat;
	X    INT putcode();
	X
	X    checkpoint = in_count + CHECK_GAP;
	X
	X    if (in_count > 0x007fffff)         /* shift will overflow */
	X    {
	X        rat = bytes_out >> 8;
	X        if (rat == 0)                  /* Don't divide by zero */
	X            rat = 0x7fffffff;
	X        else
	X            rat = in_count / rat;
	X    }
	X    else
	X        rat = (in_count<<8)/bytes_out;/* 8 fractional bits */
	X
	X    if (rat > ratio)
	X        ratio = rat;
	X    else
	X    {
	X        ratio = 0;
	X        setmem(htab,HSIZE*sizeof(long),0xff);
	X        free_ent = FIRST;
	X        clear_flg = 1;
	X        putcode(CLEAR,t);
	X    }
	X}
	X
	X/*
	X * Output a given code.
	X * Inputs:
	X *      code:   A n_bits-bit integer.  If == -1, then EOF.  This assumes
	X *              that n_bits =< (long)wordsize - 1.
	X * Outputs:
	X *      Outputs code to the file.
	X * Assumptions:
	X *      Chars are 8 bits long.
	X * Algorithm:
	X *      Maintain a BITS character long buffer (so that 8 codes will
	X * fit in it exactly).  When the buffer fills up empty it and start over.
	X */
	X
	Xstatic INT putcode(code,t)             /* output a code */
	XINT code;                              /* code to output */
	XFILE *t;                               /* where to put it */
	X{
	X    INT r_off = offset;                /* right offset */
	X    INT bits = n_bits;                 /* bits to go */
	X    unsigned char *bp = buf;           /* buffer pointer */
	X    INT n;                             /* index */
	X
	X    if (code >= 0)                     /* if a real code */
	X    {
	X        bp += (r_off >> 3);            /* Get to the first byte. */
	X        r_off &= 7;
	X
	X        /*
	X         * Since code is always >= 8 bits, only need to mask the first
	X         * hunk on the left.
	X         */
	X
	X        *bp = (*bp&rmask[r_off]) | (code<<r_off) & lmask[r_off];
	X        bp++;
	X        bits -= (8 - r_off);
	X        code >>= (8 - r_off);
	X
	X        /* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */
	X
	X        if (bits >= 8)
	X        {
	X            *bp++ = code;
	X            code >>= 8;
	X            bits -= 8;
	X        }
	X
	X        /* Last bits. */
	X
	X        if (bits)
	X            *bp = code;
	X
	X        offset += n_bits;
	X
	X        if (offset == (n_bits << 3))
	X        {
	X            bp = buf;
	X            bits = n_bits;
	X            bytes_out += bits;
	X            do
	X                putc_pak(*bp++,t);
	X            while (--bits);
	X            offset = 0;
	X        }
	X
	X        /*
	X         * If the next entry is going to be too big for the code size,
	X         * then increase it, if possible.
	X         */
	X
	X        if (free_ent>maxcode || clear_flg>0)
	X        {
	X            /*
	X             * Write the whole buffer, because the input side won't
	X             * discover the size increase until after it has read it.
	X             */
	X
	X            if (offset > 0)
	X            {
	X                bp = buf;              /* reset pointer for writing */
	X                bytes_out += n = n_bits;
	X                while (n--)
	X                    putc_pak(*bp++,t);
	X            }
	X            offset = 0;
	X
	X            if (clear_flg)             /* reset if clearing */
	X            {
	X                maxcode = MAXCODE(n_bits = INIT_BITS);
	X                clear_flg = 0;
	X            }
	X            else                       /* else use more bits */
	X            {
	X                n_bits++;
	X                if (n_bits == BITS)
	X                    maxcode = maxcodemax;
	X                else
	X                    maxcode = MAXCODE(n_bits);
	X            }
	X        }
	X    }
	X    else                               /* dump the buffer on EOF */
	X    {
	X        bytes_out += n = (offset+7) / 8;
	X
	X        if (offset > 0)
	X            while (n--)
	X                putc_pak(*bp++,t);
	X        offset = 0;
	X    }
	X}
	X
	X/*
	X * Read one code from the standard input.  If EOF, return -1.
	X * Inputs:
	X *      cmpin
	X * Outputs:
	X *      code or -1 is returned.
	X */
	X
	Xstatic INT getcode(f)                  /* get a code */
	XFILE *f;                               /* file to get from */
	X{
	X    INT code;
	X    static INT offset = 0, size = 0;
	X    INT r_off, bits;
	X    unsigned char *bp = buf;
	X
	X    if (clear_flg > 0 || offset >= size || free_ent > maxcode)
	X    {
	X        /*
	X         * If the next entry will be too big for the current code
	X         * size, then we must increase the size.  This implies reading
	X         * a new buffer full, too.
	X         */
	X
	X        if (free_ent > maxcode)
	X        {
	X            n_bits++;
	X            if (n_bits == BITS)
	X                maxcode = maxcodemax;  /* won't get any bigger now */
	X            else
	X                maxcode = MAXCODE(n_bits);
	X        }
	X        if (clear_flg > 0)
	X        {
	X            maxcode = MAXCODE(n_bits = INIT_BITS);
	X            clear_flg = 0;
	X        }
	X
	X        for (size=0; size<n_bits; size++)
	X        {
	X            if ((code=getc_unp(f))==EOF)
	X                break;
	X            else
	X                buf[size] = code;
	X        }
	X        if (size <= 0)
	X            return(-1);                /* end of file */
	X
	X        offset = 0;
	X
	X        /* Round size down to integral number of codes */
	X
	X        size = (size << 3)-(n_bits - 1);
	X    }
	X    r_off = offset;
	X    bits = n_bits;
	X
	X    /* Get to the first byte. */
	X
	X    bp +=(r_off >> 3);
	X    r_off &= 7;
	X
	X    /* Get first part (low order bits) */
	X
	X    code = (*bp++ >> r_off);
	X    bits -= 8 - r_off;
	X    r_off = 8 - r_off;                 /* now, offset into code word */
	X
	X    /* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */
	X
	X    if (bits >= 8)
	X    {
	X        code |= *bp++ << r_off;
	X        r_off += 8;
	X        bits -= 8;
	X    }
	X
	X    /* high order bits. */
	X
	X    code |= (*bp & rmask[bits]) << r_off;
	X    offset += n_bits;
	X
	X    return(code);
	X}
	X
	X/*
	X * compress a file
	X *
	X * Algorithm:  use open addressing double hashing (no chaining) on the
	X * prefix code / next character combination.  We do a variant of Knuth's
	X * algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime
	X * secondary probe.  Here, the modular division first probe is gives way
	X * to a faster exclusive-or manipulation.  Also do block compression with
	X * an adaptive reset, where the code table is cleared when the compression
	X * ratio decreases, but after the table fills.  The variable-length output
	X * codes are re-sized at this point, and a special CLEAR code is generated
	X * for the decompressor.
	X */
	X
	XINT init_cm(f,t)                       /* initialize for compression */
	XFILE *f;                               /* file we will be compressing */
	XFILE *t;                               /* where we will put it */
	X{
	X    offset = 0;
	X    bytes_out = 1;
	X    clear_flg = 0;
	X    ratio = 0;
	X    in_count = 1;
	X    checkpoint = CHECK_GAP;
	X    maxcode = MAXCODE(n_bits = INIT_BITS);
	X    free_ent = FIRST;
	X    setmem(htab,HSIZE*sizeof(long),0xff);
	X    n_bits = INIT_BITS;                /* set starting code size */
	X
	X    putc_pak(BITS,t);                  /* note our max code length */
	X
	X    firstcmp = 1;                      /* next byte will be first */
	X}
	X
	XINT putc_cm(c,t)                       /* compress a character */
	Xunsigned char c;                       /* character to compress */
	XFILE *t;                               /* where to put it */
	X{
	X    static long fcode;
	X    static INT hshift;
	X    INT i;
	X    INT disp;
	X
	X    if (firstcmp)                      /* special case for first byte */
	X    {
	X        ent = c;                       /* remember first byte */
	X
	X        hshift = 0;
	X        for (fcode=(long)HSIZE;  fcode<65536L; fcode*=2L)
	X            hshift++;
	X        hshift = 8 - hshift;           /* set hash code range bund */
	X
	X        firstcmp = 0;                  /* no longer first */
	X        return;
	X    }
	X
	X    in_count++;
	X    fcode =(long)(((long)c << BITS)+ent);
	X    i = (c<<hshift)^ent;               /* xor hashing */
	X
	X    if (htab[i]==fcode)
	X    {
	X        ent = codetab[i];
	X        return;
	X    }
	X    else if (htab[i]<0)                /* empty slot */
	X        goto nomatch;
	X    disp = HSIZE - i;                  /* secondary hash (after G.Knott) */
	X    if (i == 0)
	X        disp = 1;
	X
	Xprobe:
	X    if ((i -= disp) < 0)
	X        i += HSIZE;
	X
	X    if (htab[i] == fcode)
	X    {
	X        ent = codetab[i];
	X        return;
	X    }
	X    if (htab[i] > 0)
	X        goto probe;
	X
	Xnomatch:
	X    putcode(ent,t);
	X    ent = c;
	X    if (free_ent < maxcodemax)
	X    {
	X        codetab[i] = free_ent++;       /* code -> hashtable */
	X        htab[i] = fcode;
	X    }
	X    else if ((long)in_count >= checkpoint)
	X        cl_block(t);
	X}
	X
	Xlong pred_cm(t)                        /* finish compressing a file */
	XFILE *t;                               /* where to put it */
	X{
	X    putcode(ent,t);                    /* put out the final code */
	X    putcode(-1,t);                     /* tell output we are done */
	X
	X    return(bytes_out);                 /* say how big it got */
	X}
	X
	X/*
	X * Decompress a file.  This routine adapts to the codes in the file
	X * building the string table on-the-fly; requiring no table to be stored
	X * in the compressed file.  The tables used herein are shared with those of
	X * the compress() routine.  See the definitions above.
	X */
	X
	XINT decomp(f,t)                        /* decompress a file */
	XFILE *f;                               /* file to read codes from */
	XFILE *t;                               /* file to write text to */
	X{
	X    unsigned char *stackp;
	X    INT finchar;
	X    INT code, oldcode, incode;
	X
	X    if ((code=getc_unp(f))!=BITS)
	X        abort("File packed with %d bits, I can only handle %d",code,BITS);
	X
	X    n_bits = INIT_BITS;                /* set starting code size */
	X    clear_flg = 0;
	X
	X    /* As above, initialize the first 256 entries in the table. */
	X
	X    maxcode = MAXCODE(n_bits=INIT_BITS);
	X    for (code = 255; code >= 0; code--)
	X    {
	X        prefix[code] = 0;
	X        suffix[code] = (unsigned char)code;
	X    }
	X    free_ent = FIRST;
	X
	X    finchar = oldcode = getcode(f);
	X    if (oldcode == -1)                 /* EOF already? */
	X        return;                        /* Get out of here */
	X    putc_ncr((char)finchar,t);         /* first code must be 8 bits=char */
	X    stackp = stack;
	X
	X    while ((code = getcode(f))> -1)
	X    {
	X        if (code==CLEAR)
	X        {
	X            for (code = 255; code >= 0; code--)
	X                prefix[code] = 0;
	X            clear_flg = 1;
	X            free_ent = FIRST - 1;
	X            if ((code=getcode(f))==-1) /* O, untimely death! */
	X                break;
	X        }
	X        incode = code;
	X
	X        /* Special case for KwKwK string. */
	X
	X        if (code >= free_ent)
	X        {
	X            *stackp++ = finchar;
	X            code = oldcode;
	X        }
	X
	X        /* Generate output characters in reverse order */
	X
	X        while (code >= 256)
	X        {
	X            *stackp++ = suffix[code];
	X            code = prefix[code];
	X        }
	X        *stackp++ = finchar = suffix[code];
	X
	X        /* And put them out in forward order */
	X
	X        do
	X            putc_ncr(*--stackp,t);
	X        while (stackp > stack);
	X
	X        /* Generate the new entry. */
	X
	X        if ((code=free_ent) < maxcodemax)
	X        {
	X            prefix[code] = (unsigned short)oldcode;
	X            suffix[code] = finchar;
	X            free_ent = code+1;
	X        }
	X
	X        /* Remember previous code. */
	X
	X        oldcode = incode;
	X    }
	X}
	X
	X
	X/*
	X * Please note how much trouble it can be to maintain upwards
	X * compatibility.  All that follows is for the sole purpose of unpacking
	X * files which were packed using an older method.
	X */
	X
	X
	X/*
	X *  The h() pointer points to the routine to use for calculating a hash
	X *  value.  It is set in the init routines to point to either of oldh()
	X *  or newh().
	X *
	X *  oldh() calculates a hash value by taking the middle twelve bits
	X *  of the square of the key.
	X *
	X *  newh() works somewhat differently, and was tried because it makes
	X *  ARC about 23% faster.  This approach was abandoned because dynamic
	X *  Lempel-Zev (above) works as well, and packs smaller also.  However,
	X *  inadvertent release of a developmental copy forces us to leave this in.
	X */
	X
	Xstatic unsigned INT (*h)();            /* pointer to hash function */
	X
	Xstatic unsigned INT oldh(pred,foll)    /* old hash function */
	Xunsigned INT pred;                     /* code for preceeding string */
	Xunsigned char foll;                    /* value of following char */
	X{
	X    long local;                        /* local hash value */
	X
	X    local = (pred + foll) | 0x0800;    /* create the hash key */
	X    local *= local;                    /* square it */
	X    return((local >> 6) & 0x0FFF);     /* return the middle 12 bits */
	X}
	X
	Xstatic unsigned INT newh(pred,foll)    /* new hash function */
	Xunsigned INT pred;                     /* code for preceeding string */
	Xunsigned char foll;                    /* value of following char */
	X{
	X    return(((pred+foll)*15073)&0xFFF); /* faster hash */
	X}
	X
	X/*
	X *  The eolist() function is used to trace down a list of entries with
	X *  duplicate keys until the last duplicate is found.
	X */
	X
	Xstatic unsigned INT eolist(index)      /* find last duplicate */
	Xunsigned INT index;
	X{
	X    INT temp;
	X
	X    while (temp=string_tab[index].next) /* while more duplicates */
	X        index = temp;
	X
	X    return(index);
	X}
	X
	X/*
	X *  The hash() routine is used to find a spot in the hash table for a new
	X *  entry.  It performs a "hash and linear probe" lookup, using h() to
	X *  calculate the starting hash value and eolist() to perform the linear
	X *  probe.  This routine DOES NOT detect a table full condition.  That
	X *  MUST be checked for elsewhere.
	X */
	X
	Xstatic unsigned INT hash(pred,foll)    /* find spot in the string table */
	Xunsigned INT pred;                     /* code for preceeding string */
	Xunsigned char foll;                    /* char following string */
	X{
	X    unsigned INT local, tempnext;      /* scratch storage */
	X    struct entry *ep;                  /* allows faster table handling */
	X
	X    local = (*h)(pred,foll);           /* get initial hash value */
	X
	X    if (!string_tab[local].used)       /* if that spot is free */
	X        return(local);                 /* then that's all we need */
	X    else                               /* else a collision has occured */
	X    {
	X        local = eolist(local);         /* move to last duplicate */
	X
	X        /*
	X         * We must find an empty spot. We start looking 101 places
	X         * down the table from the last duplicate.
	X         */
	X
	X        tempnext = (local+101) & 0x0FFF;
	X        ep = &string_tab[tempnext];    /* initialize pointer */
	X
	X        while (ep->used)               /* while empty spot not found */
	X        {
	X            if (++tempnext==TABSIZE)   /* if we are at the end */
	X            {
	X                tempnext = 0;          /* wrap to beginning of table*/
	X                ep = string_tab;
	X            }
	X            else
	X                ++ep;                  /* point to next element in table */
	X        }
	X
	X        /*
	X         * local still has the pointer to the last duplicate, while
	X         * tempnext has the pointer to the spot we found.  We use
	X         * this to maintain the chain of pointers to duplicates.
	X         */
	X
	X        string_tab[local].next = tempnext;
	X
	X        return(tempnext);
	X    }
	X}
	X
	X/*
	X * The init_tab() routine is used to initialize our hash table.
	X * You realize, of course, that "initialize" is a complete misnomer.
	X */
	X
	Xstatic INT init_tab()                  /* set ground state in hash table */
	X{
	X    unsigned INT i;                    /* table index */
	X    INT upd_tab();
	X
	X    setmem((char *)string_tab,sizeof(string_tab),0);
	X
	X    for (i=0; i<256; i++)              /* list all single byte strings */
	X        upd_tab(NO_PRED,i);
	X
	X    inbuf = EMPTY;                     /* nothing is in our buffer */
	X}
	X
	X/*
	X * The upd_tab routine is used to add a new entry to the string table.
	X * As previously stated, no checks are made to ensure that the table
	X * has any room.  This must be done elsewhere.
	X */
	X
	XINT upd_tab(pred,foll)                 /* add an entry to the table */
	Xunsigned INT pred;                     /* code for preceeding string */
	Xunsigned INT foll;                     /* character which follows string */
	X{
	X    struct entry *ep;                  /* pointer to current entry */
	X
	X    /* calculate offset just once */
	X
	X    ep = &string_tab[hash(pred,foll)];
	X
	X    ep->used = TRUE;                   /* this spot is now in use */
	X    ep->next = 0;                      /* no duplicates after this yet */
	X    ep->predecessor = pred;            /* note code of preceeding string */
	X    ep->follower = foll;               /* note char after string */
	X}
	X
	X/*
	X * This algorithm encoded a file into twelve bit strings (three nybbles).
	X * The gocode() routine is used to read these strings a byte (or two)
	X * at a time.
	X */
	X
	Xstatic INT gocode(fd)                  /* read in a twelve bit code */
	XFILE *fd;                              /* file to get code from */
	X{
	X    unsigned INT localbuf, returnval;
	X
	X    if (inbuf==EMPTY)                  /* if on a code boundary */
	X    {
	X        if ((localbuf=getc_unp(fd))==EOF) /* get start of next code */
	X            return(EOF);               /* pass back end of file status */
	X        localbuf &= 0xFF;              /* mask down to true byte value */
	X        if ((inbuf=getc_unp(fd))==EOF) /* get end of code, start of next */
	X            return(EOF);               /* this should never happen */
	X        inbuf &= 0xFF;                 /* mask down to true byte value */
	X
	X        returnval = ((localbuf<<4)&0xFF0) + ((inbuf>>4)&0x00F);
	X        inbuf &= 0x000F;               /* leave partial code pending */
	X    }
	X    else                               /* buffer contains first nybble */
	X    {
	X        if ((localbuf=getc_unp(fd))==EOF)
	X            return(EOF);
	X        localbuf &= 0xFF;
	X
	X        returnval = localbuf + ((inbuf<<8)&0xF00);
	X        inbuf = EMPTY;                 /* note no hanging nybbles */
	X    }
	X    return(returnval);                 /* pass back assembled code */
	X}
	X
	Xstatic INT push(c)                     /* push char onto stack */
	XINT c;                                 /* character to push */
	X{
	X    stack[sp] = ((char) c);            /* coerce integer into a char */
	X
	X    if (++sp >= TABSIZE)
	X        abort("Stack overflow\n");
	X}
	X
	Xstatic INT pop()                       /* pop character from stack */
	X{
	X    if (sp>0)
	X        return(((INT) stack[--sp]));   /* leave ptr at next empty slot */
	X    else
	X        return(EMPTY);
	X}
	X
	X/***** LEMPEL-ZEV DECOMPRESSION *****/
	X
	Xstatic INT code_count;                 /* needed to detect table full */
	Xstatic INT firstc;                     /* true only on first character */
	X
	XINT init_ucr(new)                      /* get set for uncrunching */
	XINT new;                               /* true to use new hash function */
	X{
	X    if (new)                           /* set proper hash function */
	X        h = newh;
	X    else
	X        h = oldh;
	X
	X    sp = 0;                            /* clear out the stack */
	X    init_tab();                        /* set up atomic code definitions */
	X    code_count = TABSIZE - 256;        /* note space left in table */
	X    firstc = 1;                        /* true only on first code */
	X}
	X
	XINT getc_ucr(f)                        /* get next uncrunched byte */
	XFILE *f;                               /* file containing crunched data */
	X{
	X    INT code, newcode;
	X    static INT oldcode, finchar;
	X    struct entry *ep;                  /* allows faster table handling */
	X
	X    if (firstc)                        /* first code is always known */
	X    {
	X        firstc = FALSE;                /* but next will not be first */
	X        oldcode = gocode(f);
	X        return(finchar = string_tab[oldcode].follower);
	X    }
	X
	X    if (!sp)                           /* if stack is empty */
	X    {
	X        if ((code=newcode=gocode(f))==EOF)
	X            return(EOF);
	X
	X        ep = &string_tab[code];        /* initialize pointer */
	X
	X        if (!ep->used)                 /* if code isn't known */
	X        {
	X            code = oldcode;
	X            ep = &string_tab[code];    /* re-initialize pointer */
	X            push(finchar);
	X        }
	X
	X        while (ep->predecessor!=NO_PRED)
	X        {
	X            push(ep->follower);        /* decode string backwards */
	X            code = ep->predecessor;
	X            ep = &string_tab[code];
	X        }
	X
	X        push(finchar=ep->follower);    /* save first character also */
	X
	X        /*
	X         * The above loop will terminate, one way or another,
	X         * with string_tab[code].follower equal to the first
	X         * character in the string.
	X         */
	X
	X        if (code_count)                /* if room left in string table */
	X        {
	X            upd_tab(oldcode,finchar);
	X            --code_count;
	X        }
	X
	X        oldcode = newcode;
	X    }
	X
	X    return(pop());                     /* return saved character */
	X}
SHAR_EOF
if test 25402 -ne "`wc -c < 'arclzw.c'`"
then
	echo shar: "error transmitting 'arclzw.c'" '(should have been 25402 characters)'
fi
fi
echo shar: "extracting 'arcmatch.c'" '(3579 characters)'
if test -f 'arcmatch.c'
then
	echo shar: "will not over-write existing file 'arcmatch.c'"
else
sed 's/^	X//' << \SHAR_EOF > 'arcmatch.c'
	X/*
	X *	arcmatch.c	1.1
	X *
	X *	Author: Thom Henderson
	X *	Original System V port: Mike Stump
	X *	Enhancements, Bug fixes, and cleanup: Chris Seaman
	X *	Date: Fri Mar 20 09:57:02 1987
	X *	Last Mod.	3/22/87
	X *
	X */
	X
	X/*
	X * ARC - Archive utility - ARCMATCH
	X * 
	X * Version 2.17, created on 12/17/85) at 20:32:18
	X * 
	X * (C) COPYRIGHT 1985 by System Enhancement Associates; ALL RIGHTS RESERVED
	X * 
	X *     Description:
	X *          This file contains service routines needed to maintain an archive.
	X */
	X
	X#include "arc.h"
	X#include <sys/types.h>
	X#include <sys/dir.h>
	X
	X#define ASTERISK '*'		/* The '*' metacharacter */
	X#define QUESTION '?'		/* The '?' metacharacter */
	X#define BACK_SLASH '\\'         /* The '\' metacharacter */
	X#define LEFT_BRACKET '['	/* The '[' metacharacter */
	X#define RIGHT_BRACKET ']'	/* The ']' metacharacter */
	X
	X#define IS_OCTAL(ch) (ch >= '0' && ch <= '7')
	X
	Xtypedef INT BOOLEAN;
	X#define VOID short
	X#define TRUE 1
	X#define FALSE 0
	X#define EOS '\000'
	X
	Xstatic BOOLEAN do_list();
	Xstatic char nextch();
	Xstatic VOID list_parse();
	X
	Xint match(string, pattern)
	Xchar *string;
	Xchar *pattern;
	X{
	X    register int ismatch;
	X
	X    ismatch = FALSE;
	X    switch (*pattern)
	X    {
	X    case ASTERISK:
	X        pattern++;
	X        do
	X        {
	X            ismatch = match (string, pattern);
	X        }
	X        while (!ismatch && *string++ != EOS);
	X        break;
	X    case QUESTION:
	X        if (*string != EOS)
	X            ismatch = match (++string, ++pattern);
	X        break;
	X    case EOS:
	X        if (*string == EOS)
	X            ismatch = TRUE;
	X        break;
	X    case LEFT_BRACKET:
	X        if (*string != EOS)
	X            ismatch = do_list (string, pattern);
	X        break;
	X    case BACK_SLASH:
	X        pattern++;
	X    default:
	X        if (*string == *pattern)
	X        {
	X            string++;
	X            pattern++;
	X            ismatch = match (string, pattern);
	X        }
	X        else
	X            ismatch = FALSE;
	X        break;
	X    }
	X    return(ismatch);
	X}
	X
	Xstatic BOOLEAN do_list (string, pattern)
	Xregister char *string;
	Xchar *pattern;
	X{
	X    register BOOLEAN ismatch;
	X    register BOOLEAN if_found;
	X    register BOOLEAN if_not_found;
	X    auto char lower;
	X    auto char upper;
	X
	X    pattern++;
	X    if (*pattern == '!')
	X    {
	X        if_found = FALSE;
	X        if_not_found = TRUE;
	X        pattern++;
	X    }
	X    else
	X    {
	X        if_found = TRUE;
	X        if_not_found = FALSE;
	X    }
	X    ismatch = if_not_found;
	X    while (*pattern != ']' && *pattern != EOS)
	X    {
	X        list_parse(&pattern, &lower, &upper);
	X        if (*string >= lower && *string <= upper)
	X        {
	X            ismatch = if_found;
	X            while (*pattern != ']' && *pattern != EOS) pattern++;
	X        }
	X    }
	X
	X    if (*pattern++ != ']')
	X        abort("Character class error");
	X    else
	X        if (ismatch)
	X            ismatch = match (++string, pattern);
	X
	X    return(ismatch);
	X}
	X
	Xstatic VOID list_parse (patp, lowp, highp)
	Xchar **patp;
	Xchar *lowp;
	Xchar *highp;
	X{
	X    *lowp = nextch (patp);
	X    if (**patp == '-')
	X    {
	X        (*patp)++;
	X        *highp = nextch(patp);
	X    }
	X    else
	X        *highp = *lowp;
	X}
	X
	Xstatic char nextch (patp)
	Xchar **patp;
	X{
	X    register char ch;
	X    register char chsum;
	X    register INT count;
	X
	X    ch = *(*patp)++;
	X    if (ch == '\\')
	X    {
	X        ch = *(*patp)++;
	X        if (IS_OCTAL (ch))
	X        {
	X            chsum = 0;
	X            for (count = 0; count < 3 && IS_OCTAL (ch); count++)
	X            {
	X                chsum *= 8;
	X                chsum += ch - '0';
	X                ch = *(*patp)++;
	X            }
	X            (*patp)--;
	X            ch = chsum;
	X        }
	X    }
	X    return(ch);
	X}
SHAR_EOF
if test 3579 -ne "`wc -c < 'arcmatch.c'`"
then
	echo shar: "error transmitting 'arcmatch.c'" '(should have been 3579 characters)'
fi
fi
echo shar: "extracting 'arcmisc.c'" '(5876 characters)'
if test -f 'arcmisc.c'
then
	echo shar: "will not over-write existing file 'arcmisc.c'"
else
sed 's/^	X//' << \SHAR_EOF > 'arcmisc.c'
	X/*
	X *	arcmisc.c	1.1
	X *
	X *	Author: Thom Henderson
	X *	Original System V port: Mike Stump
	X *	Enhancements, Bug fixes, and cleanup: Chris Seaman
	X *	Date: Fri Mar 20 09:57:02 1987
	X *	Last Mod.	3/21/87
	X *
	X */
	X
	X/*
	X * ARC - Archive utility - ARCMISC
	X * 
	X * Description:
	X *      This file contains miscellaneous routines for string
	X *      management, file management, and program control.
	X */
	X
	X#include "arc.h"
	X#include <ctype.h>
	X
	XINT rempath(nargs,arg)               /* remove paths from filenames */
	XINT nargs;                           /* number of names */
	Xchar *arg[];                         /* pointers to names */
	X{
	X    char *i, *strrchr();             /* string index, reverse indexer */
	X    INT n;                           /* index */
	X
	X    for(n=0; n<nargs; n++)           /* for each supplied name */
	X    {
	X        i=strrchr(arg[n],'/');       /* search for end of path */
	X        if(i)                        /* if path was found */
	X            arg[n] = i+1;            /* then skip it */
	X    }
	X}
	X
	X/* make a file name using a template */
	Xchar *makefnam(rawfn,template,result)
	Xunsigned char *rawfn;                /* the original file name */
	Xunsigned char *template;             /* the template data */
	Xunsigned char *result;               /* where to place the result */
	X{
	X    char *arc_ext = ".arc";          /* possible existing extension */
	X    char *i, *strrchr();             /* string indexing stuff */
	X
	X    i = strrchr(rawfn,'.');          /* strip 'arc' extension from filename */
	X    if (strcmp(i,arc_ext) == 0) *i = '\0';
	X
	X    strncpy(result,rawfn,10);        /* rebuild it using supplied template */
	X    strcat(result,template);
	X    return((char *)&result[0]);
	X}
	X
	X/*  convert a string to upper case  */
	Xupper(s)
	Xchar *s;
	X{
	X    while (*s = toupper(*s)) ++s;
	X}
	X
	Xsetmem(dest,size,c)
	Xchar *dest,c;
	XINT size;
	X{
	X    int i;
	X
	X    for (i = 0; i < size; dest[i] = c, i++);
	X}
	X
	Xabort(s,arg1,arg2,arg3)                /* something went wrong...QUIT!! */
	Xchar *s;
	X{
	X    char tempname1[STRLEN], tempname2[STRLEN];
	X
	X    sprintf(tempname1,"%s.crn",arctemp);
	X    sprintf(tempname2,"%s.cvt",arctemp);
	X
	X    unlink(bakname);                   /* remove all possible temp files */
	X    unlink(newname);
	X    unlink(tempname1);
	X    unlink(tempname2);
	X
	X    fprintf(stderr,"arc: ");           /* explain things to the user */
	X    fprintf(stderr,s,arg1,arg2,arg3);
	X    fprintf(stderr,"\n");
	X    exit(1);                           /* quit */
	X}
	X
	Xrename(o, n)
	Xchar *o, *n;
	X{
	X    return(link(o, n) || unlink(o));
	X}
	X
	Xmakenames(rawfn)
	Xchar *rawfn;
	X{
	X    char pathtemp[STRLEN];             /* temporary path holder */
	X    char nametemp[STRLEN];             /* temporary arcname holder */
	X    char *buf;                         /* temporary pointer */
	X    char *i, *strrchr();               /* string indexing junk */
	X    long getpid();                     /* process id function */
	X
	X    strcat(pathtemp,rawfn);
	X    if (i = strrchr(buf=rawfn,'/'))    /* if names are part of paths */
	X    {                                  /* lots to do */
	X        buf=i+1;
	X        pathtemp[strlen(rawfn)-strlen(buf)]='\0';
	X
	X        makefnam(buf,".arc",nametemp); /* make 'arcname' */
	X        sprintf(arcname,"%s%s",pathtemp,nametemp);
	X
	X        makefnam(buf,".bak",nametemp); /* make 'bakname' */
	X        sprintf(bakname,"%s%s",pathtemp,nametemp);
	X
	X        sprintf(arctemp,"%s.Arc%ld",pathtemp,getpid());
	X    }
	X    else                               /* not so much to do */
	X    {
	X        makefnam(rawfn,".arc",arcname);
	X        makefnam(rawfn,".bak",bakname);
	X
	X        sprintf(arctemp,".Arc%ld",getpid());
	X    }
	X    sprintf(newname,"%s.arc",arctemp);
	X}
	X
	Xonintr()                               /* SIGNAL was caught */
	X{
	X    abort("User Requested Abort");
	X}
	X
	X/*
	X * This function sorts the command line file arguments.  Needed since
	X * the add, update, etc., function does no sorting, and could result in
	X * multiple archive entries for the same file name.
	X */
	Xsortarg(num,arg)                       /* sort argument list, remove dups */
	Xint num;
	Xchar *arg[];
	X{
	X    char *temp;                        /* temporary pointer */
	X    INT top, seek;                     /* placeholders for sorting */
	X    INT dups = 0;                      /* how many duplicates are there */
	X    char *strrchr(), *i;               /* string indexing stuff */
	X    char *buf1, *buf2;                 /* pointers for strcmp to use */
	X
	X    /* sort the arguments, ignoring pathnames */
	X
	X    for (top = 0;top < num-1;top++)
	X        for (seek = top+1;seek<num;seek++)
	X        {
	X            buf1 = arg[top];
	X            buf2 = arg[seek];
	X            if (i = strrchr(arg[top],'/')) buf1 = i + 1;
	X            if (i = strrchr(arg[seek],'/')) buf2 = i + 1;
	X            if (strcmp(buf1,buf2) > 0)
	X            {
	X                temp = arg[top];
	X                arg[top] = arg[seek];
	X                arg[seek] = temp;
	X            }
	X        }
	X
	X    /* find any occurences of 'arcname', and remove them */
	X
	X    for (top = 0;top < num;top++)
	X        while (strcmp(arg[top],arcname) == 0)
	X        {
	X            for (seek = top; seek < num;seek++)
	X                arg[seek] = arg[seek + 1];
	X            arg[--num] = '\0';
	X            dups++;
	X        }
	X
	X    /* find any other duplicate arguments (ignoring pathnames), */
	X    /* and remove the second and subsequent occurences */
	X
	X    for (top = 0;top < num-1;top++)
	X    {
	X        buf1 = arg[top];
	X        buf2 = arg[top + 1];
	X        if (i = strrchr(arg[top],'/')) buf1 = i + 1;
	X        if (i = strrchr(arg[top + 1],'/')) buf2 = i + 1;
	X        while (strcmp(buf1,buf2) == 0)
	X        {
	X            for (seek = top + 1;seek < num;seek++)
	X                arg[seek] = arg[seek + 1];
	X            arg[--num] = '\0';
	X            buf2 = arg[top + 1];
	X            if (i = strrchr(arg[top + 1],'/')) buf2 = i + 1;
	X            dups++;
	X        }
	X    }
	X    return(dups);              /* tell main() how many we found */
	X}
SHAR_EOF
if test 5876 -ne "`wc -c < 'arcmisc.c'`"
then
	echo shar: "error transmitting 'arcmisc.c'" '(should have been 5876 characters)'
fi
fi
echo shar: "extracting 'arcpack.c'" '(9213 characters)'
if test -f 'arcpack.c'
then
	echo shar: "will not over-write existing file 'arcpack.c'"
else
sed 's/^	X//' << \SHAR_EOF > 'arcpack.c'
	X/*
	X *	arcpack.c	1.1
	X *
	X *	Author: Thom Henderson
	X *	Original System V port: Mike Stump
	X *	Enhancements, Bug fixes, and cleanup: Chris Seaman
	X *	Date: Fri Mar 20 09:57:02 1987
	X *	Last Mod.	3/21/87
	X *
	X */
	X
	X/*
	X * ARC - Archive utility - ARCPACK
	X * 
	X * Version 3.37, created on 02/03/86 at 22:58:01
	X * 
	X * (C) COPYRIGHT 1985 by System Enhancement Associates; ALL RIGHTS RESERVED
	X * 
	X *     Description:
	X *          This file contains the routines used to compress a file
	X *          when placing it in an archive.
	X */
	X
	X#include "arc.h"
	X
	X/* stuff for non-repeat packing */
	X
	X#define DLE 0x90                       /* repeat sequence marker */
	X
	Xstatic unsigned char state;            /* current packing state */
	X
	X/* non-repeat packing states */
	X
	X#define NOHIST  0                      /* don't consider previous input*/
	X#define SENTCHAR 1                     /* lastchar set, no lookahead yet */
	X#define SENDNEWC 2                     /* run over, send new char next */
	X#define SENDCNT 3                      /* newchar set, send count next */
	X
	X/* packing results */
	X
	Xstatic long stdlen;                    /* length for standard packing */
	Xstatic INT crcval;                     /* CRC check value */
	X
	XINT pack(f,t,hdr)                      /* pack file into an archive */
	XFILE *f, *t;                           /* source, destination */
	Xstruct heads *hdr;                     /* pointer to header data */
	X{
	X    INT c;                             /* one character of stream */
	X    long ncrlen;                       /* length after packing */
	X    long huflen;                       /* length after squeezing */
	X    long lzwlen;                       /* length after crunching */
	X    long pred_sq(), file_sq();         /* stuff for squeezing */
	X    long pred_cm();                    /* dynamic crunching cleanup */
	X    char tnam[STRLEN];                 /* temporary name buffer */
	X    char *makefnam();                  /* filename fixer upper */
	X    FILE *crn = NULL;                  /* temporary crunch file */
	X    INT getch();
	X    INT getc_ncr();
	X    INT putc_pak();
	X
	X    /* first pass - see which method is best */
	X
	X    if (!nocomp)                       /* if storage kludge not active */
	X    {
	X        if (note)
	X        {
	X            printf(" analyzing, ");
	X            fflush(stdout);
	X        }
	X
	X        sprintf(tnam,"%s.crn",arctemp);
	X        crn = fopen(tnam,"w+");
	X        state = NOHIST;                /* initialize ncr packing */
	X        stdlen =  ncrlen = 0;          /* reset size counters */
	X        crcval = 0;                    /* initialize CRC check value */
	X        setcode();                     /* initialize encryption */
	X
	X        init_cm(f,crn);                /* initialize for crunching */
	X        init_sq();                     /* initialize for squeeze scan */
	X
	X        while ((c=getc_ncr(f))!=EOF)   /* for each byte of file */
	X        {
	X            ncrlen++;                  /* one more packed byte */
	X            scan_sq(c);                /* see what squeezing can do */
	X            putc_cm(c,crn);            /* see what crunching can do */
	X        }
	X        huflen = pred_sq();            /* finish up after squeezing */
	X        lzwlen = pred_cm(crn);         /* finish up after crunching */
	X    }
	X    else                               /* else kludge the method */
	X    {
	X        stdlen = 0;                    /* make standard look best */
	X        ncrlen = huflen = lzwlen = 1;
	X    }
	X
	X    /* standard set-ups common to all methods */
	X
	X    fseek(f,0L,0);                     /* rewind input */
	X    hdr->crc = crcval;                 /* note CRC check value */
	X    hdr->length = stdlen;              /* set actual file length */
	X    state = NOHIST;                    /* reinitialize ncr packing */
	X    setcode();                         /* reinitialize encryption */
	X
	X    /* choose and use the shortest method */
	X
	X    if (stdlen<=ncrlen && stdlen<=huflen && stdlen<=lzwlen)
	X    {
	X        if (note)                      /* store w/out compression */
	X        {
	X            printf("storing, ");
	X            fflush(stdout);
	X        }
	X        hdrver = 2;                    /* note packing method */
	X        stdlen = crcval = 0;           /* recalc these for kludge */
	X        while ((c=getch(f))!=EOF)      /* store it straight */
	X            putc_pak(c,t);
	X        hdr->crc = crcval;
	X        hdr->length = hdr->size = stdlen;
	X    }
	X    else if (ncrlen<huflen && ncrlen<lzwlen)
	X    {
	X        if (note)                      /* pack w/repeat suppression */
	X        {
	X            printf("packing, ");
	X            fflush(stdout);
	X        }
	X        hdrver = 3;                    /* note packing method */
	X        hdr->size = ncrlen;            /* set data length */
	X        while ((c=getc_ncr(f))!=EOF)
	X            putc_pak(c,t);
	X    }
	X    else if (huflen<lzwlen)
	X    {
	X        if (note)
	X        {
	X            printf("squeezing, ");
	X            fflush(stdout);
	X        }
	X        hdrver = 4;                    /* note packing method */
	X        hdr->size = file_sq(f,t);      /* note final size */
	X    }
	X    else
	X    {
	X        if (note)
	X        {
	X            printf("crunching, ");
	X            fflush(stdout);
	X        }
	X        hdrver = 8;
	X        hdr->size = lzwlen;            /* size should not change */
	X        if (crn)                       /* if temp was created */
	X        {
	X            fseek(crn,0L,0);           /* then copy over crunched temp */
	X            while ((c=fgetc(crn))!=EOF)
	X                putc_tst(c,t);
	X        }
	X        else                           /* else re-crunch */
	X        {
	X            init_cm(f,t);
	X            while ((c=getc_ncr(f))!=EOF)
	X                putc_cm(c,t);
	X            pred_cm(t);                /* finish up after crunching */
	X        }
	X    }
	X
	X    /* standard cleanups common to all methods */
	X
	X    if (crn)                           /* get rid of crunch temporary */
	X    {
	X        fclose(crn);
	X        if (unlink(tnam) && warn)
	X        {
	X            printf("Cannot delete temporary file %s\n",tnam);
	X            nerrs++;
	X        }
	X    }
	X    if (note)
	X        printf("done.\n");
	X}
	X
	X/*
	X *  Non-repeat compression - text is passed through normally, except that
	X *  a run of more than two is encoded as:
	X *
	X *       <char> <DLE> <count>
	X *
	X *  Special case: a count of zero indicates that the DLE is really a DLE,
	X *  not a repeat marker.
	X */
	X
	XINT getc_ncr(f)                        /* get bytes with collapsed runs */
	XFILE *f;                               /* file to get from */
	X{
	X    static INT lastc;                  /* value returned on last call */
	X    static INT repcnt;                 /* repetition counter */
	X    static INT c;                      /* latest value seen */
	X
	X    switch(state)                      /* depends on our state */
	X    {
	X    case NOHIST:                       /* no relevant history */
	X        state = SENTCHAR;
	X        return(lastc = getch(f));      /* remember the value next time */
	X    case SENTCHAR:                     /* char was sent. look ahead */
	X        switch(lastc)                  /* action depends on char */
	X        {
	X        case DLE:                      /* if we sent a real DLE */
	X            state = NOHIST;            /* then start over again */
	X            return(0);                 /* but note that the DLE was real */
	X        case EOF:                      /* EOF is always a special case */
	X            return(EOF);
	X        default:                       /* else test for a repeat */
	X            for (repcnt=1; (c=getch(f))==lastc && repcnt<255; repcnt++)
	X                ;                      /* find end of run */
	X            switch(repcnt)             /* action depends on run size */
	X            {
	X            case 1:                    /* not a repeat */
	X                return(lastc = c);     /* but remember value next time */
	X            case 2:                    /* a repeat, but too short */
	X                state = SENDNEWC;      /* send the second one next time */
	X                return(lastc);
	X            default:                   /* a run - compress it */
	X                state = SENDCNT;       /* send repeat count next time */
	X                return(DLE);           /* send repeat marker this time */
	X            }
	X        }
	X    case SENDNEWC:                     /* send second char of short run */
	X        state = SENTCHAR;
	X        return(lastc = c);
	X    case SENDCNT:                      /* sent DLE, now send count */
	X        state = SENDNEWC;
	X        return(repcnt);
	X    default:
	X        abort("Bug - bad ncr state\n");
	X    }
	X}
	X
	Xstatic INT getch(f)                    /* special get char for packing */
	XFILE *f;                               /* file to get from */
	X{
	X    INT c;                             /* a char from the file */
	X
	X    if ((c=fgetc(f))!=EOF)             /* if not the end of file */
	X    {
	X        crcval = addcrc(crcval,c);     /* then update CRC check value */
	X        stdlen++;                      /* and bump length counter */
	X    }
	X
	X    return(c);
	X}
	X
	XINT putc_pak(c,f)                      /* put a packed byte into archive */
	Xchar c;                                /* byte to put */
	XFILE *f;                               /* archive to put it in */
	X{
	X    putc_tst(code(c),f);               /* put encoded byte, with checks */
	X}
SHAR_EOF
if test 9213 -ne "`wc -c < 'arcpack.c'`"
then
	echo shar: "error transmitting 'arcpack.c'" '(should have been 9213 characters)'
fi
fi
exit 0
#	End of shell archive