/* SQ.C CHANGE HISTORY: * 1.7 Add checkurk() memory check, changed credits 8-14-83 -CAF * Check for proper linking and sufficient memory to execute * Made inbuff and outbuff global for speed gain. * 1.6 Add wild card expansion (wildexp) 6-12-82 -CAF * 1.5 Fix BUG that caused a rare few squeezed files * to be incorrect and fail the USQ crc check. * The problem was that some 17 bit codes were * generated but are not supported by other code. * THIS IS A MAJOR CHANGE affecting TR2.C and SQ.H and * requires recompilation of all files which are part * of SQ. Two basic changes were made: tree depth is now * used as a tie breaker when weights are equal. This * makes the tree shallower. Although that may always be * sufficient, an error trap was added to cause rescaling * of the counts if any code > 16 bits long is generated. * Add debugging displays option '-'. * 1.4 Break up long introductory lines. * Send introduction only to console. * Send errors only to console. * 1.3 Close files properly in case of error exit. * * This program compresses a file without losing information. * The usq.com program is required to unsqueeze the file * before it can be used. * * Typical compression rates are: * .COM 6% (Don't bother) * .ASM 33% (using full ASCII set) * .DIC 46% (using only uppercase and a few others) * Squeezing a really big file takes a few minutes. * * Useage: * SQ item ... * where ... represents more (optional) items and * "item" is either: * drive: to change the output drive * file input file * drive:file input file * - toggle debugging display mode * * If no such items are given on the command line you will be * prompted for commands (one at a time). An empty command * terminates the program. * * SQ uses the dio package, so input and output can be redirected * by special items on the command line such as: * file sends console output to file * +file sends console output to console and file * Also console output of another program using dio can be piped * to the input of this one or vice-versa. Example: * A>fls parameters |sq * where fls might be a program that expands patterns like *.com * to a list of ambiguous file names for sq to squeeze. * * The squeezed file name is formed by changing the second * letter of the file type to Q. If there is no file type, * the squeezed file type is QQQ. If the name exists it is * overwritten! * * Examples: * A>SQ GRUMP makes GRUMP.QQQ on A: * A>SQ D:CRAP.XYZ makes CRAP.XQZ on A: * A>SQ B: D:CRAP.COM makes CRAP.CQM on B: * B>SQ X.A C: Y.B makes X.AQ on B: and Y.BQ on C: * * The transformations compress strings of identical bytes and * then encode each resulting byte value and EOF as bit strings * having lengths in inverse proportion to their frequency of * occurrance in the intermediate input stream. The latter uses * the Huffman algorithm. Decoding information is included in * the squeezed file, so squeezing short files or files with * uniformly distributed byte values will actually increase size. */ #define VERSION "1.7 08-14-83" #include #include #include "sqcom.h" #include "sq.h" #define STDERR 4 /* console only (error) stream */ /* Sneak in a few "local externs": N.B. sq.crl must be linked first! */ char outfile[16]; /* output file spec. */ unsigned Sentinel; /* be sure this doesn't get munged ! */ #define SENTINEL 055555 main(argc, argv) int argc; char *argv[]; { int i,c; int getchar(); /* Directed io version */ char inparg[16]; /* parameter from input */ checkurk(); /* check for armageddon */ Sentinel = SENTINEL; /* unlikely value */ wildexp(&argc, &argv); /* do the shell's work */ dioinit(&argc, argv); /* obey directed to args */ debug = FALSE; /* Initialize output drive to default drive */ outdrv[0] = '\0'; /* But prepare for a specific drive */ outdrv[1] = ':'; outdrv[2] = '\0'; /* string terminator */ /* Process the parameters in order */ for(i = 1; i < argc; ++i) obey(argv[i]); if(argc < 2) { if(! _diflag) { fprintf(STDERR,"File squeezer %s\n", VERSION); fprintf(STDERR,"Conceived by Richard Greenlaw Modified by Chuck Forsberg et al.\n"); fprintf(STDERR,"Accepts redirection and pipes.\n"); fprintf(STDERR, "Parameters (from command line or singly from stdin)\nconsist of output drives and input file names.\n"); } do { fprintf(STDERR, "\n*"); for(i = 0; i < 16; ++i) { if((c = getchar()) == EOF) c = '\n'; /* fake empty (exit) command */ if((inparg[i] = c) == '\n') { inparg[i] = '\0'; break; } } if(inparg[0] != '\0') obey(inparg); } while(inparg[0] != '\0'); } dioflush(); /* clean up any directed io */ if (Sentinel != SENTINEL) fprintf(STDERR,"out of memory: translation suspect\007\n"); } obey(p) char *p; { char *q; if(*p == '-') { /* toggle debug option */ debug = !debug; return; } if(*(p + 1) == ':') { /* Got a drive */ if(isalpha(*p)) { if(*(p+2) == '\0') { /* Change output drive */ printf("\nOutput drive =%s",p); outdrv[0] = *p; return; } } else { fprintf(STDERR, "\nERROR - Ignoring %s", p); return; } } /* Check for ambiguous (wild-card) name */ for(q = p; *q != '\0'; ++q) if(*q == '*' || *q == '?') { fprintf(STDERR, "\nAmbiguous name %s ignored", p); return; } /* First build output file name */ outfile[0] = '\0'; /* empty */ strcat(outfile, outdrv); /* drive */ strcat(outfile, (*(p + 1) == ':') ? p + 2 : p); /* input name */ /* Find and change output file type */ for(q = outfile; *q != '\0'; ++q) if(*q == '.') if(*(q + 1) == '\0') *q = '\0'; /* kill trailing dot */ else switch(*(q+2)) { case 'q': case 'Q': fprintf(STDERR, "\n%s ignored ( already squeezed?)", p); return; case '\0': *(q+3) = '\0'; /* fall thru */ default: *(q + 2) = 'Q'; goto named; } /* No file type */ strcat(outfile, ".QQQ"); named: squeeze(p, outfile); } squeeze(infile, outfile) char *infile, *outfile; { int i, c; printf("\n%s -> %s: ", infile, outfile); if(fopen(infile, &inbuff) == ERROR) { fprintf(STDERR, "Can't open %s\n", infile); return; } if(fcreat(outfile, &outbuff) == ERROR) { fprintf(STDERR, "Can't create %s\n", outfile); fclose(&inbuff); return; } /* First pass - get properties of file */ crc = 0; /* initialize checksum */ init_ncr(); printf("scanning, "); init_huff(); fclose(&inbuff); /* Write output file header with decoding info */ wrt_head(infile); /* Second pass - encode the file */ printf("squeezing, "); if(fopen(infile, &inbuff) == ERROR) { fprintf(STDERR, "Can't open %s\n", infile); goto closeout; } init_ncr(); /* For second pass */ /* Translate the input file into the output file */ while((c = gethuff()) != EOF) if(putc(c, &outbuff) == ERROR) { fprintf(STDERR, "ERROR - write error in %s\n", outfile); goto closeall; } printf(" done."); closeall: fclose(&inbuff); closeout: fflush(&outbuff); fclose(&outbuff); } /* * Check for proper linking and sufficient memory to execute */ checkurk() { char *endext(), *topofmem(), *codend(), *externs(); if (codend() > externs() /* check for bad -e value! */ || (topofmem()-1000) < endext() ) { printf("checkurk(): bad memory layout\n"); exit(); } }