/* ARC - Archive utility - ARCPACK (C) COPYRIGHT 1985 by System Enhancement Associates; ALL RIGHTS RESERVED By: Thom Henderson Description: This file contains the routines used to compress a file when placing it in an archive. Language: Computer Innovations Optimizing C86 */ #include #include "arc.h" /* stuff for non-repeat packing */ #define DLE 0x90 /* repeat sequence marker */ static unsigned char state; /* current packing state */ /* non-repeat packing states */ #define NOHIST 0 /* don't consider previous input*/ #define SENTCHAR 1 /* lastchar set, no lookahead yet */ #define SENDNEWC 2 /* run over, send new char next */ #define SENDCNT 3 /* newchar set, send count next */ /* packing results */ static long stdlen; /* length for standard packing */ static int crcval; /* CRC check value */ pack(f,t,hdr) /* pack file into an archive */ FILE *f, *t; /* source, destination */ struct heads *hdr; /* pointer to header data */ { int c; /* one character of stream */ long ncrlen; /* length after packing */ long huflen; /* length after squeezing */ long lzwlen; /* length after crunching */ long pred_sq(), file_sq(); /* stuff for squeezing */ long pred_cm(); /* dynamic crunching cleanup */ char tnam[STRLEN]; /* temporary name buffer */ char *makefnam(); /* filename fixer upper */ FILE *crn = NULL; /* temporary crunch file */ FILE *fopen(); /* first pass - see which method is best */ if(!nocomp) /* if storage kludge not active */ { if(note) printf(" analyzing, "); if(arctemp) /* use temp area if specified */ sprintf(tnam,"%s/$ARCTEMP.CRN",arctemp); else makefnam("$ARCTEMP.CRN",arcname,tnam); crn = fopen(tnam,"w+"); state = NOHIST; /* initialize ncr packing */ stdlen = ncrlen = 0; /* reset size counters */ crcval = 0; /* initialize CRC check value */ setcode(); /* initialize encryption */ init_cm(f,crn); /* initialize for crunching */ init_sq(); /* initialize for squeeze scan */ while((c=getc_ncr(f))!=EOF) /* for each byte of file */ { ncrlen++; /* one more packed byte */ scan_sq(c); /* see what squeezing can do */ putc_cm(c,crn); /* see what crunching can do */ } huflen = pred_sq(); /* finish up after squeezing */ lzwlen = pred_cm(crn); /* finish up after crunching */ } else /* else kludge the method */ { stdlen = 0; /* make standard look best */ ncrlen = huflen = lzwlen = 1; } /* standard set-ups common to all methods */ fseek(f,0L,0); /* rewind input */ hdr->crc = crcval; /* note CRC check value */ hdr->length = stdlen; /* set actual file length */ state = NOHIST; /* reinitialize ncr packing */ setcode(); /* reinitialize encryption */ /* choose and use the shortest method */ if(stdlen<=ncrlen && stdlen<=huflen && stdlen<=lzwlen) { if(kludge) /*DEBUG*/ printf("(%ld) ",lzwlen-stdlen); if(note) printf("storing, "); /* store without compression */ hdrver = 2; /* note packing method */ stdlen = crcval = 0; /* recalc these for kludge */ while((c=getch(f))!=EOF) /* store it straight */ putc_pak(c,t); hdr->crc = crcval; hdr->length = hdr->size = stdlen; } else if(ncrlensize = ncrlen; /* set data length */ while((c=getc_ncr(f))!=EOF) putc_pak(c,t); } else if(huflensize = file_sq(f,t); /* note final size */ } else { if(kludge) /*DEBUG*/ printf("(%ld) ",huflen-lzwlen); if(note) printf("crunching, "); hdrver = 8; hdr->size = lzwlen; /* size should not change */ if(crn) /* if temp was created */ { fseek(crn,0L,0); /* then copy over crunched temp */ while((c=fgetc(crn))!=EOF) putc_tst(c,t); } else /* else re-crunch */ { init_cm(f,t); while((c=getc_ncr(f))!=EOF) putc_cm(c,t); pred_cm(t); /* finish up after crunching */ } } /* standard cleanups common to all methods */ if(crn) /* get rid of crunch temporary */ { fclose(crn); if(unlink(tnam) && warn) { printf("Cannot delete temporary file %s\n",tnam); nerrs++; } } if(note) printf("done.\n"); } /* Non-repeat compression - text is passed through normally, except that a run of more than two is encoded as: Special case: a count of zero indicates that the DLE is really a DLE, not a repeat marker. */ int getc_ncr(f) /* get bytes with collapsed runs */ FILE *f; /* file to get from */ { static int lastc; /* value returned on last call */ static int repcnt; /* repetition counter */ static int c; /* latest value seen */ switch(state) /* depends on our state */ { case NOHIST: /* no relevant history */ state = SENTCHAR; return lastc = getch(f); /* remember the value next time */ case SENTCHAR: /* char was sent. look ahead */ switch(lastc) /* action depends on char */ { case DLE: /* if we sent a real DLE */ state = NOHIST; /* then start over again */ return 0; /* but note that the DLE was real */ case EOF: /* EOF is always a special case */ return EOF; default: /* else test for a repeat */ for(repcnt=1; (c=getch(f))==lastc && repcnt<255; repcnt++) ; /* find end of run */ switch(repcnt) /* action depends on run size */ { case 1: /* not a repeat */ return lastc = c; /* but remember value next time */ case 2: /* a repeat, but too short */ state = SENDNEWC; /* send the second one next time */ return lastc; default: /* a run - compress it */ state = SENDCNT; /* send repeat count next time */ return DLE; /* send repeat marker this time */ } } case SENDNEWC: /* send second char of short run */ state = SENTCHAR; return lastc = c; case SENDCNT: /* sent DLE, now send count */ state = SENDNEWC; return repcnt; default: abort("Bug - bad ncr state\n"); } } static int getch(f) /* special get char for packing */ FILE *f; /* file to get from */ { int c; /* a char from the file */ if((c=fgetc(f))!=EOF) /* if not the end of file */ { crcval = addcrc(crcval,c); /* then update CRC check value */ stdlen++; /* and bump length counter */ } return c; } putc_pak(c,f) /* put a packed byte into archive */ char c; /* byte to put */ FILE *f; /* archive to put it in */ { putc_tst(code(c),f); /* put encoded byte, with checks */ }