{$C-}
program Unsqueeze;      { unsqueeze file from in_file to out_file }

{ This program unsqueezes a file which has been squeezed or compressed
 to  reduce  the space required to store it on disk.  The program was
 converted from the original  version  written  for  CP/M  in  the  C
 language.   This  program  can be used to unsqueeze files which have
 been downloaded from RCP/M systems where almost all files are  saved
 in this squeezed format.

 The  technique used is the Huffman encoding technique which converts
 the most common characters in the input file  to  a  compressed  bit
 stream  of  data.   This  program  unsqueezes such a Huffman encoded
 file.

 PUBLIC  DOMAIN  -  Feel  free  to  distribute  this program.  Do not
 distribute it by commercial means or make any charge for this pgm.

 Version 1.0  - 09/05/82  Scott Loftesness
 Version 1.1  - 01/06/83  Added capability to strip off parity bit if
                          output file is text. Ernie LeMay 71435,730
 Version 1.2  - 07/20/84  converted to Turbo Pascal. Steve Freeman
 Version 1.3  - 12/30/84  changed file I/O to run on CP/M as well as
                          MS-DOS.  Changed filetypes to 'file' and
                          used blockread/write for file I/O.
                          Jeff Duncan
}


const
   recognize  = $FF76;
   numvals    = 257;      { max tree size + 1 }
   speof      = 256;      { special end of file marker }
   dle: char  = #$90;
   buffersize = 128; (* 128 byte buffer *)

type
   tree       = array [0..255,0..1] of integer;
   hexstr     = string[4];

var
   debug : boolean;
   in_file, out_file: file;
   in_FN: string[30];
   dnode: tree;
   inchar, curin, filecksum, bpos, i, repct, numnodes: integer;
   c, lastchar: char;
   origfile: string[14];
   docfile, eofin, abort: boolean;
   abortM: string[50];
   infilebuffer : array[1..2048] of byte; (* allow for a 2k input buffer *)
   infilepointer : integer; (* pointer into buffer *)
   infilesize : integer;    (* input filesize for buffer loading *)
   inbuffersize : integer;  (* maximum count for buffer pointer *)
   outfilebuffer : array[1..128] of byte; (* output buffer will be minimum *)
   outfilepointer : integer; (* pointer into output buffer *)
   outbuffer_num : integer;  (* how many buffer-fulls used *)

{ iftext -- find out if output file is text and return true if so. EL }
function iftext : boolean;
 var answer: char;
 begin
   repeat
     write('Is the output file a text file?  ');
     read(kbd,answer);
     answer := upcase(answer);
   until (answer in ['Y','N']);
   writeln(answer);
   if answer='Y'
     then iftext:=true
     else iftext:=false;
 end;


function hex(num: integer): hexstr;
 var i, j: integer;
     h: string[16];
     str: hexstr;
 begin
   str := '0000';   h := '0123456789ABCDEF';   j := num;
   for i:=4 downto 1
     do begin
          str[i] := h[(j and 15)+1];
          j := j shr 4;
        end;
   hex := str;
 end;

function getc: integer;

 begin
   if (infilepointer > inbuffersize) and (not eof(in_file)) then
     begin  (* is input buffer empty and more data to follow *)
       if infilesize < 16 then (* less than 2048 bytes left? *)
         begin
           blockread(in_file, infilebuffer, infilesize); (* no get rest *)
           infilepointer := 1;
           inbuffersize := infilesize * 128;
         end
       else
         begin (* full 2048 left so get maximum *)
           blockread(in_file, infilebuffer, 16);
           inbuffersize := 2048;
           infilepointer := 1;
           infilesize := infilesize - 16;
         end;
     end;
   if not ((infilepointer > inbuffersize) and eof(in_file)) then
     begin  (* another character to read  available *)
       getc := infilebuffer[infilepointer];
       infilepointer := infilepointer + 1;
     end;
 end;

{ getw - get a word value from the input file }
function getw: integer;
   var in1,in2: byte;
 begin
   in1 := getc; (* use getc for these to simplify buffer manipulation *)
   in2 := getc;
   getw := ord(in1) + ord(in2) shl 8;
 end;


procedure initialize;
 var str: string[14];
 begin
   abort := false;     { no error conditions presently exist }
   repct:=0;
   bpos:=99;
   origfile:='';
   eofin:=false;
   clrscr;   gotoxy(1,5);   write('Enter the file to unsqueeze:');
   readln(in_FN);
   assign(in_file,in_FN);
   {$I-}
   reset(in_file);
   {$I+}
   if (IOresult = 0) then (* file is found *)
     begin
       inbuffersize := 0; (* dummy for first pass *)
       infilesize := filesize(in_file); (* filesize at initiate *)
       writeln('Input file ', in_fn,' is ',infilesize * 128,' bytes.');
       infilepointer := 1; (* point beyond buffer, so we get data on entry *)
       i := getw;
     end
   else
     i := 0;
   if (recognize <> i)
     then begin
            abort  := true;
            abortM := 'File is not a squeezed file'; (* could be not found also *)
            numnodes := -1;
          end
     else begin
            filecksum := getw;     { get checksum from chars 2 - 3 of file }
            repeat    { build original file name }
                inchar:=getc;
                if inchar <> 0
                  then origfile := origfile + chr(inchar);
              until inchar = 0;
            writeln('Original file name is ',origfile);
            write('Output to (return to default) ? ');
            readln(str);
            if length(str)=0 then
              str:=origfile;
            assign(out_file,str);
            rewrite(out_file);
            outfilepointer := 1; (* good idea to start at beginning of buffer *)
            outbuffer_num := 0; (* not neccessary *)
            numnodes:=ord(getw); { get the number of nodes in this files tree }
            if (numnodes<0) or (numnodes>=numvals)
              then begin
                     abort  := true;
                     abortM := 'File has invalid decode tree size';
                   end;
          end;
   if not(abort)
     then begin
            dnode[0,0]:= -(speof+1);
            dnode[0,1]:= -(speof+1);
            numnodes:=numnodes-1;
            for i:=0 to numnodes
              do begin
                   dnode[i,0]:=getw;
                   dnode[i,1]:=getw;
                 end;
            { following is for test }
            {for i:=0 to numnodes
              do writeln(lst,'#',i:3,' ',hex(dnode[i,0]),' ',hex(dnode[i,1]));}
          end;
 end;

procedure dochar(c: char;  text: boolean);
 begin
   if text then
     c:=chr(ord(c) and $7F); {strip off parity bit}
   outfilebuffer[outfilepointer] := ord(c); (* save data in output buffer *)
   outfilepointer := outfilepointer + 1; (* increment the pointer *)
   if outfilepointer > buffersize then
     begin (* we have a full buffer, lets write it out *)
       blockwrite(out_file,outfilebuffer,1);
       outfilepointer := 1; (* reset pinter *)
       outbuffer_num := outbuffer_num + 1;
     end;
 end;

function getuhuff: char;
var i: integer;
 begin
   i:=0;
   repeat
       bpos:=bpos+1;
       if bpos>7 then begin
                        curin := getc;
                        bpos:=0;
                      end
                 else curin := curin shr 1;
       i := ord(dnode[i,ord(curin and $0001)]);
     until (i<0);
   i := -(i+1);
   if i=speof
     then begin
            eofin:=true;
            getuhuff:=chr(26)
          end
     else getuhuff:=chr(i);
 end;

function getcr: char;
var c: char;
 begin
   if (repct>0)
     then begin
            repct:=repct-1;
            getcr:=lastchar;
          end
     else begin
            c:=getuhuff;
            if c<>dle
              then begin
                     getcr:=c;
                     lastchar:=c;
                   end
              else begin
                     repct:=ord(getuhuff);
                     if repct=0 then getcr:=dle
                                else begin
                                       repct:=repct-2;
                                       getcr:=lastchar;
                                     end;
                   end;
          end;
 end; {getcr}

begin { main }
 debug := true;
 initialize;
 if not(abort)
   then begin
          docfile := iftext;
          writeln(output,'Tree loaded sucessfully. Un-squeezing begins...');
          while not(eof(in_file)) or not(eofin)
            do begin
                 c:=getcr;
                 dochar(c,docfile);
               end;
          if docfile then (* to close the file we have to write the last
                             buffer.  If it's a text file we need to append
                             a ^Z to the buffer before writing.
                          *)
            if outfilepointer <= buffersize then
              outfilebuffer[outfilepointer] := ord(^Z);
          blockwrite(out_file,outfilebuffer, 1);
          close(out_file); (* write last buffer of data *)
        end
   else writeln('Error -- ',AbortM);
 close(in_file);
end.
 then