home *** CD-ROM | disk | FTP | other *** search
/ Shareware Overload / ShartewareOverload.cdr / utils / ziptv20.zip / ZIPTV.PAS < prev   
Pascal/Delphi Source File  |  1989-09-09  |  36KB  |  1,505 lines

  1.  
  2. (*
  3.  * Copyright 1987, 1989 Samuel H. Smith;  All rights reserved
  4.  *
  5.  * This is a component of the ProDoor System.
  6.  * Do not distribute modified versions without my permission.
  7.  * Do not remove or alter this notice or any other copyright notice.
  8.  * If you use this in your own program you must distribute source code.
  9.  * Do not use any of this in a commercial product.
  10.  *
  11.  *)
  12.  
  13. (*
  14.  * ZipTV - zipfile text view utility/door
  15.  *
  16.  *)
  17.  
  18. {$I prodef.inc}
  19. {$M 5000,0,0} {minstack,minheap,maxheap}
  20. {$D+}    {Global debug information}
  21. {$L+}    {Local debug information}
  22. { $r+,s+}
  23.  
  24. program ZipTV;
  25.  
  26. Uses
  27.    Dos, DosMem, MiniCrt, Mdosio, Tools, CInput;
  28.  
  29. const
  30.    version = 'ZipTV:  Zipfile Text Viewer v2.0 of 09-09-89;  (C) 1989 S.H.Smith';
  31.  
  32.  
  33. (* ----------------------------------------------------------- *)
  34. (*
  35.  * ZIPfile layout declarations
  36.  *
  37.  *)
  38.  
  39. type
  40.    signature_type = longint;
  41.  
  42. const
  43.    local_file_header_signature = $04034b50;
  44.  
  45. type
  46.    local_file_header = record
  47.       version_needed_to_extract:    word;
  48.       general_purpose_bit_flag:     word;
  49.       compression_method:           word;
  50.       last_mod_file_time:           word;
  51.       last_mod_file_date:           word;
  52.       crc32:                        longint;
  53.       compressed_size:              longint;
  54.       uncompressed_size:            longint;
  55.       filename_length:              word;
  56.       extra_field_length:           word;
  57.    end;
  58.  
  59. const
  60.    central_file_header_signature = $02014b50;
  61.  
  62. type
  63.    central_directory_file_header = record
  64.       version_made_by:                 word;
  65.       version_needed_to_extract:       word;
  66.       general_purpose_bit_flag:        word;
  67.       compression_method:              word;
  68.       last_mod_file_time:              word;
  69.       last_mod_file_date:              word;
  70.       crc32:                           longint;
  71.       compressed_size:                 longint;
  72.       uncompressed_size:               longint;
  73.       filename_length:                 word;
  74.       extra_field_length:              word;
  75.       file_comment_length:             word;
  76.       disk_number_start:               word;
  77.       internal_file_attributes:        word;
  78.       external_file_attributes:        longint;
  79.       relative_offset_local_header:    longint;
  80.    end;
  81.  
  82. const
  83.    end_central_dir_signature = $06054b50;
  84.  
  85. type
  86.    end_central_dir_record = record
  87.       number_this_disk:                         word;
  88.       number_disk_with_start_central_directory: word;
  89.       total_entries_central_dir_on_this_disk:   word;
  90.       total_entries_central_dir:                word;
  91.       size_central_directory:                   longint;
  92.       offset_start_central_directory:           longint;
  93.       zipfile_comment_length:                   word;
  94.    end;
  95.  
  96. const
  97.    compression_methods: array[0..7] of string[8] =
  98.       (' Stored ',
  99.        ' Shrunk ',
  100.        'Reduce-1', 'Reduce-2', 'Reduce-3', 'Reduce-4',
  101.        'Imploded',
  102.        '    ?   ');
  103.  
  104.  
  105. (* ----------------------------------------------------------- *)
  106. (*
  107.  * input file variables
  108.  *
  109.  *)
  110.  
  111. const
  112.    uinbufsize = 512;    {input buffer size}
  113.  
  114. var
  115.    zipeof:     boolean;
  116.  
  117.    csize:      longint;
  118.    cusize:     longint;
  119.    cmethod:    integer;
  120.    cflags:     word;
  121.  
  122.    inbuf:      array[1..uinbufsize] of byte;
  123.    inpos:      integer;
  124.    incnt:      integer;
  125.    pc:         byte;
  126.    pcbits:     byte;
  127.    pcbitv:     byte;
  128.    zipfd:      dos_handle;
  129.    zipfn:      dos_filename;
  130.  
  131.  
  132.  
  133. (* ----------------------------------------------------------- *)
  134. (*
  135.  * output stream variables
  136.  *
  137.  *)
  138.  
  139. const
  140.    hsize =     8192;    {must be 8192 for 13 bit shrinking}
  141.  
  142.    max_binary = 50;     {non-printing count before binary file trigger}
  143.    max_linelen = 200;   {line length before binary file triggered}
  144.  
  145. var
  146.    uoutbuf:             string[max_linelen];    {disp line buffer}
  147.    binary_count:        integer;                {non-text chars so far}
  148.  
  149.    outbuf:              array[0..hsize] of byte; {must be >= 8192 for look-back}
  150.    outpos:              longint;                 {absolute position in outfile}
  151.  
  152.  
  153. (* ----------------------------------------------------------- *)
  154. (*
  155.  * other working storage
  156.  *
  157.  *)
  158.  
  159. var
  160.    expand_files:        boolean;
  161.    header_present:      boolean;
  162.    default_pattern:     string20;
  163.    pattern:             string20;
  164.    action:              string20;
  165.  
  166.  
  167.  
  168. (* ----------------------------------------------------
  169.  *
  170.  *  Zipfile input/output handlers
  171.  *
  172.  *)
  173.  
  174. procedure skip_rest;
  175. begin
  176.    dos_lseek(zipfd,csize-incnt,seek_cur);
  177.    zipeof := true;
  178.    csize := 0;
  179.    incnt := 0;
  180. end;
  181.  
  182. procedure skip_csize;
  183. begin
  184.    incnt := 0;
  185.    skip_rest;
  186. end;
  187.  
  188.  
  189. (* ------------------------------------------------------------- *)
  190. procedure ReadByte(var x: byte);
  191. begin
  192.    if incnt = 0 then
  193.    begin
  194.       if csize = 0 then
  195.       begin
  196.          zipeof := true;
  197.          exit;
  198.       end;
  199.  
  200.       inpos := sizeof(inbuf);
  201.       if inpos > csize then
  202.          inpos := csize;
  203.       incnt := dos_read(zipfd,inbuf,inpos);
  204.  
  205.       inpos := 1;
  206.       dec(csize,incnt);
  207.    end;
  208.  
  209.    x := inbuf[inpos];
  210.    inc(inpos);
  211.    dec(incnt);
  212. end;
  213.  
  214.  
  215. (* ------------------------------------------------------------- *)
  216. procedure ReadBits(bits: integer; var x: integer);
  217.    {read the specified number of bits}
  218. var
  219.    bit:     integer;
  220.    bitv:    integer;
  221.  
  222. begin
  223.  
  224. (****
  225. write('readbits n=',bits,' b=');
  226. ****)
  227.  
  228.    x := 0;
  229.    bitv := 1;
  230.  
  231.    for bit := 0 to bits-1 do
  232.    begin
  233.  
  234.       if pcbits > 0 then
  235.       begin
  236.          dec(pcbits);
  237.          pcbitv := pcbitv shl 1;
  238.       end
  239.       else
  240.  
  241.       begin
  242.          ReadByte(pc);
  243.          pcbits := 7;
  244.          pcbitv := 1;
  245.       end;
  246.  
  247.       if (pc and pcbitv) <> 0 then
  248.          x := x or bitv;
  249.  
  250.       bitv := bitv shl 1;
  251.    end;
  252.  
  253. (****
  254. writeln(' -> ',x,' = ',binary(x));
  255. *****)
  256.  
  257. end;
  258.  
  259.  
  260. (* ---------------------------------------------------------- *)
  261. procedure get_string(len: integer; var s: string);
  262. var
  263.    n: integer;
  264. begin
  265.    if len <= 255 then
  266.       n := dos_read(zipfd,s[1],len)
  267.    else
  268.    begin
  269.       n := dos_read(zipfd,s[1],255);
  270.       dos_lseek(zipfd,len-255,seek_cur);
  271.       len := 255;
  272.    end;
  273.  
  274.    s[0] := chr(len);
  275. end;
  276.  
  277.  
  278. (* ------------------------------------------------------------- *)
  279. procedure OutByte (c: integer);
  280.    (* output each character from archive to screen *)
  281.  
  282.    procedure flushbuf;
  283.    begin
  284.       disp(uoutbuf);
  285.       uoutbuf := '';
  286.    end;
  287.  
  288.    procedure addchar;
  289.    begin
  290.       inc(uoutbuf[0]);
  291.       uoutbuf[length(uoutbuf)] := chr(c);
  292.    end;
  293.  
  294.    procedure not_text;
  295.    begin
  296.       newline;
  297.       displn('This is not a text file!');
  298.       skip_rest;
  299.    end;
  300.    
  301. begin
  302.    outbuf[outpos mod sizeof(outbuf)] := c;
  303.    inc(outpos);
  304.  
  305. (********
  306. if debug then
  307. begin
  308. if c = 13 then
  309. else
  310. if c = 10 then
  311. begin
  312.    if nomore then
  313.       skip_rest
  314.    else
  315.       newline;
  316. end
  317. else
  318.    write(chr(c));
  319. writeln(' [outbyte c=',c:3,' outpos=',outpos-1:5,']');
  320. if keypressed and (readkey=#27) then halt;
  321. exit;
  322. end;
  323. ********)
  324.  
  325.    case c of
  326.    13:  begin
  327.            if linenum < 1000 then
  328.            begin
  329.               flushbuf;
  330.               newline;
  331.            end;
  332.  
  333.            if nomore or dump_user then
  334.               skip_rest;
  335.         end;
  336.  
  337.    10: ;              
  338.  
  339.    26: begin
  340.           flushbuf;
  341.           skip_rest;         {jump to nomore mode on ^z}
  342.        end;
  343.  
  344.    8,9,32..255:
  345.        begin
  346.           if length(uoutbuf) >= max_linelen then
  347.           begin
  348.              flushbuf;
  349.              if csize > 10 then
  350.                 not_text;
  351.           end;
  352.  
  353.           if linenum < 1000 then   {stop display on nomore}
  354.              addchar;
  355.        end;
  356.  
  357.    else
  358.       begin
  359.          if binary_count < max_binary then
  360.             inc(binary_count)
  361.          else
  362.          if csize > 10 then
  363.             not_text;
  364.       end;
  365.    end;
  366.  
  367. end;
  368.  
  369.  
  370. (* ------------------------------------------------------------- *)
  371. (*
  372.  * The Reducing algorithm is actually a combination of two
  373.  * distinct algorithms.  The first algorithm compresses repeated
  374.  * byte sequences, and the second algorithm takes the compressed
  375.  * stream from the first algorithm and applies a probabilistic
  376.  * compression method.
  377.  *
  378.  *)
  379.  
  380. procedure unReduce;
  381.    {expand probablisticly reduced data}
  382.  
  383.    type
  384.       Sarray = array[0..255] of string[64];
  385.  
  386.    var
  387.       factor:     integer;
  388.       followers:  ^Sarray;
  389.       ExState:    integer;
  390.       C:          integer;
  391.       V:          integer;
  392.       Len:        integer;
  393.  
  394.    const
  395.       Lmask:   array[1..4] of integer = ($7f,$3f,$1f,$0f);
  396.       Fcase:   array[1..4] of integer = (127, 63, 31, 15);
  397.       Dshift:  array[1..4] of integer = (7,6,5,4);
  398.       Dand:    array[1..4] of integer = ($01,$03,$07,$0f);
  399.  
  400.  
  401.    procedure Expand(c: byte);
  402.    const
  403.       DLE = 144;
  404.    var
  405.       op:   longint;
  406.       i:    integer;
  407.  
  408.    begin
  409.  
  410.       case ExState of
  411.            0:  if C <> DLE then
  412.                    OutByte(C)
  413.                else
  414.                    ExState := 1;
  415.  
  416.            1:  if C <> 0 then
  417.                begin
  418.                    V := C;
  419.                    Len := V and Lmask[factor];
  420.                    if Len = Fcase[factor] then
  421.                      ExState := 2
  422.                    else
  423.                      ExState := 3;
  424.                end
  425.                else
  426.                begin
  427.                    OutByte(DLE);
  428.                    ExState := 0;
  429.                end;
  430.  
  431.            2:  begin
  432.                   inc(Len,C);
  433.                   ExState := 3;
  434.                end;
  435.  
  436.            3:  begin
  437.                   op := outpos - C - 1 - ((V shr Dshift[factor]) and
  438.                                           Dand[factor]) * 256;
  439.  
  440.                   for i := 0 to Len+2 do
  441.                   begin
  442.                      if op < 0 then
  443.                         OutByte(0)
  444.                      else
  445.                         OutByte(outbuf[op mod sizeof(outbuf)]);
  446.                      inc(op);
  447.                   end;
  448.  
  449.                   ExState := 0;
  450.                end;
  451.       end;
  452.    end;
  453.  
  454.  
  455.    procedure LoadFollowers;
  456.    var
  457.       x: integer;
  458.       i: integer;
  459.       b: integer;
  460.    begin
  461.       for x := 255 downto 0 do
  462.       begin
  463.          ReadBits(6,b);
  464.          followers^[x][0] := chr(b);
  465.  
  466.          for i := 1 to length(followers^[x]) do
  467.          begin
  468.             ReadBits(8,b);
  469.             followers^[x][i] := chr(b);
  470.          end;
  471.       end;
  472.    end;
  473.  
  474.  
  475.    function B(x: byte): word;
  476.       {number of bits needed to encode the specified number}
  477.    begin
  478.       case x-1 of
  479.          0..1:    B := 1;
  480.          2..3:    B := 2;
  481.          4..7:    B := 3;
  482.          8..15:   B := 4;
  483.         16..31:   B := 5;
  484.         32..63:   B := 6;
  485.         64..127:  B := 7;
  486.       else        B := 8;
  487.       end;
  488.    end;
  489.  
  490.  
  491. (* ----------------------------------------------------------- *)
  492. var
  493.    lchar:   integer;
  494.    lout:    integer;
  495.    I:       integer;
  496.    mem:     longint;
  497.  
  498. begin
  499.    mem := (sizeof(followers^)+100) - dos_maxavail;
  500.    if mem > 0 then
  501.    begin
  502.       displn(ltoa(mem)+' more bytes of RAM needed to UnReduce!');
  503.       skip_csize;
  504.       exit;
  505.    end;
  506.  
  507.    factor := cmethod - 1;
  508.    if (factor < 1) or (factor > 4) then
  509.    begin
  510.       skip_csize;
  511.       exit;
  512.    end;
  513.  
  514.    dos_getmem(followers,sizeof(followers^));
  515.    ExState := 0;
  516.    LoadFollowers;
  517.    lchar := 0;
  518.  
  519.    while (not zipeof) and (outpos < cusize) and (not dump_user) do
  520.    begin
  521.  
  522.       if followers^[lchar] = '' then
  523.          ReadBits( 8,lout )
  524.       else
  525.  
  526.       begin
  527.          ReadBits(1,lout);
  528.          if lout <> 0 then
  529.             ReadBits( 8,lout )
  530.          else
  531.          begin
  532.             ReadBits( B(length(followers^[lchar])), I );
  533.             lout := ord( followers^[lchar][I+1] );
  534.          end;
  535.       end;
  536.  
  537.       Expand( lout );
  538.       lchar := lout;
  539.    end;
  540.  
  541.    dos_freemem(followers);
  542. end;
  543.  
  544.  
  545.  
  546. (* ------------------------------------------------------------- *)
  547. (*
  548.  * UnShrinking
  549.  * -----------
  550.  *
  551.  * Shrinking is a Dynamic Ziv-Lempel-Welch compression algorithm
  552.  * with partial clearing.  The initial code size is 9 bits, and
  553.  * the maximum code size is 13 bits.  Shrinking differs from
  554.  * conventional Dynamic Ziv-lempel-Welch implementations in several
  555.  * respects:
  556.  *
  557.  * 1)  The code size is controlled by the compressor, and is not
  558.  *     automatically increased when codes larger than the current
  559.  *     code size are created (but not necessarily used).  When
  560.  *     the decompressor encounters the code sequence 256
  561.  *     (decimal) followed by 1, it should increase the code size
  562.  *     read from the input stream to the next bit size.  No
  563.  *     blocking of the codes is performed, so the next code at
  564.  *     the increased size should be read from the input stream
  565.  *     immediately after where the previous code at the smaller
  566.  *     bit size was read.  Again, the decompressor should not
  567.  *     increase the code size used until the sequence 256,1 is
  568.  *     encountered.
  569.  *
  570.  * 2)  When the table becomes full, total clearing is not
  571.  *     performed.  Rather, when the compresser emits the code
  572.  *     sequence 256,2 (decimal), the decompressor should clear
  573.  *     all leaf nodes from the Ziv-Lempel tree, and continue to
  574.  *     use the current code size.  The nodes that are cleared
  575.  *     from the Ziv-Lempel tree are then re-used, with the lowest
  576.  *     code value re-used first, and the highest code value
  577.  *     re-used last.  The compressor can emit the sequence 256,2
  578.  *     at any time.
  579.  *
  580.  *)
  581.  
  582. procedure unShrink;
  583.  
  584. const
  585.    max_bits =  13;
  586.    init_bits = 9;
  587.    first_ent = 257;
  588.    clear =     256;
  589.    
  590. type
  591.    hsize_array_integer = array[0..hsize] of integer;
  592.    hsize_array_byte    = array[0..hsize] of byte;
  593.  
  594. var
  595.    cbits:      integer;
  596.    maxcode:    integer;
  597.    free_ent:   integer;
  598.    maxcodemax: integer;
  599.    offset:     integer;
  600.    sizex:      integer;
  601.    prefix_of:  ^hsize_array_integer;
  602.    suffix_of:  ^hsize_array_byte;
  603.    stack:      hsize_array_byte absolute outbuf;
  604.    stackp:     integer;
  605.    finchar:    integer;
  606.    code:       integer;
  607.    oldcode:    integer;
  608.    incode:     integer;
  609.  
  610.  
  611.    (* ------------------------------------------------------------- *)
  612.    procedure partial_clear;
  613.    var
  614.       pr:   integer;
  615.       cd:   integer;
  616.  
  617.    begin
  618.       {mark all nodes as potentially unused}
  619.       for cd := first_ent to free_ent-1 do
  620.          word(prefix_of^[cd]) := prefix_of^[cd] or $8000;
  621.  
  622.  
  623.       {unmark those that are used by other nodes}
  624.       for cd := first_ent to free_ent-1 do
  625.       begin
  626.          pr := prefix_of^[cd] and $7fff;    {reference to another node?}
  627.          if pr >= first_ent then            {flag node as referenced}
  628.             prefix_of^[pr] := prefix_of^[pr] and $7fff;
  629.       end;
  630.  
  631.  
  632.       {clear the ones that are still marked}
  633.       for cd := first_ent to free_ent-1 do
  634.          if (prefix_of^[cd] and $8000) <> 0 then
  635.             prefix_of^[cd] := -1;
  636.  
  637.  
  638.       {find first cleared node as next free_ent}
  639.       free_ent := first_ent;
  640.       while (free_ent < maxcodemax) and (prefix_of^[free_ent] <> -1) do
  641.          inc(free_ent);
  642.    end;
  643.  
  644.  
  645.  
  646. (* ------------------------------------------------------------- *)
  647. var
  648.    mem:  longint;
  649. begin
  650.    mem := (sizeof(prefix_of^)+sizeof(suffix_of^)+ 100) - dos_maxavail;
  651.  
  652.    if mem > 0 then
  653.    begin
  654.       displn(ltoa(mem)+' more bytes of RAM needed to UnShrink!');
  655.       skip_csize;
  656.       exit;
  657.    end;
  658.  
  659.  
  660.    {allocate heap storage}
  661.    dos_getmem(prefix_of,sizeof(prefix_of^));
  662.    dos_getmem(suffix_of,sizeof(suffix_of^));
  663.  
  664.  
  665.    {decompress the file}
  666.    maxcodemax := 1 shl max_bits;
  667.    cbits := init_bits;
  668.    maxcode := (1 shl cbits)- 1;
  669.    free_ent := first_ent;
  670.    offset := 0;
  671.    sizex := 0;
  672.  
  673.    fillchar(prefix_of^,sizeof(prefix_of^),$FF);
  674.    for code := 255 downto 0 do
  675.    begin
  676.       prefix_of^[code] := 0;
  677.       suffix_of^[code] := code;
  678.    end;
  679.  
  680.    ReadBits(cbits,oldcode);
  681.    finchar := oldcode;
  682.    if zipeof then
  683.       exit;
  684.  
  685.    OutByte(finchar);
  686.  
  687.    stackp := 0;
  688.  
  689.    while (not zipeof) and (not dump_user) do
  690.    begin
  691.       ReadBits(cbits,code);
  692.  
  693.       while code = clear do
  694.       begin
  695.          ReadBits(cbits,code);
  696.  
  697.          case code of
  698.             1: begin
  699.                   inc(cbits);
  700.                   if cbits = max_bits then
  701.                      maxcode := maxcodemax
  702.                   else
  703.                      maxcode := (1 shl cbits) - 1;
  704.                end;
  705.  
  706.             2: partial_clear;
  707.          end;
  708.  
  709.          ReadBits(cbits,code);
  710.       end;
  711.  
  712.  
  713.       {special case for KwKwK string}
  714.       incode := code;
  715.       if prefix_of^[code] = -1 then
  716.       begin
  717.          stack[stackp] := finchar;
  718.          inc(stackp);
  719.          code := oldcode;
  720.       end;
  721.  
  722.  
  723.       {generate output characters in reverse order}
  724.       while (code >= first_ent) do
  725.       begin
  726.          stack[stackp] := suffix_of^[code];
  727.          inc(stackp);
  728.          code := prefix_of^[code];
  729.       end;
  730.  
  731.       finchar := suffix_of^[code];
  732.       stack[stackp] := finchar;
  733.       inc(stackp);
  734.  
  735.  
  736.       {and put them out in forward order}
  737.       while (stackp > 0) do
  738.       begin
  739.          outpos := stackp; {required to preserve shared buffer/stack}
  740.          dec(stackp);
  741.          OutByte(stack[stackp]);
  742.       end;
  743.  
  744.  
  745.       {generate new entry}
  746.       code := free_ent;
  747.       if code < maxcodemax then
  748.       begin
  749.          prefix_of^[code] := oldcode;  {previous code}
  750.          suffix_of^[code] := finchar;  {final character from this code}
  751.          while (free_ent < maxcodemax) and (prefix_of^[free_ent] <> -1) do
  752.             inc(free_ent);
  753.       end;
  754.  
  755.  
  756.       {remember previous code}
  757.       oldcode := incode;
  758.    end;
  759.  
  760.  
  761.    {release heap storage}
  762.    dos_freemem(suffix_of);
  763.    dos_freemem(prefix_of);
  764. end;
  765.  
  766.  
  767. (* ------------------------------------------------------------- *)
  768. (*
  769.  * Imploding
  770.  * ---------
  771.  *
  772.  * The Imploding algorithm is actually a combination of two distinct
  773.  * algorithms.  The first algorithm compresses repeated byte sequences
  774.  * using a sliding dictionary.  The second algorithm is used to compress
  775.  * the encoding of the sliding dictionary ouput, using multiple
  776.  * Shannon-Fano trees.
  777.  *
  778.  *)
  779.  
  780. procedure unImplode;
  781.    {expand imploded data}
  782.  
  783.    const
  784.       maxSF = 256;
  785.  
  786.    type
  787.       sf_entry = record
  788.                     Code:       word;
  789.                     Value:      byte;
  790.                     BitLength:  byte;
  791.                  end;
  792.  
  793.       sf_tree = record  {a shannon-fano tree}
  794.          entry:         array[0..maxSF] of sf_entry;
  795.          entries:       integer;
  796.          MaxLength:     integer;
  797.       end;
  798.  
  799.       sf_treep = ^sf_tree;
  800.  
  801.    var
  802.       lit_tree:               sf_treep;
  803.       length_tree:            sf_treep;
  804.       distance_tree:          sf_treep;
  805.       lit_tree_present:       boolean;
  806.       eightK_dictionary:      boolean;
  807.       minimum_match_length:   integer;
  808.       dict_bits:              integer;
  809.  
  810.  
  811.    (* ----------------------------------------------------------- *)
  812.    procedure LoadTree(var tree: sf_treep;
  813.                       treesize: integer);
  814.       {allocate and load a shannon-fano tree from the compressed file}
  815.  
  816.       procedure SortLengths;
  817.          {Sort the Bit Lengths in ascending order, while retaining the order
  818.           of the original lengths stored in the file}
  819.       var
  820.          x:       integer;
  821.          gap:     integer;
  822.          t:       sf_entry;
  823.          noswaps: boolean;
  824.          a,b:     word;
  825.  
  826.       begin
  827.          gap := treesize div 2;
  828.  
  829.          with tree^ do
  830.          repeat
  831.             repeat
  832.                noswaps := true;
  833.                for x := 0 to (treesize-1)-gap do
  834.                begin
  835.                   a := entry[x].BitLength;
  836.                   b := entry[x+gap].BitLength;
  837.                   if (a > b) or
  838.                      ((a = b) and (entry[x].Value > entry[x+gap].Value)) then
  839.                   begin
  840.                      t := entry[x];
  841.                      entry[x] := entry[x+gap];
  842.                      entry[x+gap] := t;
  843.                      noswaps := false;
  844.                   end;
  845.                end;
  846.             until noswaps;
  847.  
  848.             gap := gap div 2;
  849.          until gap < 1;
  850.       end;
  851.  
  852.  
  853.       procedure ReadLengths;
  854.       var
  855.          treeBytes:  integer;
  856.          i:          integer;
  857.          num,len:    integer;
  858.       begin
  859.          {get number of bytes in compressed tree}
  860.          ReadBits(8,treeBytes);
  861.          inc(treeBytes);
  862.          i := 0;
  863.          with tree^ do
  864.          begin
  865.             MaxLength := 0;
  866.  
  867.             {High 4 bits: Number of values at this bit length + 1. (1 - 16)
  868.              Low  4 bits: Bit Length needed to represent value + 1. (1 - 16)}
  869.             while treeBytes > 0 do
  870.             begin
  871.                ReadBits(4,len);  inc(len);
  872.                ReadBits(4,num);  inc(num);
  873.  
  874.                while num > 0 do
  875.                with entry[i] do
  876.                begin
  877.                   if len > MaxLength then
  878.                      MaxLength := len;
  879.                   BitLength := len;
  880.                   Value := i;
  881.                   inc(i);
  882.                   dec(num);
  883.                end;
  884.  
  885.                dec(treeBytes);
  886.             end;
  887.          end;
  888.       end;
  889.  
  890.       procedure GenerateTrees;
  891.          {Generate the Shannon-Fano trees}
  892.       var
  893.          Code:          word;
  894.          CodeIncrement: integer;
  895.          LastBitLength: integer;
  896.          i:             integer;
  897.       begin
  898.          Code := 0;
  899.          CodeIncrement := 0;
  900.          LastBitLength := 0;
  901.  
  902.          i := treesize - 1;   {either 255 or 63}
  903.          with tree^ do
  904.          while i >= 0 do
  905.          begin
  906.             inc(Code,CodeIncrement);
  907.             if entry[i].BitLength <> LastBitLength then
  908.             begin
  909.                LastBitLength := entry[i].BitLength;
  910.                CodeIncrement := 1 shl (16 - LastBitLength);
  911.             end;
  912.  
  913.             entry[i].Code := Code;
  914.             dec(i);
  915.          end;
  916.       end;
  917.  
  918.       procedure ReverseBits;
  919.          {Reverse the order of all the bits in the above ShannonCode[]
  920.           vector, so that the most significant bit becomes the least
  921.           significant bit. For example, the value 0x1234 (hex) would become
  922.           0x2C48 (hex).}
  923.       var
  924.          i:    integer;
  925.          mask: word;
  926.          revb: word;
  927.          v:    word;
  928.          o:    word;
  929.          b:    integer;
  930.  
  931.       begin
  932.          for i := 0 to treesize-1 do
  933.          begin
  934.             {get original code}
  935.             o := tree^.entry[i].Code;
  936.  
  937.             {reverse each bit}
  938.             mask := $0001;
  939.             revb := $8000;
  940.             v := 0;
  941.             for b := 0 to 15 do
  942.             begin
  943.                {if bit set in mask, then substitute reversed bit}
  944.                if (o and mask) <> 0 then
  945.                   v := v or revb;
  946.  
  947.                {advance to next bit}
  948.                revb := revb shr 1;
  949.                mask := mask shl 1;
  950.             end;
  951.  
  952.             {store reversed bits}
  953.             tree^.entry[i].Code := v;
  954.          end;
  955.       end;
  956.  
  957.    begin
  958.       dos_getmem(tree,sizeof(tree^));
  959.       tree^.entries := treesize;
  960.       ReadLengths;
  961.       SortLengths;
  962.       GenerateTrees;
  963.       ReverseBits;
  964.    end;
  965.  
  966.  
  967.    (* ----------------------------------------------------------- *)
  968.    procedure LoadTrees;
  969.    begin
  970.       eightK_dictionary := (cflags and $02) <> 0; {bit 1}
  971.       lit_tree_present := (cflags and $04) <> 0; {bit 2}
  972.  
  973.       if eightK_dictionary then
  974.          dict_bits := 7
  975.       else
  976.          dict_bits := 6;
  977.  
  978.       if lit_tree_present then
  979.       begin
  980.          minimum_match_length := 3;
  981.          LoadTree(lit_tree,256);
  982.       end
  983.       else
  984.          minimum_match_length := 2;
  985.  
  986.       LoadTree(length_tree,64);
  987.       LoadTree(distance_tree,64);
  988.    end;
  989.  
  990.  
  991.    (* ----------------------------------------------------------- *)
  992.    procedure ReadTree(tree: sf_treep;
  993.                       var dest: integer);
  994.       {read next byte using a shannon-fano tree}
  995.    var
  996.       bits: integer;
  997.       cv:   word;
  998.       b:    integer;
  999.       cur:  integer;
  1000.  
  1001.    begin
  1002.       bits := 0;
  1003.       cv := 0;
  1004.       cur := 0;
  1005.       dest := -1; {in case of error}
  1006.  
  1007.       with tree^ do
  1008.       while true do
  1009.       begin
  1010.          ReadBits(1,b);
  1011.          cv := cv or (b shl bits);
  1012.          inc(bits);
  1013.  
  1014.          while entry[cur].BitLength < bits do
  1015.          begin
  1016.             inc(cur);
  1017.             if cur >= entries then
  1018.                exit;
  1019.          end;
  1020.  
  1021.          while entry[cur].BitLength = bits do
  1022.          begin
  1023.             if entry[cur].Code = cv then
  1024.             begin
  1025.                dest := entry[cur].Value;
  1026.                exit;
  1027.             end;
  1028.  
  1029.             inc(cur);
  1030.             if cur >= entries then
  1031.                exit;
  1032.          end;
  1033.       end;
  1034.  
  1035.    end;
  1036.  
  1037.  
  1038. (* ----------------------------------------------------------- *)
  1039. var
  1040.    lout:       integer;
  1041.    mem:        longint;
  1042.    op:         longint;
  1043.    Length:     integer;
  1044.    Distance:   integer;
  1045.    i:          integer;
  1046.  
  1047. begin
  1048.    mem := (sizeof(sf_tree)*3+100) - dos_maxavail;
  1049.    if mem > 0 then
  1050.    begin
  1051.       displn(ltoa(mem)+' more bytes of RAM needed to UnImplode!');
  1052.       skip_csize;
  1053.       exit;
  1054.    end;
  1055.  
  1056.    LoadTrees;
  1057.  
  1058.    while (not zipeof) and (outpos < cusize) and (not dump_user) do
  1059.    begin
  1060.       ReadBits(1,lout);
  1061.  
  1062.       if lout <> 0 then    {encoded data is literal data}
  1063.       begin
  1064.          if lit_tree_present then
  1065.             ReadTree(lit_tree,lout)   {use Literal Shannon-Fano tree}
  1066.          else
  1067.             ReadBits(8,lout);
  1068.  
  1069.          OutByte(lout);
  1070.       end
  1071.       else
  1072.  
  1073.       begin          {encoded data is sliding dictionary match}
  1074.          readBits(dict_bits,lout);
  1075.          Distance := lout;
  1076.  
  1077.          ReadTree(distance_tree,lout);
  1078.          Distance := Distance or (lout shl dict_bits);
  1079.          {using the Distance Shannon-Fano tree, read and decode the
  1080.             upper 6 bits of the Distance value}
  1081.  
  1082.          ReadTree(length_tree,Length);
  1083.          {using the Length Shannon-Fano tree, read and decode the Length value}
  1084.  
  1085.          inc(Length,Minimum_Match_Length);
  1086.          if Length = (63 + Minimum_Match_Length) then
  1087.          begin
  1088.             ReadBits(8,lout);
  1089.             inc(Length,lout);
  1090.          end;
  1091.  
  1092.          {move backwards Distance+1 bytes in the output stream, and copy
  1093.           Length characters from this position to the output stream.
  1094.           (if this position is before the start of the output stream,
  1095.           then assume that all the data before the start of the output
  1096.           stream is filled with zeros)}
  1097.  
  1098.          op := outpos - Distance - 1;
  1099.          for i := 1 to Length do
  1100.          begin
  1101.             if op < 0 then
  1102.                OutByte(0)
  1103.             else
  1104.                OutByte(outbuf[op mod sizeof(outbuf)]);
  1105.             inc(op);
  1106.          end;
  1107.       end;
  1108.    end;
  1109.  
  1110.    if lit_tree_present then
  1111.       dos_freemem(lit_tree);
  1112.    dos_freemem(distance_tree);
  1113.    dos_freemem(length_tree);
  1114. end;
  1115.  
  1116.  
  1117.  
  1118. (* ---------------------------------------------------------- *)
  1119. (*
  1120.  * This procedure displays the text contents of a specified archive
  1121.  * file.  The filename must be fully specified and verified.
  1122.  *
  1123.  *)
  1124. procedure viewfile;
  1125. var
  1126.    b: byte;
  1127.  
  1128. begin
  1129.    newline;
  1130.    default_color;
  1131.    binary_count := 0;
  1132.    pcbits := 0;
  1133.    incnt := 0;
  1134.    outpos := 0;
  1135.    uoutbuf := '';
  1136.    zipeof := false;
  1137.  
  1138.    case cmethod of
  1139.       0:    {stored}
  1140.             while (not zipeof) and (not dump_user) do
  1141.             begin
  1142.                ReadByte(b);
  1143.                OutByte(b);
  1144.             end;
  1145.  
  1146.       1:    UnShrink;
  1147.  
  1148.       2..5: UnReduce;
  1149.  
  1150.       6:    UnImplode;
  1151.  
  1152.       else  displn('Unknown compression method.');
  1153.    end;
  1154.  
  1155.    if nomore=false then
  1156.       newline;
  1157.  
  1158.    linenum := 1;
  1159. end;
  1160.  
  1161.  
  1162. (* ---------------------------------------------------------- *)
  1163. procedure _itoa(i: integer; var sp);
  1164. var
  1165.    s: array[1..2] of char absolute sp;
  1166. begin
  1167.    s[1] := chr( (i div 10) + ord('0'));
  1168.    s[2] := chr( (i mod 10) + ord('0'));
  1169. end;
  1170.  
  1171. function format_date(date: word): string8;
  1172. const
  1173.    s:       string8 = 'mm-dd-yy';
  1174. begin
  1175.    _itoa(((date shr 9) and 127)+80, s[7]);
  1176.    _itoa( (date shr 5) and 15,  s[1]);
  1177.    _itoa( (date      ) and 31,  s[4]);
  1178.    format_date := s;
  1179. end;
  1180.  
  1181. function format_time(time: word): string8;
  1182. const
  1183.    s:       string8 = 'hh:mm:ss';
  1184. begin
  1185.    _itoa( (time shr 11) and 31, s[1]);
  1186.    _itoa( (time shr  5) and 63, s[4]);
  1187.    _itoa( (time shl  1) and 63, s[7]);
  1188.    format_time := s;
  1189. end;
  1190.  
  1191.  
  1192. (* ---------------------------------------------------------- *)
  1193. procedure process_local_file_header;
  1194. var
  1195.    n:             word;
  1196.    rec:           local_file_header;
  1197.    filename:      string;
  1198.    extra:         string;
  1199.    fpos:          longint;
  1200.  
  1201. begin
  1202.    dos_lseek(zipfd,0,seek_cur);
  1203.    fpos := dos_tell;
  1204.  
  1205.    while (dump_user = false) do
  1206.    begin
  1207.       set_function(fun_arcview);
  1208.  
  1209.       dos_lseek(zipfd,fpos,seek_start);
  1210.       n := dos_read(zipfd,rec,sizeof(rec));
  1211.       get_string(rec.filename_length,filename);
  1212.       filename := remove_path(filename);
  1213.       stoupper(filename);
  1214.       get_string(rec.extra_field_length,extra);
  1215.       csize := rec.compressed_size;
  1216.       cusize := rec.uncompressed_size;
  1217.       cmethod := rec.compression_method;
  1218.       cflags := rec.general_purpose_bit_flag;
  1219.  
  1220.  
  1221.       (* exclude the file if outside current pattern *)
  1222.       if nomore or (not wildcard_match(pattern,filename)) then
  1223.       begin
  1224.          skip_csize;
  1225.          exit;
  1226.       end;
  1227.  
  1228.       (* display file information headers if needed *)
  1229.       if not header_present then
  1230.       begin
  1231.          header_present := true;
  1232.  
  1233.          newline;
  1234.          disp(' File Name    Length   Method     Date      Time');
  1235.          if expand_files then disp('    (Enter) or (S)kip, (V)iew');
  1236.          newline;
  1237.  
  1238.          disp('------------  ------  --------  --------  --------');
  1239.          if expand_files then disp('  -------------------------');
  1240.          newline;
  1241.       end;
  1242.  
  1243.  
  1244.       (* display file information *)
  1245.       disp(ljust(filename,12)+' '+
  1246.            rjust(ltoa(rec.uncompressed_size),7)+'  '+
  1247.            compression_methods[rec.compression_method]+'  '+
  1248.            format_date(rec.last_mod_file_date)+'  '+
  1249.            format_time(rec.last_mod_file_time));
  1250.  
  1251.       if not expand_files then
  1252.       begin
  1253.          skip_csize;
  1254.          newline;
  1255.          exit;
  1256.       end;
  1257.  
  1258.  
  1259.       (* determine action to perform on this member file *)
  1260.       action := 'S';
  1261.       disp('  Action? ');
  1262.       input(action,1);
  1263.       stoupper(action);
  1264.  
  1265.       case action[1] of
  1266.          'S':
  1267.             begin
  1268.                displn(' [Skip]');
  1269.                skip_csize;
  1270.                exit;
  1271.             end;
  1272.  
  1273.          'V','R':
  1274.             begin
  1275.                displn(' [View]');
  1276.                viewfile;
  1277.  
  1278.                header_present := false;
  1279.  
  1280.             {  make_log_entry('View archive member ('+extname
  1281.                                         +') from ('+remove_path(arcname)
  1282.                                         +')',true); }
  1283.             end;
  1284.  
  1285.          'Q':
  1286.             begin
  1287.                displn(' [Quit]');
  1288.                dos_lseek(zipfd,0,seek_end);
  1289.                exit;
  1290.             end;
  1291.  
  1292.          else
  1293.             displn(' [Type S, V or Q!]');
  1294.       end;
  1295.    end;
  1296. end;
  1297.  
  1298.  
  1299. (* ---------------------------------------------------------- *)
  1300. procedure process_central_file_header;
  1301. var
  1302.    n:             word;
  1303.    rec:           central_directory_file_header;
  1304.    filename:      string;
  1305.    extra:         string;
  1306.    comment:       string;
  1307.  
  1308. begin
  1309.    n := dos_read(zipfd,rec,sizeof(rec));
  1310.    get_string(rec.filename_length,filename);
  1311.    get_string(rec.extra_field_length,extra);
  1312.    get_string(rec.file_comment_length,comment);
  1313.   {dos_lseek(zipfd,rec.compressed_size,seek_cur);}
  1314. end;
  1315.  
  1316.  
  1317. (* ---------------------------------------------------------- *)
  1318. procedure process_end_central_dir;
  1319. var
  1320.    n:             word;
  1321.    rec:           end_central_dir_record;
  1322.    comment:       string;
  1323.  
  1324. begin
  1325.    n := dos_read(zipfd,rec,sizeof(rec));
  1326.    get_string(rec.zipfile_comment_length,comment);
  1327. end;
  1328.  
  1329.  
  1330. (* ---------------------------------------------------------- *)
  1331. procedure process_headers;
  1332. var
  1333.    sig:  longint;
  1334.  
  1335. begin
  1336.    dos_lseek(zipfd,0,seek_start);
  1337.    header_present := false;
  1338.  
  1339.    while (not dump_user) do
  1340.    begin
  1341.       if nomore or (dos_read(zipfd,sig,sizeof(sig)) <> sizeof(sig)) then
  1342.          exit
  1343.       else
  1344.  
  1345.       if sig = local_file_header_signature then
  1346.          process_local_file_header
  1347.       else
  1348.  
  1349.       if sig = central_file_header_signature then
  1350.          process_central_file_header
  1351.       else
  1352.  
  1353.       if sig = end_central_dir_signature then
  1354.       begin
  1355.          process_end_central_dir;
  1356.          exit;
  1357.       end
  1358.  
  1359.       else
  1360.       begin
  1361.          displn('Invalid Zipfile Header');
  1362.          exit;
  1363.       end;
  1364.    end;
  1365.  
  1366. end;
  1367.  
  1368.  
  1369. (* ---------------------------------------------------------- *)
  1370. procedure select_pattern;
  1371. begin
  1372.    default_pattern := '*.*';
  1373.  
  1374.    while true do
  1375.    begin
  1376.       newline;
  1377.       disp(remove_path(zipfn));
  1378.       get_def(': View member filespec:', enter_eq+default_pattern+'? ');
  1379.       
  1380.       get_nextpar;
  1381.       pattern := par;
  1382.       stoupper(pattern);
  1383.       if length(pattern) = 0 then
  1384.          pattern := default_pattern;
  1385.  
  1386.       if (pattern = 'none') or (pattern = 'Q') or dump_user then
  1387.          exit;
  1388.    
  1389.       process_headers;
  1390.    
  1391.       default_pattern := 'none';
  1392.    end;
  1393. end;
  1394.  
  1395.  
  1396. (* ---------------------------------------------------------- *)
  1397. procedure view_zipfile;
  1398. begin
  1399.    zipfd := dos_open(zipfn,open_read);
  1400.    if zipfd = dos_error then
  1401.       exit;
  1402.  
  1403.    if expand_files then
  1404.       select_pattern
  1405.    else
  1406.    begin
  1407.       pattern := '*.*';
  1408.       process_headers;
  1409.    end;
  1410.  
  1411.    dos_close(zipfd);
  1412. end;
  1413.  
  1414.  
  1415.  
  1416. (* ---------------------------------------------------------- *)
  1417. procedure process_zipfile(name: filenames);
  1418. var
  1419.    mem:    longint;
  1420.  
  1421. begin
  1422.    linenum := 1;
  1423.    cmdline := '';
  1424.    expand_files := false;
  1425.    zipfn := name;
  1426.    view_zipfile;
  1427.  
  1428.    newline;
  1429.    get_def('View text files in this zipfile:','(Enter)=yes? ');
  1430.  
  1431.    (* process text viewing if desired *)
  1432.    get_nextpar;
  1433.    if par[1] <> 'N' then
  1434.    begin
  1435.       expand_files := true;
  1436.       view_zipfile;
  1437.    end;
  1438. end;
  1439.  
  1440.  
  1441. (*
  1442.  * main program
  1443.  *
  1444.  *)
  1445.  
  1446. var
  1447.    i:      integer;
  1448.    par:    anystring;
  1449.  
  1450. begin
  1451.    gotoxy(60,scroll_line+1);
  1452.    reverseVideo;
  1453.    disp(' ZipTV ');
  1454.  
  1455.    SetScrollPoint(scroll_line);
  1456.    gotoxy(1,23);  lowVideo;
  1457.    linenum := 1;
  1458.  
  1459.    if paramcount = 0 then
  1460.    begin
  1461. {     newline;
  1462.       displn(version);
  1463.       displn('Courtesy of:  S.H.Smith  and  The Tool Shop BBS,  (602) 279-2673.');
  1464.       newline;  }
  1465.  
  1466.       displn('Usage:  ziptv [-Pport] [-Tminutes] [-Llines] FILE[.zip]');
  1467.  
  1468. {     newline;
  1469.       displn('-Pn   enables com port COMn and monitors carrier');
  1470.       displn('-Tn   allows user to stay in program for n minutes');
  1471.       displn('-Ln   sets lines per screen');
  1472. }
  1473.       halt;
  1474.    end;
  1475.  
  1476.    for i := 1 to paramcount do
  1477.    begin
  1478.       par := paramstr(i);
  1479.  
  1480.       if par[1] = '-' then
  1481.          case upcase(par[2]) of
  1482.             'P':  opencom(ord(par[3]) - ord('0'));
  1483.             'T':  tlimit := atoi(copy(par,3,5));
  1484.             'L':  user.pagelen := atoi(copy(par,3,5));
  1485.          end
  1486.       else
  1487.  
  1488.       begin
  1489.         if pos('.',par) = 0 then
  1490.             par := par + '.ZIP';
  1491.  
  1492.         if dos_exists(par) then
  1493.             process_zipfile(par)
  1494.         else
  1495.             displn('File not found: '+par);
  1496.       end;
  1497.    end;
  1498.  
  1499.    newline;
  1500.    displn(version);
  1501.    closecom;
  1502. end.
  1503.  
  1504.  
  1505.