home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / PASCAL / ADC_TP3.ZIP / SQUEEZE.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1987-03-01  |  25.6 KB  |  705 lines

  1. {$C-}
  2. {$R-}
  3. program Squeezer;
  4.  
  5.   const version   = '1.9  last update 08-28-85';
  6.  
  7. { CP/M compatible file squeezer utility.
  8.  
  9.   This translation uses the Huffman algorithm to develop a binary tree representing the decoding information
  10.   for a variable length bit string code for each input value.  Each string's length is in inverse proportion
  11.   to its frequency of appearance in the incoming data stream.  The encoding table is derived from the decoding
  12.   table.
  13.  
  14.   The range of valid values into the Huffman algorithm are the values of a byte stored in an integer plus the
  15.   special endfile value chosen to be an adjacent value. Overall, 0-SPEOF.
  16.  
  17.   The algorithm develops the single element trees into a single binary tree by forming subtrees rooted in
  18.   interior nodes having weights equal to the sum of weights of all their descendents and having depth counts
  19.   indicating the depth of their longest paths.
  20.  
  21.   When all trees have been formed into a single tree satisfying the heap property (on weight, with depth as a
  22.   tie breaker) then the binary code assigned to a leaf (value to be encoded) is then the series of left (0) and
  23.   right (1) paths leading from the root to the leaf.  Note that trees are removed from the heaped list by moving
  24.   the last element over the top element and reheaping the shorter list.
  25.  
  26.   To further compress the output code stream, all bytes pass directly through except for:
  27.         1) DLE is encoded as (DLE, zero).
  28.         2) repeated byte values (count >= 3) are encoded as (value, DLE, count).
  29.  
  30.   In the original design it was believed that a Huffman code would fit in the same number of bits that will hold
  31.   the sum of all the counts.  That was disproven by a user's file and was a rare but infamous bug. This version
  32.   attempts to choose among equally weighted subtrees according to their maximum depths to avoid unnecessarily long
  33.   codes. In case that is not sufficient to guarantee codes <= 16 bits long, we initially scale the counts so the
  34.   total fits in an unsigned integer, but if codes longer than 16 bits are generated the counts are rescaled to a
  35.   lower ceiling and code generation is retried.
  36.  
  37.   The "node" array of structures contains the nodes of the binary tree. The first NUMVALS nodes are the leaves
  38.   of the tree and represent the values of the data bytes being encoded and the special endfile, SPEOF.  The
  39.   remaining nodes become the internal nodes of the tree.
  40.  
  41.   Program states:
  42.      NoHist    don't consider previous input
  43.      SentChar  lastchar set, no lookahead yet
  44.      SendNewC  newchar set, previous sequence done
  45.      SendCnt   newchar set, DLE sent, send count next
  46. }
  47. {.pa}
  48.   const space     = ' ';
  49.         Error     = -1;
  50.         Null      = -2;
  51.         Recognize = $FF76;  { unlikely pattern }
  52.         DLE       = #$90;
  53.         SPEOF     = 256;    { special endfile token }
  54.         NumVals   = 257;    { 256 data values plus SPEOF }
  55.         NumNodes  = 513;    { = 2 * NUMVALS - 1 = number of nodes }
  56.         NoChild   = -1;     { indicates end of path through tree }
  57.         maxcount  = MAXINT; { biggest UNSIGNED integer }
  58.         RecSize   = 16384;  { read in 16384 byte chunks  }
  59.  
  60.    type FileName = string[30];
  61.         ValType  = array[0..numvals] of integer;
  62.         StateTypes = (NoHist,SentChar,SendNewC,SendCnt,EndFile);
  63.         NodeType = record
  64.                      weight: integer;          { number of appearances }
  65.                      tdepth: integer;          { length on longest path in tree }
  66.                      lchild, rchild: integer;  { indices to next level }
  67.                    end;
  68.         bytechar = record case boolean of
  69.                    true : (c:char);
  70.                    false: (b:byte);
  71.                    end;
  72.         FBuffer  = Array [ 1..RecSize] of bytechar;
  73.         BufPlace = record
  74.                    RecOff : integer;
  75.                    MaxBuf : integer;
  76.                    end;
  77.  
  78.     var InFileName, OutFileName: FileName;
  79.         InFile, OutFile: file;
  80.         InBuf, OutBuf: FBuffer;
  81.         start, finish, i: integer;
  82.         crc: integer;      { Cyclic Redundancy Check code }
  83.  
  84.         likect: bytechar;   { count of consecutive identical chars }
  85.         lastchar, newchar: bytechar;
  86.  
  87.         State: StateTypes;
  88.         EOFlag, done: boolean;
  89.  
  90.         node: array[0..NUMNODES] of NodeType;
  91.         dctreehd: integer;        { index to head node of final tree }
  92.  
  93.         Incnt : BufPlace ;   { input buffer position }
  94.         Outcnt: BufPlace ;   { output buffer position}
  95.         End_of_InFile : boolean;    { hit end of input file }
  96.  
  97. { This is the encoding table:  The bit strings have first bit in = low bit.
  98.   Note that counts were scaled so code fits UNSIGNED integer }
  99.  
  100.         codelen, code: array[0..numvals] of integer; { number of bits in code & code itself, right adjusted }
  101.         tcode: integer;     { temporary code value }
  102.  
  103.         curin: integer;     { Value currently being encoded }
  104.         cbitsrem: integer;  { Number of code string bits remaining }
  105.         ccode: integer;     { Current code shifted so next code bit is at right }
  106. {.pa}
  107. {.cp12}
  108.   procedure zero_tree;
  109.     { Initialize all nodes to single element binary trees with zero weight and depth. }
  110.     var i: integer;
  111.     begin
  112.       for i := 0 to NUMNODES
  113.         do begin
  114.              with node[i] do
  115.                begin
  116.                  weight := 0;
  117.                  tdepth := 0;
  118.                  lchild := NoChild;
  119.                  rchild := NoChild;
  120.                end;
  121.            end;
  122.     end;
  123. {.cp8}
  124.   procedure Reset_InFile;
  125.   begin
  126.     with Incnt do
  127.       begin
  128.         RecOff:=RecSize;
  129.         MaxBuf:=RecSize;
  130.       end;
  131.     End_of_Infile:=false;
  132.   end;
  133.   procedure Reset_OutFile;
  134.   begin
  135.     with Outcnt do
  136.       RecOff:=0;
  137.   end;
  138. {.cp8}
  139.   procedure Out_write(b:byte);
  140.     begin
  141.       with Outcnt do
  142.         begin
  143.           RecOff:=RecOff+1;                           { up offset            }
  144.           if RecOff > RecSize then                    { buffer, end of record}
  145.             begin                                     { yep, next record     }
  146.               BlockWrite(OutFile,OutBuf,RecSize,MaxBuf);
  147.               RecOff:=1;                              { reset offset         }
  148.             end;                                      { end end of buffer    }
  149.           OutBuf[RecOff].b:=b;                        { put byte in buffer   }
  150.         end;  { with }                                { end with Outcnt      }
  151.     end;  { Out_write }
  152. {.cp8}
  153.   procedure Out_Flush;
  154.     begin
  155.       with Outcnt do
  156.         if RecOff > 0 then
  157.           BlockWrite(OutFile,OutBuf,RecOff,MaxBuf);
  158.     end;
  159. {.cp8}
  160.   procedure putwe(w: integer);
  161.     { write out low order byte of word to file, then high order byte regardless of host CPU. }
  162.     begin
  163.       Out_write(Lo(w));
  164.       Out_write(Hi(w));
  165.     end;
  166. {.cp8}
  167.   procedure In_read(var b:byte);
  168.     begin
  169.       with Incnt do
  170.         begin
  171.           RecOff:=RecOff+1;
  172.           if RecOff > MaxBuf then                     { buffer, end of record}
  173.             begin                                     { yep, next record     }
  174.               BlockRead(InFile,InBuf,RecSize,MaxBuf);
  175.               RecOff:=1;                              { reset offset         }
  176.             end;                                      { end end of buffer    }
  177.           b:=InBuf[RecOff].b;                         { pull out a byte      }
  178.           if MaxBuf=0 then                            { any more input ??    }
  179.             End_of_InFile:=true;                      { no .. set flag       }
  180.         end;  { with }                                { end with Incnt       }
  181.     end;  { In_read }
  182. {.cp8}
  183.   function GetC_CRC: char;
  184.     { Get next byte from file and update checksum }
  185.     var c: bytechar;
  186.     begin
  187.       In_read(c.b);
  188.       if not(End_of_InFile) then
  189.         crc := crc + c.b { update checksum }
  190.       else
  191.         EOFlag := true;
  192.       GetC_CRC := c.c;  {undefined if EOFlag is true}
  193.     end;
  194. {.cp11}
  195.   procedure PrintBits(len, number: integer);
  196.   var i, j: integer;
  197.   begin
  198.     write('  code ');
  199.     for i:=len-1 downto 0
  200.       do begin
  201.            j := (number shr i) and $0001;
  202.            write(j:1);
  203.          end;
  204.     writeln;
  205.   end;
  206. {.pa}
  207.   function getcnr: char;
  208.     var return: char;
  209.  
  210.     function alike: boolean;
  211.       begin
  212.         newchar.c := GetC_CRC;
  213.         if EOFlag
  214.           then alike := false
  215.           else begin
  216.                  if (newchar.c = lastchar.c) and (likect.b < 255)
  217.                    then alike := true
  218.                    else alike := false;
  219.                end;
  220.       end;
  221.  
  222.     procedure NoHistory; {set up the state machine}
  223.       begin
  224.         state := SentChar;
  225.         lastchar.c := GetC_CRC;
  226.         if EOFlag then state := EndFile;
  227.         return := lastchar.c;
  228.       end;
  229.  
  230.     procedure SentAChar;   {Lastchar is set, need lookahead}
  231.  
  232.       procedure SentDLE;
  233.         begin
  234.           state := NoHist;
  235.           return := #$00;
  236.         end;
  237.  
  238.       procedure CheckAlike;
  239.         begin
  240.           likect.b := 1;   while alike do likect.b := likect.b + 1;
  241.           case likect.b of
  242.             1: begin
  243.                  lastchar := newchar;
  244.                  return := lastchar.c;
  245.                end;
  246.             2: begin { just pass through }
  247.                  state := SendNewC;
  248.                  return := lastchar.c;
  249.                end;
  250.           else
  251.             state := SendCnt;
  252.             return := DLE;
  253.           end;
  254.         end;
  255.  
  256.       begin
  257.         if EOFlag
  258.           then state := EndFile {no return value, set to SPEOF in calling routine}
  259.           else begin
  260.                  if lastchar.c = DLE
  261.                    then SentDLE
  262.                    else CheckAlike;
  263.                end;
  264.       end;
  265.  
  266.     procedure SendNewChar;   {Previous sequence complete, newchar set}
  267.       begin
  268.         state := SentChar;
  269.         lastchar := newchar;
  270.         return := lastchar.c;
  271.       end;
  272.  
  273.     procedure SendCount;  {Sent DLE for repeat sequence, send count}
  274.       begin
  275.         state := SendNewC;
  276.         return := likect.c;
  277.       end;
  278.  
  279.     begin
  280.       case state of
  281.         NoHist:   NoHistory;
  282.         SentChar: SentAChar;
  283.         SendNewC: SendNewChar;
  284.         SendCnt:  SendCount;
  285.       else writeln('program bug - bad state');
  286.       end;
  287.       getcnr := return;
  288.     end;
  289. {.pa}
  290.   procedure Write_Header;
  291.   { Write out the header of the compressed file }
  292.  
  293.     var i, k, l, r, numnodes: integer;
  294.         { numnodes: nbr of nodes in simplified tree }
  295.  
  296.     begin
  297.       putwe(RECOGNIZE);   { identifies as compressed }
  298.       putwe(crc);         { unsigned sum of original data }
  299.  
  300.       { Record the original file name w/o drive }
  301.       if (InFileName[2] = ':')
  302.         then InFileName := copy(InFileName,3,length(InFileName)-2);
  303.  
  304.       InFileName := InFileName + chr(0);  {mark end of file name}
  305.       for i:=1 to length(InFileName) do
  306.         Out_write(Ord(InFileName[i]));
  307.  
  308.       { Write out a simplified decoding tree. Only the interior nodes are written. When a child is a leaf index
  309.         (representing a data value) it is recoded as -(index + 1) to distinguish it from interior indexes which
  310.         are recoded as positive indexes in the new tree.  Note that this tree will be empty for an empty file. }
  311.  
  312.       if dctreehd < NUMVALS
  313.         then numnodes := 0
  314.         else numnodes := dctreehd - (NUMVALS - 1);
  315.       putwe(numnodes);
  316.  
  317.       i := dctreehd;
  318.       for k:=0 to numnodes-1
  319.         do begin
  320.              l := node[i].lchild;
  321.              r := node[i].rchild;
  322.              if l < NUMVALS
  323.                then l := -(l + 1)
  324.                else l := dctreehd - l;
  325.              if r < NUMVALS
  326.                then r := -(r + 1)
  327.                else r := dctreehd - r;
  328.              putwe(l);  { left child }
  329.              putwe(r);  { right child }
  330.              i := i - 1;
  331.            end;
  332.     end;
  333. {.pa}
  334.   procedure Adjust(top, bottom: integer; var list: ValType);
  335.   { Make a heap from a heap with a new top }
  336.  
  337.     var k, temp: integer;
  338.  
  339.     function cmptrees(a, b: integer): boolean; {entry with root nodes}
  340.     { Compare two trees, if a > b return true, else return false. }
  341.       begin
  342.         cmptrees := false;
  343.         if node[a].weight > node[b].weight
  344.           then cmptrees := true
  345.           else if node[a].weight = node[b].weight
  346.                  then if node[a].tdepth > node[b].tdepth
  347.                         then cmptrees := true;
  348.       end;
  349.  
  350.     begin
  351.       k := 2 * top + 1;    { left child of top }
  352.       temp := list[top];   { remember root node of top tree }
  353.       if (k <= bottom)
  354.         then begin
  355.                if (k < bottom) and (cmptrees(list[k], list[k + 1])) then k := k + 1;
  356.                { k indexes "smaller" child (in heap of trees) of top
  357.                  now make top index "smaller" of old top and smallest child }
  358.                if cmptrees(temp,list[k])
  359.                  then begin
  360.                         list[top] := list[k];
  361.                         list[k] := temp;
  362.                         adjust(k, bottom, list);
  363.                       end;
  364.              end;
  365.     end;
  366.  
  367. {.pa}
  368. { The count of number of occurrances of each input value have already been prevented from exceeding MAXCOUNT.
  369.   Now we must scale them so that their sum doesn't exceed ceiling and yet no non-zero count can become zero.
  370.   This scaling prevents errors in the weights of the interior nodes of the Huffman tree and also ensures that
  371.   the codes will fit in an unsigned integer.  Rescaling is used if necessary to limit the code length. }
  372.  
  373.   procedure Scale(ceil: integer);  { upper limit on total weight }
  374.  
  375.     var i, c, ovflw, divisor: integer;
  376.         w, sum: real;
  377.         increased: boolean;
  378.  
  379.     begin
  380.       repeat
  381.           sum := 0;   ovflw := 0;
  382.           for i:=0 to numvals-1
  383.             do begin
  384.                  if node[i].weight > (ceil - sum) then ovflw := ovflw + 1;
  385.                  sum := sum + node[i].weight;
  386.                end;
  387.  
  388.           divisor := ovflw + 1;
  389.  
  390.           { Ensure no non-zero values are lost }
  391.           increased := FALSE;
  392.           for i:=0 to numvals-1
  393.             do begin
  394.                  w := node[i].weight;
  395.                  if (w < divisor) and (w <> 0)
  396.                    then begin
  397.                           { Don't fail to provide a code if it's used at all }
  398.                           node[i].weight := divisor;
  399.                           increased := TRUE;
  400.                         end;
  401.                end;
  402.         until not(increased);
  403.  
  404.       { Scaling factor choosen, now scale }
  405.       if divisor > 1
  406.         then for i:=0 to numvals-1
  407.                do with node[i] do weight := trunc((weight / divisor)+0.5);
  408.     end;
  409. {.pa}
  410.   function buildenc(level, root: integer): integer; {returns error or null}
  411.  
  412.   { Recursive routine to walk the indicated subtree and level
  413.     and maintain the current path code in bstree. When a leaf
  414.     is found the entire code string and length are put into
  415.     the encoding table entry for the leaf's data value.
  416.  
  417.     Returns ERROR if codes are too long. }
  418.  
  419.     var l, r, return: integer;
  420.  
  421.     begin
  422.       return := null;
  423.       l := node[root].lchild;
  424.       r := node[root].rchild;
  425.  
  426.       if (l=NOCHILD) and (r=NOCHILD)
  427.         then begin  {have a leaf}
  428.                codelen[root] := level;
  429.                code[root] := tcode and ($FFFF shr (16 - level));
  430.                if level > 16
  431.                  then return := ERROR
  432.                  else return := NULL;
  433.              end
  434.         else begin
  435.                if l <> NOCHILD
  436.                  then begin  {Clear path bit and go deeper}
  437.                         tcode := tcode and not(1 shl level);
  438.                         if buildenc(level+1,l) = ERROR then return := ERROR;
  439.                       end;
  440.                if r <> NOCHILD
  441.                  then begin  {Set path bit and go deeper}
  442.                         tcode := tcode or (1 shl level);
  443.                         if buildenc(level+1,r)=ERROR then return := ERROR;
  444.                       end;
  445.              end;
  446.       buildenc := return;
  447.     end;
  448. {.pa}
  449.   procedure Build_Tree(var list: ValType; len: integer); {Huffman algorithm}
  450.  
  451.     var freenode: integer;         {next free node in tree}
  452.         lch, rch: integer;         {temporaries for left, right children}
  453.         i: integer;
  454.  
  455.     function Maximum(a, b: integer): integer;
  456.       begin
  457.         if a>b then Maximum:=a else Maximum:=b;
  458.       end;
  459.  
  460.     begin
  461.       write(', Building tree');
  462.       { Initialize index to next available (non-leaf) node.
  463.         Lower numbered nodes correspond to leaves (data values). }
  464.       freenode := NUMVALS;
  465.  
  466.       { Take from list two btrees with least weight and build an
  467.         interior node pointing to them.  This forms a new tree. }
  468.       while (len > 1)
  469.         do begin
  470.              lch := list[0]; { This one will be left child }
  471.  
  472.              { delete top (least) tree from the list of trees }
  473.              len := len - 1;
  474.              list[0] := list[len];
  475.              adjust(0, len - 1, list);
  476.  
  477.              { Take new top (least) tree. Reuse list slot later }
  478.              rch := list[0]; { This one will be right child }
  479.  
  480.              { Form new tree from the two least trees using a free node as root.
  481.                Put the new tree in the list. }
  482.              with node[freenode]
  483.               do begin;
  484.                    lchild := lch;
  485.                    rchild := rch;
  486.                    weight := node[lch].weight + node[rch].weight;
  487.                    tdepth := 1 + Maximum(node[lch].tdepth, node[rch].tdepth);
  488.                  end;
  489.              list[0] := freenode;       {put at top for now}
  490.              freenode := freenode + 1;  {next free node}
  491.              { reheap list to get least tree at top }
  492.              adjust(0, len - 1, list);
  493.            end;
  494.       dctreehd := list[0];   { head of final tree }
  495.     end;
  496. {.pa}
  497.   procedure Initialize_Huffman;
  498.  
  499.   { Initialize the Huffman translation. This requires reading the input file through any preceding translation
  500.     functions to get the frequency distribution of the various values. }
  501.  
  502.     var c     : integer;
  503.         b     : bytechar;
  504.         i     : integer;
  505.         btlist: ValType;   { list of intermediate binary trees }
  506.         listlen: integer;  { length of btlist }
  507.         ceiling: integer;  { limit for scaling }
  508.  
  509. { Heap and Adjust maintain a list of binary trees as a heap with the top indexing the binary tree on the list which
  510.   has the least weight or, in case of equal weights, least depth in its longest path. The depth part is not strictly
  511.   necessary, but tends to avoid long codes which might provoke rescaling. }
  512.  
  513.     procedure Heap(var list: ValType; l: integer);
  514.       var i, len: integer;
  515.       begin
  516.         len := (l - 2) div 2;
  517.         for i:=len downto 0 do adjust(i, l - 1, list);
  518.       end;
  519.  
  520.     procedure PrintFrequency;
  521.       var i, j: integer;
  522.       begin
  523.         j := 0;
  524.         for i:=0 to numvals-1
  525.           do if node[i].weight>0
  526.                then begin
  527.                       j := j + 1;
  528.                       writeln(lst,'node ',i:3,'  weight is ',node[i].weight:4);
  529.                     end;
  530.         writeln(lst);
  531.         writeln(lst,'Total node count is ',j);
  532.       end;
  533.  
  534.     procedure PrintList;
  535.       var i: integer;
  536.           str: string[10];
  537.       begin
  538.         writeln(', waiting');   readln(str);
  539.         for i:=0 to numvals-1
  540.           do begin
  541.                write('number ',i:3,'  length ',codelen[i]:2);
  542.                write('  weight ',node[i].weight:4);
  543.                if codelen[i]>0 then PrintBits(codelen[i], code[i]) else writeln;
  544.              end;
  545.       end;
  546.  
  547.   begin
  548.       write('Pass 1: Analysis');
  549.       crc := 0;   zero_tree;   state := NoHist;   EOFlag := false;
  550.  
  551.       repeat    { Build frequency info in tree }
  552.           b.c := getcnr;
  553.           c := b.b;
  554.           if EOFlag then c := SPEOF;
  555.           with node[c] do if weight < maxcount then weight := weight + 1;
  556.           if EOFlag then write(', End of file found');
  557.         until (EOFlag);
  558.       {PrintFrequency;}
  559.  
  560.       ceiling := MAXCOUNT;
  561.  
  562.       { Try to build encoding table. Fail if any code is > 16 bits long. }
  563.       repeat
  564.           if (ceiling <> MAXCOUNT) then write('*** rescaling ***, ');
  565.           scale(ceiling);
  566.           ceiling := ceiling div 2;  {in case we rescale again}
  567.  
  568.           listlen := 0;   {find length of list and build single nodes}
  569.           for i:=0 to numvals-1
  570.             do begin
  571.                  if node[i].weight > 0
  572.                    then begin
  573.                           node[i].tdepth := 0;
  574.                           btlist[listlen] := i;
  575.                           listlen := listlen + 1;
  576.                         end;
  577.                end;
  578.           heap(btlist, listlen-1);  { *** changed from listlen }
  579.           Build_Tree(btlist, listlen);
  580.           for i := 0 to NUMVALS-1 do codelen[i] := 0;
  581.         until (buildenc(0,dctreehd) <> ERROR);
  582.  
  583.       {PrintList;}
  584.       { Initialize encoding variables }
  585.       cbitsrem := 0;   curin := 0;
  586.     end;
  587. {.pa}
  588.   function gethuff: char; {returns byte values except for EOF}
  589.   { Get an encoded byte or EOF. Reads from specified stream AS NEEDED.
  590.  
  591.     There are two unsynchronized bit-byte relationships here:
  592.       The input stream bytes are converted to bit strings of various lengths via
  593.       the static variables named Cxxxxx.  These bit strings are concatenated without
  594.       padding to become the stream of encoded result bytes, which this function
  595.       returns one at a time. The EOF (end of file) is converted to SPEOF for
  596.       convenience and encoded like any other input value. True EOF is returned after
  597.       that. }
  598.  
  599.     var rbyte: integer;       {Result byte value}
  600.         need, take: integer;  {numbers of bits}
  601.         return: integer;
  602.         inbyte: bytechar;
  603.  
  604.     begin
  605.       rbyte := 0;
  606.       need := 8;        {build one byte per call}
  607.       return := ERROR;  {start off with an error}
  608.  
  609.       {Loop to build a byte of encoded data.  Initialization forces read the first time}
  610.       while return=ERROR
  611.         do begin
  612.              if cbitsrem >= need
  613.                then begin {Current code fullfills our needs}
  614.                       if need = 0
  615.                         then return := rbyte and $00FF
  616.                         else begin
  617.                                rbyte := rbyte or (ccode shl (8 - need)); {take what we need}
  618.                                ccode := ccode shr need;                  {and leave the rest}
  619.                                cbitsrem := cbitsrem - need;
  620.                                return := rbyte and $00FF;
  621.                              end;
  622.                     end
  623.                else begin
  624.                       if cbitsrem > 0
  625.                         then begin  {We need more than current code}
  626.                                rbyte := rbyte or (ccode shl (8 - need)); {take what there is}
  627.                                need := need - cbitsrem;
  628.                              end;
  629.                       if curin = SPEOF
  630.                         then begin
  631.                                cbitsrem := 0;
  632.                                if need=8
  633.                                  then begin                       {end of file}
  634.                                         done := true;
  635.                                         return := 0; {any valid char value}
  636.                                       end
  637.                                  else return := rbyte and $00FF;  {data first}
  638.                              end
  639.                         else begin
  640.                                inbyte.c:=getcnr;
  641.                                curin := inbyte.b;
  642.                                if EOFlag then curin := SPEOF;
  643.                                ccode := code[curin];
  644.                                cbitsrem := codelen[curin];
  645.                              end;
  646.                     end;
  647.            end;
  648.       gethuff := chr(return);
  649.     end;
  650. {.pa}
  651.   procedure squeeze;
  652.     var c: bytechar;
  653.     begin
  654.       writeln;   write('Pass 2: Squeezing');
  655.       reset(InFile,1);   rewrite(OutFile,1);   EOFlag := false;
  656.       Reset_InFile;  Reset_OutFile;
  657.       write(', header');   Write_Header;
  658.       write(', body');     state := NoHist;
  659.       done := false;   c.c := gethuff;  {prime while loop}
  660.       while not(done)
  661.         do begin
  662.              Out_write(c.b);
  663.              c.c := gethuff;
  664.            end;
  665.       Out_Flush;
  666.     end;
  667.  
  668. begin { Main }
  669.  
  670.   clrscr;   gotoxy(1,5);
  671.   writeln('File squeezer version ',version);
  672.   writeln;
  673.  
  674.   { get filename to process & convert to upper case}
  675.   write('Enter file to squeeze: ');   readln(InFileName);   writeln;
  676.   for i:=1 to length(InFileName) do InFileName[i] := upcase(InFileName[i]);
  677.  
  678.   { Find and change output file type }
  679.   start := 1; { skip leading blanks }
  680.   while (InFileName[start]=space) and (start <= length(InFileName)) do start := start + 1;
  681.   InFileName := copy(InFileName, start, length(InFileName)-start+1);
  682.   finish := pos('.',InFileName);
  683.   if finish=0
  684.     then OutFileName := InFileName + '.QQQ'
  685.     else begin
  686.            OutFileName := InFileName;
  687.            OutFileName[finish+2] := 'Q';
  688.          end;
  689.  
  690.   { open source file and check for existence }
  691.   assign(InFile,InFileName);   assign(OutFile,OutFileName);
  692.   {$I-}   reset(InFile,1);   {$I+}
  693.   if IOresult=0
  694.     then begin
  695.            Reset_InFile;
  696.            write('The file ',InFileName,' (',(longfilesize(InFile)):6:0);
  697.            writeln(' bytes) is being squeezed to ',OutFilename);
  698.            Initialize_Huffman;
  699.            squeeze;
  700.            writeln(', Done.');   close(InFile);   close(OutFile);
  701.          end
  702.     else writeln('Error -- input file doesn''t exist');
  703.  
  704. end.
  705.