Path: utzoo!utgpu!watmath!uunet!xanth!galaxia!lazlo!gizzmo!fthood!egray From: egray@fthood.UUCP Newsgroups: unix-pc.sources Subject: Arc v5.21 (5 of 6) Message-ID: <6800099@fthood> Date: 2 Jan 89 23:54:00 GMT Lines: 1711 Nf-ID: #N:fthood:6800099:000:45264 Nf-From: fthood.UUCP!egray Jan 2 17:54:00 1989 This is part 5 (of 6) to the Arc v5.21 distribution package. Emmet P. Gray US Army, HQ III Corps & Fort Hood ...!uunet!uiucuxc!fthood!egray Attn: AFZF-DE-ENV Directorate of Engineering & Housing Environmental Management Office Fort Hood, TX 76544-5057 ------------------------------------------------------------------------------ #! /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: # arcsq.c # arcsqs.c # arcsvc.c # arctst.c # arcunp.c # arcusq.c # getwd.c # This archive created: Sun Jan 1 12:48:31 1989 export PATH; PATH=/bin:/usr/bin:$PATH echo shar: "extracting 'arcsq.c'" '(14548 characters)' if test -f 'arcsq.c' then echo shar: "will not over-write existing file 'arcsq.c'" else sed 's/^X//' << \SHAR_EOF > 'arcsq.c' X/* X * $Header: arcsq.c,v 1.3 88/07/31 18:53:32 hyc Exp $ X */ X X/* X * ARC - Archive utility - ARCSQ X * X * Version 3.10, created on 01/30/86 at 20:10:46 X * X * (C) COPYRIGHT 1985 by System Enhancement Associates; ALL RIGHTS RESERVED X * X * By: Thom Henderson X * X * Description: This file contains the routines used to squeeze a file when X * placing it in an archive. X * X * Language: Computer Innovations Optimizing C86 X * X * Programming notes: Most of the routines used for the Huffman squeezing X * algorithm were lifted from the SQ program by Dick Greenlaw, as adapted to X * CI-C86 by Robert J. Beilstein. X */ X#include X#include "arc.h" X X/* stuff for Huffman squeezing */ X X#define TRUE 1 X#define FALSE 0 X#define ERROR (-1) X#define SPEOF 256 /* special endfile token */ X#define NOCHILD (-1) /* marks end of path through tree */ X#define NUMVALS 257 /* 256 data values plus SPEOF */ X#define NUMNODES (NUMVALS+NUMVALS-1) /* number of nodes */ X#define MAXCOUNT (unsigned short) 65535 /* biggest unsigned integer */ X X/* X * The following array of structures are the nodes of the binary trees. The X * first NUMVALS nodes become the leaves of the final tree and represent the X * values of the data bytes being encoded and the special endfile, SPEOF. The X * remaining nodes become the internal nodes of the final tree. X */ X Xstruct nd { /* shared by unsqueezer */ X unsigned short weight; /* number of appearances */ X short tdepth; /* length on longest path in tree */ X short lchild, rchild; /* indices to next level */ X} node[NUMNODES]; /* use large buffer */ X Xstatic int dctreehd; /* index to head of final tree */ X X/* X * This is the encoding table: The bit strings have first bit in low bit. X * Note that counts were scaled so code fits unsigned integer. X */ X Xstatic int codelen[NUMVALS]; /* number of bits in code */ Xstatic unsigned short code[NUMVALS]; /* code itself, right adjusted */ Xstatic unsigned short tcode; /* temporary code value */ Xstatic long valcount[NUMVALS]; /* actual count of times seen */ X X/* Variables used by encoding process */ X Xstatic int curin; /* value currently being encoded */ Xstatic int cbitsrem; /* # of code string bits left */ Xstatic unsigned short ccode; /* current code right justified */ X Xstatic void scale(), heap(), adjust(), bld_tree(), init_enc(), put_int(); Xstatic int cmptrees(), buildenc(), maxchar(); Xvoid Xinit_sq() X{ /* prepare for scanning pass */ X int i; /* node index */ X X /* X * Initialize all nodes to single element binary trees with zero X * weight and depth. X */ X X for (i = 0; i < NUMNODES; ++i) { X node[i].weight = 0; X node[i].tdepth = 0; X node[i].lchild = NOCHILD; X node[i].rchild = NOCHILD; X } X X for (i = 0; i < NUMVALS; i++) X valcount[i] = 0; X} X Xvoid Xscan_sq(c) /* add a byte to the tables */ X int c; /* byte to add */ X{ X unsigned short *wp; /* speeds up weight counting */ X X /* Build frequency info in tree */ X X if (c == EOF) /* it's traditional */ X c = SPEOF; /* dumb, but traditional */ X X if (*(wp = &node[c].weight) != MAXCOUNT) X ++(*wp); /* bump weight counter */ X X valcount[c]++; /* bump byte counter */ X} X Xlong Xpred_sq() X{ /* predict size of squeezed file */ X int i; X int btlist[NUMVALS]; /* list of intermediate X * b-trees */ X int listlen;/* length of btlist */ X unsigned short ceiling;/* limit for scaling */ X long size = 0; /* predicted size */ X int numnodes; /* # of nodes in simplified tree */ X X scan_sq(EOF); /* signal end of input */ X X ceiling = MAXCOUNT; X X /* Keep trying to scale and encode */ X X do { X scale(ceiling); X ceiling /= 2; /* in case we rescale */ X X /* X * Build list of single node binary trees having leaves for X * the input values with non-zero counts X */ X X for (i = listlen = 0; i < NUMVALS; ++i) { X if (node[i].weight != 0) { X node[i].tdepth = 0; X btlist[listlen++] = i; X } X } X X /* X * Arrange list of trees into a heap with the entry indexing X * the node with the least weight at the top. X */ X X heap(btlist, listlen); X X /* Convert the list of trees to a single decoding tree */ X X bld_tree(btlist, listlen); X X /* Initialize the encoding table */ X X init_enc(); X X /* X * Try to build encoding table. Fail if any code is > 16 bits X * long. X */ X } while (buildenc(0, dctreehd) == ERROR); X X /* Initialize encoding variables */ X X cbitsrem = 0; /* force initial read */ X curin = 0; /* anything but endfile */ X X for (i = 0; i < NUMVALS; i++) /* add bits for each code */ X size += valcount[i] * codelen[i]; X X size = (size + 7) / 8; /* reduce to number of bytes */ X X numnodes = dctreehd < NUMVALS ? 0 : dctreehd - (NUMVALS - 1); X X size += sizeof(short) + 2 * numnodes * sizeof(short); X X return size; X} X X/* X * The count of number of occurrances of each input value have already been X * prevented from exceeding MAXCOUNT. Now we must scale them so that their X * sum doesn't exceed ceiling and yet no non-zero count can become zero. This X * scaling prevents errors in the weights of the interior nodes of the X * Huffman tree and also ensures that the codes will fit in an unsigned X * integer. Rescaling is used if necessary to limit the code length. X */ X Xstatic void Xscale(ceil) X unsigned short ceil; /* upper limit on total weight */ X{ X register int i; X int ovflw, divisor; X unsigned short w, sum; X unsigned char increased; /* flag */ X X do { X for (i = sum = ovflw = 0; i < NUMVALS; ++i) { X if (node[i].weight > (ceil - sum)) X ++ovflw; X sum += node[i].weight; X } X X divisor = ovflw + 1; X X /* Ensure no non-zero values are lost */ X X increased = FALSE; X for (i = 0; i < NUMVALS; ++i) { X w = node[i].weight; X if (w < divisor && w != 0) { /* Don't fail to provide X * a code if it's used X * at all */ X X node[i].weight = divisor; X increased = TRUE; X } X } X } while (increased); X X /* Scaling factor chosen, now scale */ X X if (divisor > 1) X for (i = 0; i < NUMVALS; ++i) X node[i].weight /= divisor; X} X X/* X * heap() and adjust() maintain a list of binary trees as a heap with the top X * indexing the binary tree on the list which has the least weight or, in X * case of equal weights, least depth in its longest path. The depth part is X * not strictly necessary, but tends to avoid long codes which might provoke X * rescaling. X */ X Xstatic void Xheap(list, length) X int list[], length; X{ X register int i; X X for (i = (length - 2) / 2; i >= 0; --i) X adjust(list, i, length - 1); X} X X/* Make a heap from a heap with a new top */ X Xstatic void Xadjust(list, top, bottom) X int list[], top, bottom; X{ X register int k, temp; X X k = 2 * top + 1; /* left child of top */ X temp = list[top]; /* remember root node of top tree */ X X if (k <= bottom) { X if (k < bottom && cmptrees(list[k], list[k + 1])) X ++k; X X /* k indexes "smaller" child (in heap of trees) of top */ X /* now make top index "smaller" of old top and smallest child */ X X if (cmptrees(temp, list[k])) { X list[top] = list[k]; X list[k] = temp; X X /* Make the changed list a heap */ X X adjust(list, k, bottom); /* recursive */ X } X } X} X X/* X * Compare two trees, if a > b return true, else return false. Note X * comparison rules in previous comments. X */ X Xstatic int Xcmptrees(a, b) X int a, b; /* root nodes of trees */ X{ X if (node[a].weight > node[b].weight) X return TRUE; X if (node[a].weight == node[b].weight) X if (node[a].tdepth > node[b].tdepth) X return TRUE; X return FALSE; X} X X/* X * HUFFMAN ALGORITHM: develops the single element trees into a single binary X * tree by forming subtrees rooted in interior nodes having weights equal to X * the sum of weights of all their descendents and having depth counts X * indicating the depth of their longest paths. X * X * When all trees have been formed into a single tree satisfying the heap X * property (on weight, with depth as a tie breaker) then the binary code X * assigned to a leaf (value to be encoded) is then the series of left (0) X * and right (1) paths leading from the root to the leaf. Note that trees are X * removed from the heaped list by moving the last element over the top X * element and reheaping the shorter list. X */ X Xstatic void Xbld_tree(list, len) X int list[]; Xint len; X{ X register int freenode; /* next free node in tree */ X register struct nd *frnp; /* free node pointer */ X int lch, rch; /* temps for left, right children */ X X /* X * Initialize index to next available (non-leaf) node. Lower numbered X * nodes correspond to leaves (data values). X */ X X freenode = NUMVALS; X X while (len > 1) { /* Take from list two btrees with least X * weight and build an interior node pointing X * to them. This forms a new tree. */ X X lch = list[0]; /* This one will be left child */ X X /* delete top (least) tree from the list of trees */ X X list[0] = list[--len]; X adjust(list, 0, len - 1); X X /* Take new top (least) tree. Reuse list slot later */ X X rch = list[0]; /* This one will be right child */ X X /* X * Form new tree from the two least trees using a free node X * as root. Put the new tree in the list. X */ X X frnp = &node[freenode]; /* address of next free node */ X list[0] = freenode++; /* put at top for now */ X frnp->lchild = lch; X frnp->rchild = rch; X frnp->weight = node[lch].weight + node[rch].weight; X frnp->tdepth = 1 + maxchar(node[lch].tdepth, node[rch].tdepth); X X /* reheap list to get least tree at top */ X X adjust(list, 0, len - 1); X } X dctreehd = list[0]; /* head of final tree */ X} X Xstatic int Xmaxchar(a, b) X{ X return a > b ? a : b; X} X Xstatic void Xinit_enc() X{ X register int i; X X /* Initialize encoding table */ X X for (i = 0; i < NUMVALS; ++i) X codelen[i] = 0; X} X X/* X * Recursive routine to walk the indicated subtree and level and maintain the X * current path code in bstree. When a leaf is found the entire code string X * and length are put into the encoding table entry for the leaf's data value X * . X * X * Returns ERROR if codes are too long. X */ X Xstatic int Xbuildenc(level, root) X int level; /* level of tree being examined, from zero */ X int root; /* root of subtree is also data value if leaf */ X{ X register int l, r; X X l = node[root].lchild; X r = node[root].rchild; X X if (l == NOCHILD && r == NOCHILD) { /* Leaf. Previous path X * determines bit string code X * of length level (bits 0 to X * level - 1). Ensures unused X * code bits are zero. */ X X codelen[root] = level; X code[root] = tcode & (((unsigned short ) ~0) >> (16 - level)); X return (level > 16) ? ERROR : 0; X } else { X if (l != NOCHILD) { /* Clear path bit and continue deeper */ X X tcode &= ~(1 << level); X if (buildenc(level + 1, l) == ERROR) X return ERROR; /* pass back bad statuses */ X } X if (r != NOCHILD) { /* Set path bit and continue deeper */ X X tcode |= 1 << level; X if (buildenc(level + 1, r) == ERROR) X return ERROR; /* pass back bad statuses */ X } X } X return NULL; /* it worked if we reach here */ X} X Xstatic void Xput_int(n, f) /* output an integer */ X short n; /* integer to output */ X FILE *f; /* file to put it to */ X{ X void putc_pak(); X X putc_pak(n & 0xff, f); /* first the low byte */ X putc_pak(n >> 8, f); /* then the high byte */ X} X X/* Write out the header of the compressed file */ X Xstatic long Xwrt_head(ob) X FILE *ob; X{ X register int l, r; X int i, k; X int numnodes; /* # of nodes in simplified tree */ X X /* X * Write out a simplified decoding tree. Only the interior nodes are X * written. When a child is a leaf index (representing a data value) X * it is recoded as -(index + 1) to distinguish it from interior X * indexes which are recoded as positive indexes in the new tree. X * X * Note that this tree will be empty for an empty file. X */ X X numnodes = dctreehd < NUMVALS ? 0 : dctreehd - (NUMVALS - 1); X put_int(numnodes, ob); X X for (k = 0, i = dctreehd; k < numnodes; ++k, --i) { X l = node[i].lchild; X r = node[i].rchild; X l = l < NUMVALS ? -(l + 1) : dctreehd - l; X r = r < NUMVALS ? -(r + 1) : dctreehd - r; X put_int(l, ob); X put_int(r, ob); X } X X return sizeof(short) + numnodes * 2 * sizeof(short); X} X X/* X * Get an encoded byte or EOF. Reads from specified stream AS NEEDED. X * X * There are two unsynchronized bit-byte relationships here. The input stream X * bytes are converted to bit strings of various lengths via the static X * variables named c... These bit strings are concatenated without padding to X * become the stream of encoded result bytes, which this function returns one X * at a time. The EOF (end of file) is converted to SPEOF for convenience and X * encoded like any other input value. True EOF is returned after that. X */ X Xstatic int Xgethuff(ib) /* Returns bytes except for EOF */ X FILE *ib; X{ X int rbyte; /* Result byte value */ X int need; /* number of bits */ X int getc_ncr(); X X rbyte = 0; X need = 8; /* build one byte per call */ X X /* X * Loop to build a byte of encoded data. Initialization forces read X * the first time. X */ X Xloop: X if (cbitsrem >= need) { /* if current code is big enough */ X if (need == 0) X return rbyte; X X rbyte |= ccode << (8 - need); /* take what we need */ X ccode >>= need; /* and leave the rest */ X cbitsrem -= need; X return rbyte & 0xff; X } X /* We need more than current code */ X X if (cbitsrem > 0) { X rbyte |= ccode << (8 - need); /* take what there is */ X need -= cbitsrem; X } X /* No more bits in current code string */ X X if (curin == SPEOF) { /* The end of file token has been encoded. If X * result byte has data return it and do EOF X * next time. */ X X cbitsrem = 0; X return (need == 8) ? EOF : rbyte + 0; X } X /* Get an input byte */ X X if ((curin = getc_ncr(ib)) == EOF) X curin = SPEOF; /* convenient for encoding */ X X ccode = code[curin]; /* get the new byte's code */ X cbitsrem = codelen[curin]; X X goto loop; X} X X/* X * This routine is used to perform the actual squeeze operation. It can only X * be called after the file has been scanned. It returns the true length of X * the squeezed entry. X */ X Xlong Xfile_sq(f, t) /* squeeze a file into an archive */ X FILE *f; /* file to squeeze */ X FILE *t; /* archive to receive file */ X{ X int c; /* one byte of squeezed data */ X long size; /* size after squeezing */ X X size = wrt_head(t); /* write out the decode tree */ X X while ((c = gethuff(f)) != EOF) { X putc_pak(c, t); X size++; X } X X return size; /* report true size */ X} SHAR_EOF if test 14548 -ne "`wc -c < 'arcsq.c'`" then echo shar: "error transmitting 'arcsq.c'" '(should have been 14548 characters)' fi fi echo shar: "extracting 'arcsqs.c'" '(11513 characters)' if test -f 'arcsqs.c' then echo shar: "will not over-write existing file 'arcsqs.c'" else sed 's/^X//' << \SHAR_EOF > 'arcsqs.c' X/* X * $Header: arcsqs.c,v 1.3 88/07/31 18:54:14 hyc Exp $ X */ X X/* ARC - Archive utility - SQUASH X X(C) COPYRIGHT 1985 by System Enhancement Associates; ALL RIGHTS RESERVED X X This is a quick hack to ARCLZW to make it handle squashed archives. X Dan Lanciani (ddl@harvard.*) July 87 X X*/ X X/* X * $Header: arcsqs.c,v 1.3 88/07/31 18:54:14 hyc Exp $ X */ X X#include X#include "arc.h" X X#if MSDOS Xchar *setmem(); X#else Xchar *memset(); X#endif Xint getc_unp(); Xvoid putc_pak(), putc_unp(); Xstatic void putcode(); X X/* definitions for the new dynamic Lempel-Zev crunching */ X X#define BITS 13 /* maximum bits per code */ X#define HSIZE 10007 /* 80% occupancy */ X#define INIT_BITS 9 /* initial number of bits/code */ Xstatic int n_bits; /* number of bits/code */ Xstatic int maxcode; /* maximum code, given n_bits */ X#define MAXCODE(n) ((1<<(n)) - 1) /* maximum code calculation */ Xstatic int maxcodemax = 1 << BITS; /* largest possible code (+1) */ X Xstatic unsigned char buf[BITS]; /* input/output buffer */ X Xstatic unsigned char lmask[9] = /* left side masks */ X{0xff, 0xfe, 0xfc, 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 short ent; X Xlong htab[HSIZE]; /* hash code table (crunch) */ Xunsigned short codetab[HSIZE]; /* string code table (crunch) */ X Xstatic unsigned short *prefix = codetab; /* prefix code table (uncrunch) */ Xstatic unsigned char *suffix=(unsigned char *)htab; /* suffix table (uncrunch) */ Xstatic int free_ent; /* first unused entry */ Xstatic int firstcmp; /* true at start of compression */ Xunsigned 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 void Xcl_block(t) /* table clear for block compress */ X FILE *t; /* our output file */ X{ X long rat; X X checkpoint = in_count + CHECK_GAP; X X if (in_count > 0x007fffffL) { /* shift will overflow */ X rat = bytes_out >> 8; X if (rat == 0) /* Don't divide by zero */ X rat = 0x7fffffffL; X else X rat = in_count / rat; X } else X 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 void Xputcode(code, t) /* output a code */ X int code; /* code to output */ X FILE *t; /* where to put it */ X{ X int r_off = offset; /* right offset */ X int bits = n_bits; /* bits to go */ X unsigned char *bp = buf; /* buffer pointer */ X int n; /* index */ X register int ztmp; X X if (code >= 0) { /* if a real code *//* Get to the first byte. */ X bp += (r_off >> 3); X r_off &= 7; X X /* X * Since code is always >= 8 bits, only need to mask the X * first hunk on the left. X */ X ztmp = (code << r_off) & lmask[r_off]; X *bp = (*bp & rmask[r_off]) | ztmp; 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 /* 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 * If the next entry is going to be too big for the code X * size, then increase it, if possible. X */ X if (free_ent > maxcode || clear_flg > 0) { /* Write the whole X * buffer, because the X * input side won't X * discover the size X * increase until after X * it has read it. */ 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 } else {/* else use more bits */ X n_bits++; X if (n_bits == BITS) X maxcode = maxcodemax; X else X maxcode = MAXCODE(n_bits); 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 Xgetcode(f) /* get a code */ X FILE *f; /* file to get from */ X{ X int code; X static int loffset = 0, size = 0; X int r_off, bits; X unsigned char *bp = buf; X X if (clear_flg > 0 || loffset >= size || free_ent > maxcode) { 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 if (free_ent > maxcode) { X n_bits++; X if (n_bits == BITS) X maxcode = maxcodemax; /* won't get any bigger X * now */ X else X maxcode = MAXCODE(n_bits); X } X if (clear_flg > 0) { X maxcode = MAXCODE(n_bits = INIT_BITS); X clear_flg = 0; X } X for (size = 0; size < n_bits; size++) { X if ((code = getc_unp(f)) == EOF) X break; X else X buf[size] = code; X } X if (size <= 0) X return -1; /* end of file */ X X loffset = 0; X /* Round size down to integral number of codes */ X size = (size << 3) - (n_bits - 1); X } X r_off = loffset; 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 loffset += 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 Xvoid Xsqinit_cm() /* initialize for compression */ X{ X offset = 0; X bytes_out = 0; 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 firstcmp = 1; /* next byte will be first */ X} X Xvoid Xsqputc_cm(c, t) /* compress a character */ X unsigned char c; /* character to compress */ X FILE *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 bund */ X X firstcmp = 0; /* no longer first */ X return; 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 } 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 < maxcodemax) { X codetab[i] = free_ent++; /* code -> hashtable */ X htab[i] = fcode; X } else if ((long) in_count >= checkpoint) X cl_block(t); X} X Xlong Xsqpred_cm(t) /* finish compressing a file */ X FILE *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 Xvoid Xsqdecomp(f, t) /* decompress a file */ X FILE *f; /* file to read codes from */ X FILE *t; /* file to write text to */ X{ X unsigned char *stackp; X int finchar; X int code, oldcode, incode; 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_unp((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 if (code > free_ent) { X if (warn) { X printf("Corrupted compressed file.\n"); X printf("Invalid code %d when max is %d.\n", X code, free_ent); X } X nerrs++; X return; X } X *stackp++ = finchar; X code = oldcode; 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_unp(*--stackp, t); X while (stackp > stack); X X /* X * Generate the new entry. X */ X if ((code = free_ent) < maxcodemax) { 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} SHAR_EOF if test 11513 -ne "`wc -c < 'arcsqs.c'`" then echo shar: "error transmitting 'arcsqs.c'" '(should have been 11513 characters)' fi fi echo shar: "extracting 'arcsvc.c'" '(4689 characters)' if test -f 'arcsvc.c' then echo shar: "will not over-write existing file 'arcsvc.c'" else sed 's/^X//' << \SHAR_EOF > 'arcsvc.c' X/* X * $Header: arcsvc.c,v 1.9 88/07/31 18:54:55 hyc Exp $ X */ X X/* ARC - Archive utility - ARCSVC X X Version 2.23, created on 04/22/87 at 13:10:10 X X(C) COPYRIGHT 1985-87 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 X#include "arc.h" X#if MTS X#include X#endif X Xvoid abort(), setstamp(); Xint unlink(); X Xvoid Xopenarc(chg) /* open archive */ X int chg; /* true to open for changes */ X{ X FILE *fopen();/* file opener */ X X if (!(arc = fopen(arcname, OPEN_R))) { X if (chg) { X if (note) X printf("Creating new archive: %s\n", arcname); X } X else X abort("Archive not found: %s", arcname); X } X#if MTS X /* allow reading archives of max MTS record length */ X else { X char *buffer, *malloc(); X int inlen; X struct GDDSECT *region; X X region=gdinfo(arc->_fd._fdub); X inlen=region->GDINLEN; X buffer=malloc(inlen); X setbuf(arc, buffer); X arc->_bufsiz=inlen; X } X#endif X if (chg) { /* if opening for changes */ X if (!(new = fopen(newname, OPEN_W))) X abort("Cannot create archive copy: %s", newname); X X changing = chg; /* note if open for changes */ X } X} X Xvoid Xclosearc(chg) /* close an archive */ X int chg; /* true if archive was changed */ X{ X if (arc) { /* if we had an initial archive */ X fclose(arc); X#if !MTS X if (kludge) /* kludge to update timestamp */ X setstamp(arcname, olddate, oldtime); X#endif X } X if (chg) { /* if things have changed */ X fclose(new); /* close the new copy */ X if (arc) { /* if we had an original archive */ X if (keepbak) { /* if a backup is wanted */ X unlink(bakname); /* erase any old copies */ X if (move(arcname, bakname)) X abort("Cannot rename %s to %s", arcname, bakname); X printf("Keeping backup archive: %s\n", bakname); X } else if (unlink(arcname)) X abort("Cannot delete old archive: %s", arcname); X } X if (move(newname, arcname)) X abort("Cannot move %s to %s", newname, arcname); X#if !MTS X setstamp(arcname, arcdate, arctime); X#endif X } X} X X/* X * CRC computation logic X * X * The logic for this method of calculating the CRC 16 bit polynomial is taken X * from an article by David Schwaderer in the April 1985 issue of PC Tech X * Journal. X */ X Xstatic short crctab[] = /* CRC lookup table */ X{ X 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, X 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, X 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, X 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841, X 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40, X 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41, X 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641, X 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040, X 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240, X 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, X 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41, X 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, X 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41, X 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40, X 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640, X 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041, X 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240, X 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441, X 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41, X 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, X 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41, X 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, X 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640, X 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041, X 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241, X 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440, X 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40, X 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, X 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40, X 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41, X 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, X 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040 X}; X Xint Xaddcrc(crc, c) /* update a CRC check */ X int crc; /* running CRC value */ X unsigned char c; /* character to add */ X{ X return ((crc >> 8) & 0x00ff) ^ crctab[(crc ^ c) & 0x00ff]; X} SHAR_EOF if test 4689 -ne "`wc -c < 'arcsvc.c'`" then echo shar: "error transmitting 'arcsvc.c'" '(should have been 4689 characters)' fi fi echo shar: "extracting 'arctst.c'" '(1284 characters)' if test -f 'arctst.c' then echo shar: "will not over-write existing file 'arctst.c'" else sed 's/^X//' << \SHAR_EOF > 'arctst.c' X/* X * $Header: arctst.c,v 1.4 88/04/19 01:40:28 hyc Exp $ X */ X X/* X * ARC - Archive utility - ARCTST X * X * Version 2.12, created on 02/03/86 at 23:00:40 X * X * (C) COPYRIGHT 1985 by System Enhancement Associates; ALL RIGHTS RESERVED X * X * By: Thom Henderson X * X * Description: This file contains the routines used to test archive integrity. X * X * Language: Computer Innovations Optimizing C86 X */ X#include X#include "arc.h" X Xvoid openarc(); Xint readhdr(), unpack(); X Xvoid Xtstarc() X{ /* test integrity of an archive */ X struct heads hdr; /* file header */ X long arcsize, ftell(); /* archive size */ X X openarc(0); /* open archive for reading */ X fseek(arc, 0L, 2); /* move to end of archive */ X arcsize = ftell(arc); /* see how big it is */ X fseek(arc, 0L, 0); /* return to top of archive */ X X while (readhdr(&hdr, arc)) { X if (ftell(arc) + hdr.size > arcsize) { X printf("Archive truncated in file %s\n", hdr.name); X nerrs++; X break; X } else { X printf("Testing file: %-12s ", hdr.name); X fflush(stdout); X if (unpack(arc, NULL, &hdr)) X nerrs++; X else X printf("okay\n"); X } X } X X if (nerrs < 1) X printf("No errors detected\n"); X else if (nerrs == 1) X printf("One error detected\n"); X else X printf("%d errors detected\n", nerrs); X} SHAR_EOF if test 1284 -ne "`wc -c < 'arctst.c'`" then echo shar: "error transmitting 'arctst.c'" '(should have been 1284 characters)' fi fi echo shar: "extracting 'arcunp.c'" '(5347 characters)' if test -f 'arcunp.c' then echo shar: "will not over-write existing file 'arcunp.c'" else sed 's/^X//' << \SHAR_EOF > 'arcunp.c' X/* X * $Header: arcunp.c,v 1.7 88/06/18 03:12:36 hyc Locked $ X */ X X/* X * ARC - Archive utility - ARCUNP X * X * Version 3.17, created on 02/13/86 at 10:20:08 X * X * (C) COPYRIGHT 1985 by System Enhancement Associates; ALL RIGHTS RESERVED X * X * By: Thom Henderson X * X * Description: This file contains the routines used to expand a file when X * taking it out of an archive. X * X * Language: Computer Innovations Optimizing C86 X */ X#include X#include "arc.h" X#if MTS X#include X#endif X Xvoid setcode(), init_usq(), init_ucr(), decomp(), sqdecomp(); Xvoid abort(), putc_tst(); Xint getc_usq(), getc_ucr(), addcrc(); X X/* stuff for repeat unpacking */ X X#define DLE 0x90 /* repeat byte flag */ X Xstatic int state; /* repeat unpacking state */ X X/* repeat unpacking states */ X X#define NOHIST 0 /* no relevant history */ X#define INREP 1 /* sending a repeated value */ X Xstatic short crcval; /* CRC check value */ Xstatic long size; /* bytes to read */ X#if !DOS Xstatic int gotcr; /* got a carriage return? */ X#endif X Xint Xunpack(f, t, hdr) /* unpack an archive entry */ X FILE *f, *t; /* source, destination */ X struct heads *hdr; /* pointer to file header data */ X{ X int c; /* one char of stream */ X void putc_unp(); X void putc_ncr(); X int getc_unp(); X X /* setups common to all methods */ X#if !DOS X gotcr = 0; X#endif X crcval = 0; /* reset CRC check value */ X size = hdr->size; /* set input byte counter */ X state = NOHIST; /* initial repeat unpacking state */ X setcode(); /* set up for decoding */ X X /* use whatever method is appropriate */ X X switch (hdrver) { /* choose proper unpack method */ X case 1: /* standard packing */ X case 2: X while ((c = getc_unp(f)) != EOF) X putc_unp((char) c, t); X break; X X case 3: /* non-repeat packing */ X while ((c = getc_unp(f)) != EOF) X putc_ncr((unsigned char) c, t); X break; X X case 4: /* Huffman squeezing */ X init_usq(f); X while ((c = getc_usq(f)) != EOF) X putc_ncr((unsigned char) c, t); X break; X X case 5: /* Lempel-Zev compression */ X init_ucr(0); X while ((c = getc_ucr(f)) != EOF) X putc_unp((char) c, t); X break; X X case 6: /* Lempel-Zev plus non-repeat */ X init_ucr(0); X while ((c = getc_ucr(f)) != EOF) X putc_ncr((unsigned char) c, t); X break; X X case 7: /* L-Z plus ncr with new hash */ X init_ucr(1); X while ((c = getc_ucr(f)) != EOF) X putc_ncr((unsigned char) c, t); X break; X X case 8: /* dynamic Lempel-Zev */ X decomp(f, t); X break; X X case 9: /* Squashing */ X sqdecomp(f, t); X break; X X default: /* unknown method */ X if (warn) { X printf("I don't know how to unpack file %s\n", hdr->name); X printf("I think you need a newer version of ARC\n"); X nerrs++; X } X fseek(f, hdr->size, 1); /* skip over bad file */ X return 1; /* note defective file */ X } X X /* cleanups common to all methods */ X X if (crcval != hdr->crc) { X if (warn || kludge) { X printf("WARNING: File %s fails CRC check\n", hdr->name); X nerrs++; X } X return 1; /* note defective file */ X } X return 0; /* file is okay */ X} X X/* X * This routine is used to put bytes in the output file. It also performs X * various housekeeping functions, such as maintaining the CRC check value. X */ X Xvoid Xputc_unp(c, t) /* output an unpacked byte */ X char c; /* byte to output */ X FILE *t; /* file to output to */ X{ X crcval = addcrc(crcval, c); /* update the CRC check value */ X#if MTS X if (!image) X atoe(&c, 1); X#endif X#if DOS X putc_tst(c, t); X#else X if (image) X putc_tst(c, t); X else { X if (gotcr) { X gotcr = 0; X if (c != '\n') X putc_tst('\r', t); X } X if (c == '\r') X gotcr = 1; X else X putc_tst(c, t); X } X#endif X} X X/* X * This routine is used to decode non-repeat compression. Bytes are passed X * one at a time in coded format, and are written out uncoded. The data is X * stored normally, except that runs of more than two characters are X * represented as: X * X * X * X * With a special case that a count of zero indicates a DLE as data, not as a X * repeat marker. X */ X Xvoid Xputc_ncr(c, t) /* put NCR coded bytes */ X unsigned char c; /* next byte of stream */ X FILE *t; /* file to receive data */ X{ X static int lastc; /* last character seen */ X X switch (state) { /* action depends on our state */ X case NOHIST: /* no previous history */ X if (c == DLE) /* if starting a series */ X state = INREP; /* then remember it next time */ X else X putc_unp(lastc = c, t); /* else nothing unusual */ X return; X X case INREP: /* in a repeat */ X if (c) /* if count is nonzero */ X while (--c) /* then repeatedly ... */ X putc_unp(lastc, t); /* ... output the byte */ X else X putc_unp(DLE, t); /* else output DLE as data */ X state = NOHIST; /* back to no history */ X return; X X default: X abort("Bad NCR unpacking state (%d)", state); X } X} X X/* X * This routine provides low-level byte input from an archive. This routine X * MUST be used, as end-of-file is simulated at the end of the archive entry. X */ X Xint Xgetc_unp(f) /* get a byte from an archive */ X FILE *f; /* archive file to read */ X{ X register int xx; X unsigned char code(); X X if (!size) /* if no data left */ X return EOF; /* then pretend end of file */ X X size--; /* deduct from input counter */ X xx = getc(f); X return code(xx); /* and return next decoded byte */ X} SHAR_EOF if test 5347 -ne "`wc -c < 'arcunp.c'`" then echo shar: "error transmitting 'arcunp.c'" '(should have been 5347 characters)' fi fi echo shar: "extracting 'arcusq.c'" '(2486 characters)' if test -f 'arcusq.c' then echo shar: "will not over-write existing file 'arcusq.c'" else sed 's/^X//' << \SHAR_EOF > 'arcusq.c' X/* X * $Header: arcusq.c,v 1.2 88/06/02 16:27:44 hyc Locked $ X */ X X/* X * ARC - Archive utility - ARCUSQ X * X * Version 3.14, created on 07/25/86 at 13:04:19 X * X * (C) COPYRIGHT 1985 by System Enhancement Associates; ALL RIGHTS RESERVED X * X * By: Thom Henderson X * X * Description: This file contains the routines used to expand a file which was X * packed using Huffman squeezing. X * X * Most of this code is taken from an USQ program by Richard Greenlaw, which was X * adapted to CI-C86 by Robert J. Beilstein. X * X * Language: Computer Innovations Optimizing C86 X */ X#include X#include "arc.h" X Xvoid abort(); Xint getc_unp(); X X/* stuff for Huffman unsqueezing */ X X#define ERROR (-1) X X#define SPEOF 256 /* special endfile token */ X#define NUMVALS 257 /* 256 data values plus SPEOF */ X Xextern struct nd { /* decoding tree */ X int child[2]; /* left, right */ X} node[NUMVALS]; /* use large buffer */ X Xstatic int bpos; /* last bit position read */ Xstatic int curin; /* last byte value read */ Xstatic int numnodes; /* number of nodes in decode tree */ X Xstatic short Xget_int(f) /* get a 16bit integer */ X FILE *f; /* file to get it from */ X{ X int i,j; X i = getc_unp(f); X j = getc_unp(f) << 8; X return (i | j) & 0xFFFF; X} X Xvoid Xinit_usq(f) /* initialize Huffman unsqueezing */ X FILE *f; /* file containing squeezed data */ X{ X int i; /* node index */ X X bpos = 99; /* force initial read */ X X numnodes = get_int(f); X X if (numnodes < 0 || numnodes >= NUMVALS) X abort("File has an invalid decode tree"); X X /* initialize for possible empty tree (SPEOF only) */ X X node[0].child[0] = -(SPEOF + 1); X node[0].child[1] = -(SPEOF + 1); X X for (i = 0; i < numnodes; ++i) { /* get decoding tree from X * file */ X node[i].child[0] = get_int(f); X node[i].child[1] = get_int(f); X } X} X Xint Xgetc_usq(f) /* get byte from squeezed file */ X FILE *f; /* file containing squeezed data */ X{ X short i; /* tree index */ X X /* follow bit stream in tree to a leaf */ X X for (i = 0; i >= 0;) { /* work down(up?) from root */ X if (++bpos > 7) { X if ((curin = getc_unp(f)) == ERROR) X return (ERROR); X bpos = 0; X X /* move a level deeper in tree */ X i = node[i].child[1 & curin]; X } else X i = node[i].child[1 & (curin >>= 1)]; X } X X /* decode fake node index to original data value */ X X i = -(i + 1); X X /* decode special endfile token to normal EOF */ X X i = (i == SPEOF) ? EOF : i; X return i; X} SHAR_EOF if test 2486 -ne "`wc -c < 'arcusq.c'`" then echo shar: "error transmitting 'arcusq.c'" '(should have been 2486 characters)' fi fi echo shar: "extracting 'getwd.c'" '(786 characters)' if test -f 'getwd.c' then echo shar: "will not over-write existing file 'getwd.c'" else sed 's/^X//' << \SHAR_EOF > 'getwd.c' X/* X * 4.2bsd getwd simulation for Sys V.3 X */ X X#include X X#undef SYSV3 X X#define MAXWD 1024 /* limited by 4.2 getwd(2) */ X X#ifdef SYSV3 X Xchar *getcwd(); X Xchar * Xgetwd(path) Xchar *path; X{ X return(getcwd(path,MAXWD)); X} X X#else X X/* X * 4.2bsd getwd simulation for Sys V.2 X */ X X#include X X#define MAXWD 1024 /* limited by 4.2 getwd(2) */ X Xchar * Xgetwd(path) Xchar *path; X{ X char *nlp; X FILE *fp; X FILE *popen(); X char *strrchr(); X X putenv("IFS= \t\n"); X fp = popen("PATH=/bin:/usr/bin pwd", "r"); X if (fp == NULL) X return 0; X if (fgets(path, MAXWD, fp) == NULL) { X (void) pclose(fp); X return 0; X } X if ((nlp = strrchr(path, '\n')) != NULL) X *nlp = '\0'; X (void) pclose(fp); X return path; X} X#endif X SHAR_EOF if test 786 -ne "`wc -c < 'getwd.c'`" then echo shar: "error transmitting 'getwd.c'" '(should have been 786 characters)' fi fi exit 0 # End of shell archive