home *** CD-ROM | disk | FTP | other *** search
/ Oakland CPM Archive / oakcpm.iso / sigm / vol144 / sq.cq / SQ.C
Encoding:
Text File  |  1985-02-10  |  10.9 KB  |  375 lines

  1. /*-----------------------------------------------------------------------------
  2.  
  3.     'SQ' version 1.5             Richard Greenlaw,
  4.                     251 Colony Ct,
  5.                     Gahanna, Ohio 43230,
  6.                     U.S.A.
  7.  
  8.     Version 1.5a amendments     John Hastwell-Batten,
  9.                     38 Silvia St,
  10.                     Hornsby, NSW 2077,
  11.                     Australia.
  12.  
  13. This program compresses a file without losing information.  The companion
  14. program USQ.COM is required to unsqueeze the file before it can be used.
  15.  
  16. Typical compression rates are:
  17.     .COM    6%    (Don't bother)
  18.     .ASM    33%    (using full ASCII set)
  19.     .DIC    46%    (using only uppercase and a few others)
  20. Squeezing a really big file takes a few minutes.
  21.  
  22. Usage:
  23.     SQ item ...
  24.     where ... represents more (optional) items and
  25.     "item" is either:
  26.         drive:        to change the output drive
  27.         file        input file
  28.         drive:file    input file
  29.         -        toggle debugging display mode
  30.         +        toggle "permission" flag
  31.  
  32. Ambiguous file names are supported by version 1.5a of this utility through
  33. Leor Zolman's WILDEXP routine which also allows for EXCLUDING files from
  34. the parameter list.  See the documentation in WILDEXP.C for details.
  35.  
  36. The squeezed file name is formed by changing the second letter of the file
  37. type to Q. If there is no file type, the squeezed file type is QQQ.  If the
  38. name exists it is overwritten!
  39.  
  40. Examples:
  41.     A>SQ GRUMP        makes GRUMP.QQQ on A:
  42.     A>SQ D:CRAP.XYZ        makes CRAP.XQZ on A:
  43.     A>SQ B: D:CRAP.COM    makes CRAP.CQM on B:
  44.     B>SQ X.A C: Y.B        makes X.AQ on B: and Y.BQ on C:
  45.     A>sq    .*        squeezes all files on A:
  46.     A>sq b:    .*        puts squeezed versions of all
  47.                 files from A: onto B:
  48.     A>sq *.* !-.* !*.com    squeezes all files on A: except
  49.                 .COM files and those having
  50.                 names starting with a hyphen
  51.     A>sq b:    .* !*.com    squeeze all except .COM files
  52.                 onto B:
  53.  
  54. The transformations compress strings of identical bytes and then encode
  55. each resulting byte value and EOF as bit strings having lengths in
  56. inverse proportion to their frequency of occurrence in the intermediate
  57. input stream. The latter uses the Huffman algorithm.  Decoding infor-
  58. mation is included in the squeezed file, so squeezing short files or
  59. files with uniformly distributed byte values will actually increase size.
  60.  
  61. SQ version 1.5a checks the output file size.  If it is not WORTHWHILE
  62. percent smaller than the original then it will automatically discard the
  63. output file, and, if the output file was created on a different disk from
  64. the original then the input file will be copied in place of the discarded
  65. output.
  66.  
  67. If the + option is active and a poorly-compressed file is generated then
  68. SQ 1.5a will ask permission before discarding the output file (and maybe
  69. copying the input file.
  70.  
  71. In general, version 1.5a should make it easy to squeeze whole disks with
  72. a series of commands such as:
  73.  
  74.     findbad b:            ;Validate the output disk
  75.     pip b:=a:-*.*            ;Copy the volume id files
  76.     pip b:=a:crclist.*        ;Copy the CRC file if it exists
  77.     sq b: *.* !-*.* !crclist.*    ;Squeeze everything else
  78.  
  79. -------------------------------------------------------------------------------
  80.  
  81. CHANGE HISTORY:
  82.  
  83. 1.3    Close files properly in case of error exit.
  84.  
  85. 1.4    Break up long introductory lines.
  86.     Send introduction only to console.
  87.     Send errors only to console.
  88.  
  89. 1.5    Fix BUG that caused a rare few squeezed files to be incorrect and
  90.     fail the USQ crc check.
  91.  
  92.     The problem was that some 17 bit codes were generated but are not
  93.     supported by other code.  THIS IS A MAJOR CHANGE affecting TR2.C
  94.     and SQ.H and requires recompilation of all files which are part
  95.     of SQ.  Two basic changes were made: (1) tree depth is now used
  96.     as a tie-breaker when weights are equal.  This makes the tree
  97.     shallower.  Although that may always be sufficient, (2) an error
  98.     trap was added to cause rescaling of the counts if any code more
  99.     than 16 bits long is generated.
  100.  
  101.     Add debugging displays option '-'.
  102.  
  103. 1.5a    Leor Zolman's WILDEXP command-line preprocessor routine is invoked
  104.     to support ambiguous file names and exclusions.  I/O redirection
  105.     was removed.
  106.  
  107.     Output file size checking was implemented to trap the creation of
  108.     poorly-compressed files.
  109.  
  110.     Instead of ignoring files which are already squeezed, SQ will
  111.     copy them if the destination disk is different from the source
  112.     disk.
  113.  
  114.     The only changes to modules other than SQ.C were to remove the
  115.     "#include <dio.h>" statements and to replace "fprintf(STDERR.."
  116.     with "printf(.."
  117.  
  118. 1.6    (Obsolete)  Added simple wildcard expansion.
  119.  
  120. -----------------------------------------------------------------------------*/
  121.  
  122. #define        VERSION        "1.5a"
  123. #define        DATE        "19th June 1983"
  124. #include    <bdscio.h>
  125. #include    "sqcom.h"
  126. #include    "sq.h"
  127. #define     WORTHWHILE     8    /* any less compression than 8% is
  128.                        hardly worth it! */
  129. #define        THRESH        9    /* no real point in squeezing any
  130.                        file shorter than 9 sectors */
  131. char    default_drive,
  132.     newdrive,
  133.     permission,
  134.     l_worthwhile[4],
  135.     l_num[4],
  136.     l_den[4],
  137.     one_hundred[4],
  138.     l_pct[8];
  139. int    orig_sects,
  140.     sq_sects,
  141.     unsq_sects;
  142.  
  143. main(argc, argv)
  144. int argc;
  145. char *argv[];
  146. {
  147.     int i,c;
  148.  
  149.     if (wildexp(&argc,&argv))
  150.        exit(puts("\07**** Too many files to process\n"));
  151.  
  152.     itol(l_worthwhile,WORTHWHILE);
  153.     itol(one_hundred,100);
  154.     permission = TRUE;
  155.     debug = FALSE;
  156.     printf("\n\n\tSQUEEZE version %s (%s)\n",VERSION,DATE);
  157.     printf("\tA file compression utility by Richard Greenlaw\n");
  158. /*    printf("\t251 Colony Ct, Gahanna, Ohio 43230\n");    */
  159.  
  160.     /* Initialize output drive to default drive */
  161.     outdrv[0]  = '\0';
  162.     /* But prepare for a specific drive */
  163.     outdrv[1] = ':';
  164.     outdrv[2] = '\0';    /* string terminator */
  165.     default_drive = bdos(25,0)+'A';
  166.  
  167.     /* Process the parameters in order */
  168.     for(i = 1; i < argc; ++i)
  169.         obey(argv[i]);
  170.     if (argc > 1) {
  171.         printf("\n\n%d sectors were compressed to %d representing a ",
  172.             orig_sects, sq_sects);
  173.         printf("%s%c shrinkage.\n",
  174.         percent(orig_sects-sq_sects,orig_sects),'%');
  175.         printf("%d sectors were not squeezed.  Nett shrinkage was %s%c",
  176.         unsq_sects,
  177.         percent(orig_sects-sq_sects,orig_sects+unsq_sects),'%');
  178.     }
  179. }
  180.  
  181. percent(numerator,denominator)
  182. int    numerator,denominator;
  183. {
  184. /*
  185.     Returns a pointer to an ASCII string representation of the quantity
  186.             numerator*100/denominator
  187.                                     */
  188.    return(ltoa(l_pct,ldiv(l_num,lmul(l_num,itol(l_num,numerator),
  189.         one_hundred),itol(l_den,denominator))));
  190. }
  191.  
  192. obey(p)
  193. char *p;
  194. {
  195.     char *q, just_copy;
  196.     char outfile[16];    /* output file spec. */
  197.  
  198.     if((*p == '-') && (*(p+1)=='\0')) {
  199.         /* toggle debug option */
  200.         debug = !debug;
  201.         return;
  202.     }
  203.     if((*p == '+') && (*(p+1)=='\0')) {
  204.         /* toggle "permission" flag */
  205.         permission = !permission;
  206.         return;
  207.     }
  208.     if(*(p + 1) == ':') {
  209.         /* Got a drive */
  210.         if(isalpha(*p)) {
  211.             if(*(p+2) == '\0') {
  212.                 /* Change output drive */
  213.                 printf("\nOutput drive =%s",p);
  214.                 outdrv[0] = *p;
  215.                 newdrive=(*p != default_drive);
  216.                 return;
  217.             }
  218.         } else {
  219.             printf( "\nERROR - Ignoring %s", p);
  220.             return;
  221.         }
  222.     }
  223.  
  224.     /* First build output file name */
  225.     outfile[0] = '\0';        /* empty */
  226.     strcat(outfile, outdrv);    /* drive */
  227.     strcat(outfile, (*(p + 1) == ':') ? p + 2 : p);    /* input name */
  228.  
  229. /* Find and change output file type */
  230.   just_copy = FALSE;
  231.   for(q = outfile; *q != '\0'; ++q)
  232.      if(*q == '.')
  233.         if(*(q + 1) == '\0')
  234.            *q = '\0';    /* kill trailing dot */
  235.     else
  236.        switch(*(q+2))
  237.        {
  238. case 'q':
  239. case 'Q':
  240.          just_copy = TRUE;    /* just copy to output if necessary */
  241. case '\0':
  242.          *(q+3) = '\0';
  243.          /* fall thru */
  244. default:
  245.          *(q + 2) = 'Q';
  246.          goto named;
  247.       }
  248. /* No file type */
  249.    strcat(outfile, ".QQQ");
  250. named:
  251.    squeeze(p, outfile, just_copy);
  252. }
  253.  
  254. squeeze(infile, outfile, just_copy)
  255. char *infile, *outfile, just_copy;
  256. {
  257.     int orgsize, newsize;
  258.     int i, c, gain;
  259.     char junkit, copy_original;
  260.     struct _buf inbuff, outbuff;    /* file buffers */
  261.  
  262.     printf("\n\n%s -> %s: ", infile, outfile);
  263.  
  264.     if(fopen(infile, &inbuff) == ERROR) {
  265.        printf( "Can't open %s for input pass 1\n", infile);
  266.        return;
  267.     }
  268.     if (((orgsize=cfsize(inbuff._fd)) < THRESH) || just_copy) {
  269.        unsq_sects += orgsize;
  270.        printf("%d sectors - ",orgsize);
  271.        if (just_copy) printf("already squeezed?\n");
  272.        else printf("too short to be worth squeezing\n");
  273.        copy_original=newdrive;
  274.        fabort(inbuff._fd);    /* File gets opened again later */
  275.        goto copy;
  276.     }
  277.     if(fcreat(outfile, &outbuff) == ERROR) {
  278.        printf( "Can't create %s\n", outfile);
  279.        fclose(&inbuff);
  280.        return;
  281.     }
  282.     junkit = copy_original = FALSE;
  283.  
  284. /* First pass - get properties of file */
  285.     crc = 0;    /* initialize checksum */
  286.     printf("analyzing, ");
  287.     init_ncr();
  288.     init_huff(&inbuff);   
  289.     fclose(&inbuff);
  290.  
  291.     /* Write output file header with decoding info */
  292.     wrt_head(&outbuff, infile);
  293.  
  294.     /* Second pass - encode the file */
  295.     printf("squeezing, ");
  296.     if(fopen(infile, &inbuff) == ERROR) {
  297.        printf( "Can't open %s for input pass 2\n", infile);
  298.        junkit = TRUE;
  299.        goto closeout;
  300.     }
  301.     init_ncr();    /* For second pass */
  302.  
  303.     /* Translate the input file into the output file */
  304.     while((c = gethuff(&inbuff)) != EOF)
  305.        if(putc(c, &outbuff) == ERROR) {
  306.           printf( "ERROR - write failure in %s\n", outfile);
  307.           junkit = TRUE;
  308.           goto closeall;
  309.        }
  310.     printf("done.");
  311.  
  312.     orgsize=cfsize(inbuff._fd);
  313.     fflush(&outbuff);
  314.     newsize=cfsize(outbuff._fd);
  315.     printf("\nInput %d sectors, output %d sectors, i.e. ",
  316.             orgsize,newsize);
  317.     if (orgsize-newsize) {
  318.        printf("a%ssion of %d sectors (%s%c)",
  319.         ((orgsize<newsize) ? "n expan" : " compres"),
  320.         abs(orgsize-newsize),
  321.         (orgsize ? percent(abs(orgsize-newsize),orgsize) : "INFINITY"),
  322.         '%');
  323.     }
  324.     else
  325.        printf("no change in file size");
  326.  
  327.     if ((newsize>=orgsize) || (lcomp(l_num,l_worthwhile)-1))
  328.        if (junkit=permission) {
  329.           printf("\nDiscarding output file\n");
  330.           copy_original = newdrive; }
  331.        else {
  332.           printf("\nShould I discard the output file%s?",
  333.           (newdrive ? " and copy the original" : ""));
  334.           if (junkit=(tolower(c=getchar()) != 'n')) {
  335.          copy_original = newdrive;
  336.          if (c != '\n')
  337.             putc('\n');
  338.           }
  339.        }
  340.     if (junkit)
  341.        unsq_sects += orgsize;
  342.     else
  343.     {
  344.        orig_sects += orgsize;
  345.        sq_sects += newsize;
  346.     }
  347.  
  348. closeall:
  349.     fclose(&inbuff);
  350. closeout:
  351.     fflush(&outbuff);
  352.     fclose(&outbuff);
  353.     if (junkit)
  354.        unlink(outfile);
  355. copy:
  356.     if (copy_original) {
  357.        fopen(infile,&inbuff);    /* don't have to check result */
  358.        outfile[0] = '\0';        /* empty */
  359.        strcat(outfile, outdrv);    /* drive */
  360.        strcat(outfile,(*(infile+1)==':') ? infile+2 : infile);
  361.        if (fcreat(outfile,&outbuff)==ERROR)
  362.         printf("Cannot open output %s%s for simple copy",
  363.             outdrv,outfile);
  364.        else {
  365.         printf("Copying %s unchanged to %s disk",infile,outdrv);
  366.         while ((c=getc(&inbuff)) != EOF)
  367.             if (putc(c,&outbuff)==ERROR) {
  368.             printf("\nError during copy - disk full?");
  369.             break;
  370.             }
  371.        }
  372.        copy_original = junkit = FALSE;
  373.        goto closeall; /* Go back and tidy up the files */
  374.     }
  375. }