[net.sources] arc.shar.03

stu@jpusa1.UUCP (Stu Heiss) (09/30/86)

#! /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
#	arcpack.c
#	arcrun.c
#	arcs.h
# This archive created: Tue Sep 30 11:44:30 1986
# By:	stu (JPUSA - Chicago, IL)
export PATH; PATH=/bin:/usr/bin:$PATH
echo shar: "x - 'arclst.c'"
if test -f 'arclst.c'
then
	echo shar: "will not over-write existing file 'arclst.c'"
else
sed 's/^X//' << \SHAR_EOFarclst.c > 'arclst.c'
X/*  ARC - Archive utility - ARCLST
X
X(C) COPYRIGHT 1985 by System Enhancement Associates; ALL RIGHTS RESERVED
X
X    By:  Thom Henderson
X
X    Description:
X         This file contains the routines used to list the contents
X         of an archive.
X
X    Language:
X         Computer Innovations Optimizing C86
X*/
X#include <stdio.h>
X#include "arc.h"
X
Xlstarc(num,arg)                        /* list files in archive */
Xint num;                               /* number of arguments */
Xchar *arg[];                           /* 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
X    tnum = tlen = tsize = 0;           /* reset totals */
X
X    for(n=0; n<num; n++)               /* for each argument */
X         did[n] = 0;                   /* reset usage flag */
X    rempath(num,arg);                  /* strip off paths */
X
X    if(!kludge)
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
X    openarc(0);                        /* open archive for reading */
X
X    if(num)                            /* if files were named */
X    {    while(readhdr(&hdr,arc))      /* process all archive files */
X         {    list = 0;                /* reset list flag */
X              for(n=0; n<num; n++)     /* for each template given */
X              {    if(match(hdr.name,arg[n]))
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              {    if(!kludge)
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    {    if(!kludge)
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    if(!kludge)
X    {    printf("        ====  ========");
X         if(bose)
X              printf("            ====  ========");
X         printf("\n");
X    }
X
X    printf("Total %6ld  %8ld  ",tnum,tlen);
X    if(bose)
X         printf("          %3ld%%  %8ld  ",100L - (100L*tsize)/tlen,tsize);
X    printf("\n");
X
X    if(note)
X    {    for(n=0; n<num; n++)          /* report unused args */
X         {    if(!did[n])
X              {    printf("File not found: %s\n",arg[n]);
X                   nerrs++;
X              }
X         }
X    }
X}
X
Xstatic 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, ss;                    /* 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    ss = (hdr->time & 0x1f) * 2;
X
X    printf("%-12s  %8ld  ",hdr->name,hdr->length);
X
X    if(bose)
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         printf("  %3d%%",100L - (100L*hdr->size)/hdr->length);
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>12?hh-12:hh), mm, (hh>12?'p':'a'),
X              hdr->crc);
X
X    printf("\n");
X}
SHAR_EOFarclst.c
if test 5098 -ne "`wc -c < 'arclst.c'`"
then
	echo shar: "error transmitting 'arclst.c'" '(should have been 5098 characters)'
fi
fi
echo shar: "x - 'arclzw.c'"
if test -f 'arclzw.c'
then
	echo shar: "will not over-write existing file 'arclzw.c'"
else
sed 's/^X//' << \SHAR_EOFarclzw.c > 'arclzw.c'
X/*  ARC - Archive utility - ARCLZW
X
X(C) COPYRIGHT 1985 by System Enhancement Associates; ALL RIGHTS RESERVED
X
X    By:  Thom Henderson
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    Language:
X         Computer Innovations Optimizing C86
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#include <stdio.h>
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 maxmaxcode =  1 << BITS;    /* largest possible code (+1) */
X
Xstatic char buf[BITS];                 /* input/output buffer */
X
Xstatic unsigned char lmask[9] =        /* left side masks */
X{   0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00 };
Xstatic unsigned char rmask[9] =        /* right side masks */
X{   0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff};
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/* 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 = (long *)string_tab;        /* hash code table   (crunch) */
Xstatic unsigned int codetab[HSIZE];    /* string code table (crunch) */
X
Xstatic unsigned int *prefix = codetab; /* prefix code table (uncrunch) */
Xstatic unsigned char *suffix = (unsigned char *)string_tab;  /* suffix table (uncrunch) */
X
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#define FIRST   257                    /* first free entry */
X#define CLEAR   256                    /* table clear output code */
X
Xstatic cl_block(t)                     /* table clear for block compress */
XFILE *t;                               /* our output file */
X{
X    long int rat;
X
X    checkpoint = in_count + CHECK_GAP;
X
X    if(in_count > 0x007fffff)          /* shift will overflow */
X    {    rat = bytes_out >> 8;
X         if(rat == 0)                  /* Don't divide by zero */
X              rat = 0x7fffffff;
X         else rat = in_count / rat;
X    }
X    else rat = (in_count<<8)/bytes_out;/* 8 fractional bits */
X
X    if(rat > ratio)
X         ratio = rat;
X    else
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 *
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 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    char *bp = buf;                    /* buffer pointer */
X    int n;                             /* index */
X
X    if(code >= 0)                      /* if a real code */
X    {    /*
X          * Get to the first byte.
X          */
X         bp += (r_off >> 3);
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         *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         if(bits >= 8)
X         {    *bp++ = code;
X              code >>= 8;
X              bits -= 8;
X         }
X
X         /* Last bits. */
X         if(bits)
X              *bp = code;
X
X         offset += n_bits;
X
X         if(offset == (n_bits << 3))
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         if(free_ent>maxcode || clear_flg>0)
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              if(offset > 0)
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              {    maxcode = MAXCODE(n_bits = INIT_BITS);
X                   clear_flg = 0;
X              }
X              else                     /* else use more bits */
X              {    n_bits++;
X                   if(n_bits == BITS)
X                        maxcode = maxmaxcode;
X                   else
X                        maxcode = MAXCODE(n_bits);
X              }
X         }
X    }
X
X    else                               /* dump the buffer on EOF */
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 *
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 = (unsigned char *)buf;
X
X    if(clear_flg > 0 || offset >= size || free_ent > maxcode)
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         if(free_ent > maxcode)
X         {    n_bits++;
X              if(n_bits == BITS)
X                   maxcode = maxmaxcode;    /* won't get any bigger now */
X              else maxcode = MAXCODE(n_bits);
X         }
X         if(clear_flg > 0)
X         {    maxcode = MAXCODE(n_bits = INIT_BITS);
X              clear_flg = 0;
X         }
X
X         for(size=0; size<n_bits; size++)
X         {    if((code=getc_unp(f))==EOF)
X                   break;
X              else buf[size] = code;
X         }
X         if(size <= 0)
X              return -1;               /* end of file */
X
X         offset = 0;
X         /* Round size down to integral number of codes */
X         size = (size << 3)-(n_bits - 1);
X    }
X    r_off = offset;
X    bits = n_bits;
X
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    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    if(bits >= 8)
X    {    code |= *bp++ << r_off;
X         r_off += 8;
X         bits -= 8;
X    }
X    /* high order bits. */
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
Xinit_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
Xputc_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    {    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 bound */
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    {    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    {    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 < maxmaxcode)
X    {    codetab[i] = free_ent++;      /* code -> hashtable */
X         htab[i] = fcode;
X    }
X    else if((long int)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
Xdecomp(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    /*
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    {    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    {    if(code==CLEAR)
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         {    *stackp++ = finchar;
X              code = oldcode;
X         }
X
X         /*
X          * Generate output characters in reverse order
X          */
X         while(code >= 256)
X         {    *stackp++ = suffix[code];
X              code = prefix[code];
X         }
X         *stackp++ = finchar = suffix[code];
X
X         /*
X          * And put them out in forward order
X          */
X         do
X              putc_ncr(*--stackp,t);
X         while(stackp > stack);
X
X         /*
X          * Generate the new entry.
X          */
X         if((code=free_ent) < maxmaxcode)
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/*  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 (*h)();                /* pointer to hash function */
X
Xstatic unsigned 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 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/*  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 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/*  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 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
X    else                               /* else a collision has occured */
X    {    local = eolist(local);        /* move to last duplicate */
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         {    if(++tempnext==TABSIZE)  /* if we are at the end */
X              {    tempnext = 0;       /* wrap to beginning of table*/
X                   ep = string_tab;
X              }
X              else ++ep;               /* point to next element in table */
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/*  The unhash() function is used to search the hash table for a given key.
X    Like hash(), it performs a hash and linear probe search.  It returns
X    either the number of the entry (if found) or NOT_FND (if not found).
X*/
X
Xstatic unsigned unhash(pred,foll)      /* search string table for a key */
Xunsigned int pred;                     /* code of preceeding string */
Xunsigned char foll;                    /* character following string */
X{
X    unsigned int local, offset;        /* scratch storage */
X    struct entry *ep;                  /* this speeds up access */
X
X    local = (*h)(pred,foll);           /* initial hash */
X
X    while(1)
X    {    ep = &string_tab[local];      /* speed up table access */
X
X         if((ep->predecessor==pred) && (ep->follower==foll))
X              return local;            /* we have a match */
X
X         if(!ep->next)                 /* if no more duplicates */
X              return NOT_FND;          /* then key is not listed */
X
X         local = ep->next;             /* move on to next duplicate */
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 init_tab()                      /* set ground state in hash table */
X{
X    unsigned int i;                    /* table index */
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/*  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
Xupd_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/*  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 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    {    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
X    else                               /* buffer contains first nybble */
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 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
X    else return EMPTY;
X}
X
X/***** LEMPEL-ZEV DECOMPRESSION *****/
X
Xstatic int code_count;                 /* needed to detect table full */
Xstatic unsigned code;                  /* where we are so far */
Xstatic int firstc;                     /* true only on first character */
X
Xinit_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 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    unsigned int c;                    /* a character of input */
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    {    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    {    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         {    code = oldcode;
X              ep = &string_tab[code];  /* re-initialize pointer */
X              push(finchar);
X         }
X
X         while(ep->predecessor!=NO_PRED)
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         /*   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         {    upd_tab(oldcode,finchar);
X              --code_count;
X         }
X
X         oldcode = newcode;
X    }
X
X    return pop();                      /* return saved character */
X}
SHAR_EOFarclzw.c
if test 26619 -ne "`wc -c < 'arclzw.c'`"
then
	echo shar: "error transmitting 'arclzw.c'" '(should have been 26619 characters)'
fi
fi
echo shar: "x - 'arcmatch.c'"
if test -f 'arcmatch.c'
then
	echo shar: "will not over-write existing file 'arcmatch.c'"
else
sed 's/^X//' << \SHAR_EOFarcmatch.c > 'arcmatch.c'
X/*  ARC - Archive utility - ARCMATCH
X
X(C) COPYRIGHT 1985 by System Enhancement Associates; ALL RIGHTS RESERVED
X
X    By:  Thom Henderson
X
X    Description:
X         This file contains service routines needed to maintain an archive.
X
X    Language:
X         Computer Innovations Optimizing C86
X*/
X#include <stdio.h>
X#include "arc.h"
X
Xint match(n,t)                         /* test name against template */
Xchar *n;                               /* name to test */
Xchar *t;                               /* template to test against */
X{
X    upper(n); upper(t);                /* avoid case problems */
X
X    /* first match name part */
X
X    while((*n && *n!='.') || (*t && *t!='.'))
X    {    if(*n!=*t && *t!='?')         /* match fail? */
X         {    if(*t!='*')              /* wildcard fail? */
X                   return 0;           /* then no match */
X              else                     /* else jump over wildcard */
X              {    while(*n && *n!='.')
X                        n++;
X                   while(*t && *t!='.')
X                        t++;
X                   break;              /* name part matches wildcard */
X              }
X         }
X         else                          /* match good for this char */
X         {    n++;                     /* advance to next char */
X              t++;
X         }
X    }
X
X    if(*n && *n=='.') n++;             /* skip extension delimiters */
X    if(*t && *t=='.') t++;
X
X    /* now match name part */
X
X    while(*n || *t)
X    {    if(*n!=*t && *t!='?')         /* match fail? */
X         {    if(*t!='*')              /* wildcard fail? */
X                   return 0;           /* then no match */
X              else return 1;           /* else good enough */
X         }
X         else                          /* match good for this char */
X         {    n++;                     /* advance to next char */
X              t++;
X         }
X    }
X
X    return 1;                          /* match worked */
X}
X
Xrempath(nargs,arg)                     /* remove paths from filenames */
Xint nargs;                             /* number of names */
Xchar *arg[];                           /* pointers to names */
X{
X    char *i, *rindex();                /* string index, reverse indexer */
X    int n;                             /* index */
X
X    for(n=0; n<nargs; n++)             /* for each supplied name */
X    {    if(!(i=rindex(arg[n],'\\')))  /* search for end of path */
X              if(!(i=rindex(arg[n],'/')))
X                   i=rindex(arg[n],':');
X         if(i)                         /* if path was found */
X              arg[n] = i+1;            /* then skip it */
X    }
X}
SHAR_EOFarcmatch.c
if test 2639 -ne "`wc -c < 'arcmatch.c'`"
then
	echo shar: "error transmitting 'arcmatch.c'" '(should have been 2639 characters)'
fi
fi
echo shar: "x - 'arcpack.c'"
if test -f 'arcpack.c'
then
	echo shar: "will not over-write existing file 'arcpack.c'"
else
sed 's/^X//' << \SHAR_EOFarcpack.c > 'arcpack.c'
X/*  ARC - Archive utility - ARCPACK
X
X(C) COPYRIGHT 1985 by System Enhancement Associates; ALL RIGHTS RESERVED
X
X    By:  Thom Henderson
X
X    Description:
X         This file contains the routines used to compress a file
X         when placing it in an archive.
X
X    Language:
X         Computer Innovations Optimizing C86
X*/
X#include <stdio.h>
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
Xpack(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    FILE *fopen();
X
X    /* first pass - see which method is best */
X
X    if(!nocomp)                        /* if storage kludge not active */
X    {    if(note)
X              printf(" analyzing, ");
X
X         if(arctemp)                   /* use temp area if specified */
X              sprintf(tnam,"%s/$ARCTEMP.CRN",arctemp);
X         else makefnam("$ARCTEMP.CRN",arcname,tnam);
X         crn = fopen(tnam,"w+");
X
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         {    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    {    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    {    if(kludge)                    /*DEBUG*/
X              printf("(%ld) ",lzwlen-stdlen);
X         if(note)
X              printf("storing, ");     /* store without compression */
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
X    else if(ncrlen<huflen && ncrlen<lzwlen)
X    {    if(kludge)                    /*DEBUG*/
X              printf("(%ld) ",lzwlen-ncrlen);
X         if(note)
X              printf("packing, ");     /* pack with repeat suppression */
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
X    else if(huflen<lzwlen)
X    {    if(kludge)                    /*DEBUG*/
X              printf("(%ld) ",lzwlen-huflen);
X         if(note)
X              printf("squeezing, ");
X         hdrver = 4;                   /* note packing method */
X         hdr->size = file_sq(f,t);     /* note final size */
X    }
X
X    else
X    {    if(kludge)                    /*DEBUG*/
X              printf("(%ld) ",huflen-lzwlen);
X         if(note)
X              printf("crunching, ");
X         hdrver = 8;
X         hdr->size = lzwlen;           /* size should not change */
X         if(crn)                       /* if temp was created */
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         {    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    {    fclose(crn);
X         if(unlink(tnam) && warn)
X         {    printf("Cannot delete temporary file %s\n",tnam);
X              nerrs++;
X         }
X    }
X    if(note)
X         printf("done.\n");
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
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
X         case EOF:                     /* EOF is always a special case */
X              return EOF;
X
X         default:                      /* else test for a repeat */
X              for(repcnt=1; (c=getch(f))==lastc && repcnt<255; repcnt++)
X                   ;                   /* find end of run */
X
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
X              case 2:                  /* a repeat, but too short */
X                   state = SENDNEWC;   /* send the second one next time */
X                   return lastc;
X
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
X    case SENDNEWC:                     /* send second char of short run */
X         state = SENTCHAR;
X         return lastc = c;
X
X    case SENDCNT:                      /* sent DLE, now send count */
X         state = SENDNEWC;
X         return repcnt;
X
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    {    crcval = addcrc(crcval,c);    /* then update CRC check value */
X         stdlen++;                     /* and bump length counter */
X    }
X
X    return c;
X}
X
Xputc_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_EOFarcpack.c
if test 9215 -ne "`wc -c < 'arcpack.c'`"
then
	echo shar: "error transmitting 'arcpack.c'" '(should have been 9215 characters)'
fi
fi
echo shar: "x - 'arcrun.c'"
if test -f 'arcrun.c'
then
	echo shar: "will not over-write existing file 'arcrun.c'"
else
sed 's/^X//' << \SHAR_EOFarcrun.c > 'arcrun.c'
X/*  ARC - Archive utility - ARCRUN
X
X(C) COPYRIGHT 1985 by System Enhancement Associates; ALL RIGHTS RESERVED
X
X    By:  Thom Henderson
X
X    Description:
X         This file contains the routines used to "run" a file
X         which is stored in an archive.  At present, all we really do
X         is (a) extract a temporary file, (b) give its name as a system
X         command, and then (c) delete the file.
X
X    Language:
X         Computer Innovations Optimizing C86
X*/
X#include <stdio.h>
X#include "arc.h"
X
X#if unix
Xrunarc(num,arg)                        /* run file from archive */
Xint num;                               /* number of arguments */
Xchar *arg[];                           /* pointers to arguments */
X{
X	fprintf(stderr, "runarc(): not supported under unix\n");
X}
X#else
Xrunarc(num,arg)                        /* run file from archive */
Xint num;                               /* number of arguments */
Xchar *arg[];                           /* pointers to arguments */
X{
X    struct heads hdr;                  /* file header */
X    int run;                           /* true to run current file */
X    int did[MAXARG];                  /* true when argument was used */
X    int n;                             /* index */
X    char *makefnam();                  /* filename fixer */
X    char buf[STRLEN];                 /* filename buffer */
X    FILE *fopen();                     /* file opener */
X
X    for(n=0; n<num; n++)               /* for each argument */
X         did[n] = 0;                   /* reset usage flag */
X    rempath(num,arg);                  /* strip off paths */
X
X    openarc(0);                        /* open archive for reading */
X
X    if(num)                            /* if files were named */
X    {    while(readhdr(&hdr,arc))      /* while more files to check */
X         {    run = 0;                 /* reset run flag */
X              for(n=0; n<num; n++)     /* for each template given */
X              {    if(match(hdr.name,makefnam(arg[n],".*",buf)))
X                   {    run = 1;       /* turn on run flag */
X                        did[n] = 1;    /* turn on usage flag */
X                        break;         /* stop looking */
X                   }
X              }
X
X              if(run)                  /* if running this one */
X                   runfile(&hdr);      /* then do it */
X              else                     /* else just skip it */
X                   fseek(arc,hdr.size,1);
X         }
X    }
X
X    else while(readhdr(&hdr,arc))      /* else run all files */
X         runfile(&hdr);
X
X    closearc(0);                       /* close archive after changes */
X
X    if(note)
X    {    for(n=0; n<num; n++)          /* report unused args */
X         {    if(!did[n])
X              {    printf("File not found: %s\n",arg[n]);
X                   nerrs++;
X              }
X         }
X    }
X}
X
Xstatic runfile(hdr)                    /* run a file */
Xstruct heads *hdr;                     /* pointer to header data */
X{
X    FILE *tmp, *fopen();               /* temporary file */
X    char buf[STRLEN], *makefnam();    /* temp file name, fixer */
X    char sys[STRLEN];                 /* invocation command buffer */
X    char *dir, *gcdir();               /* directory stuff */
X
X    makefnam("$ARCTEMP",hdr->name,buf);
X
X    if(!strcmp(buf,"$ARCTEMP.BAS"))
X         strcpy(sys,"BASICA $ARCTEMP");
X
X    else if(!strcmp(buf,"$ARCTEMP.BAT")
X         || !strcmp(buf,"$ARCTEMP.COM")
X         || !strcmp(buf,"$ARCTEMP.EXE"))
X         strcpy(sys,"$ARCTEMP");
X
X    else
X    {    if(warn)
X         {    printf("File %s is not a .BAS, .BAT, .COM, or .EXE\n",
X                   hdr->name);
X              nerrs++;
X         }
X         fseek(arc,hdr->size,1);  /* skip this file */
X         return;
X    }
X
X    if(warn)
X         if(tmp=fopen(buf,"r"))
X              abort("Temporary file %s already exists",buf);
X    if(!(tmp=fopen(makefnam("$ARCTEMP",hdr->name,buf),"w+")))
X         abort("Unable to create temporary file %s",buf);
X
X    if(note)
X         printf("Invoking file: %s\n",hdr->name);
X
X    dir = gcdir("");                   /* see where we are */
X    unpack(arc,tmp,hdr);               /* unpack the entry */
X    fclose(tmp);                       /* release the file */
X    system(sys);                       /* try to invoke it */
X    chdir(dir); free(dir);             /* return to whence we started */
X    if(unlink(buf) && warn)
X    {    printf("Cannot unsave temporary file %s\n",buf);
X         nerrs++;
X    }
X}
X#endif
SHAR_EOFarcrun.c
if test 4479 -ne "`wc -c < 'arcrun.c'`"
then
	echo shar: "error transmitting 'arcrun.c'" '(should have been 4479 characters)'
fi
fi
echo shar: "x - 'arcs.h'"
if test -f 'arcs.h'
then
	echo shar: "will not over-write existing file 'arcs.h'"
else
sed 's/^X//' << \SHAR_EOFarcs.h > 'arcs.h'
X/*  ARC - Archive utility - Archive file header format
X
X    Version 2.12, created on 12/17/85 at 14:40:26
X
X(C) COPYRIGHT 1985 by System Enhancement Associates; ALL RIGHTS RESERVED
X
X    By:  Thom Henderson
X
X    Description:
X         This file defines the format of an archive file header, excluding
X         the archive marker and the header version number.
X
X         Each entry in an archive begins with a one byte archive marker,
X         which is set to 26.  The marker is followed by a one byte
X         header type code, from zero to 7.
X
X         If the header type code is zero, then it is an end marker, and
X         no more data should be read from the archive.
X
X         If the header type code is in the range 2 to 7, then it is
X         followed by a standard archive header, which is defined below.
X
X         If the header type code is one, then it is followed by an older
X         format archive header.  The older format header does not contain
X         the true length.  A header should be read for a length of
X         sizeof(struct heads)-sizeof(long).  Then set length equal to size
X         and change the header version to 2.
X
X    Programming note:
X         The crc value given in the header is based on the unpacked data.
X
X    Language:
X         Computer Innovations Optimizing C86
X*/
X
Xstruct heads                           /* archive entry header format */
X{   char name[13];                 /* file name */
X    long size;                         /* size of file, in bytes */
X    unsigned int date;                 /* creation date */
X    unsigned int time;                 /* creation time */
X    int crc;                           /* cyclic redundancy check */
X    long length;                       /* true file length */
X}   ;
SHAR_EOFarcs.h
if test 1753 -ne "`wc -c < 'arcs.h'`"
then
	echo shar: "error transmitting 'arcs.h'" '(should have been 1753 characters)'
fi
fi
exit 0
#	End of shell archive
-- 
Stu Heiss {ihnp4!jpusa1!stu}