arc.shar.03
Stu Heiss
stu at jpusa1.UUCP
Wed Oct 1 06:50:30 AEST 1986
#! /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}
More information about the Comp.sources.unix
mailing list