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}