home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / PASCAL / ARCTV26B.ZIP / PROUNSQ.INC < prev    next >
Encoding:
Text File  |  1993-01-04  |  30.8 KB  |  1,387 lines

  1.  
  2. (*
  3.  * prounsq.inc - PCB ProDOOR view-archive text library (low-level)
  4.  *              (uses 25k of heap)
  5.  *
  6.  * 9-sep-87 (rev. 14-Dec-87)
  7.  *
  8.  * This function displays the text contents of a specified archive
  9.  * file.  The filename must be fully specified and verified.
  10.  *
  11.  * Processes archive view and extract functions.
  12.  *
  13.  *)
  14.  
  15. {$R-}   {some but fiddling causes range errors; this is okay}
  16.  
  17. (*
  18.   *** original author unknown ***
  19.  
  20.   version 1.01 - 10/19/85. 
  21.      changed end-of-file processing to, hopefully, be
  22.      more compatible with cpm (whatever that is).
  23.  
  24.   version 1.01a - 12/19/85 modified by Roy Collins
  25.      mail: techmail bbs @ 703-430-2535
  26.  
  27.   version 2.00 - 6/11/86   modified by David W. Carroll
  28.      mail: high sierra rbbs-pc @ 209/296-3534
  29.  
  30.   version 3.00 - 7/30/87   modified by Richard P. Byrne
  31.      bbs mail: software society bbs @ (201) 729-7410
  32.  
  33.   version 3.01 - 8/08/87   modified by Samuel H. Smith
  34.      mail: the tool shop @ 602-279-2673
  35.  
  36.   *** integration with ProDOOR ***
  37.  
  38.   version 4.0 (2.4) - 9/10/87  by Samuel H. Smith
  39.      integrated with pcb prodoor as a text view
  40.      function.  rewrote all i/o calls for door
  41.      library calls.  removed crc calculation for speed.
  42.      added user interface that lists archive member information
  43.      and allows view or extract on selected files.
  44. *)
  45.  
  46.  
  47.  
  48. (* ------------------------------------------------------------- *)
  49.  
  50. procedure resync;
  51.    (* flush input buffer and force re-synchronization *)
  52. begin
  53.    dos_lseek(arcfile,ufilepos,seek_start);
  54.    uinpos := 0;
  55. end;
  56.  
  57. procedure skip_rest;
  58.    (* skip to the end of the current archive entry *)
  59. begin
  60.    inc(ufilepos,fsize);
  61.    resync;
  62.    fsize := 0;
  63. end;
  64.  
  65.  
  66. (* ------------------------------------------------------------- *)
  67.  
  68. procedure putc_unp (c:                  integer);
  69.    (* output each character from archive to screen *)
  70.  
  71.    procedure flushbuf;
  72.    begin
  73.       disp(uoutbuf);
  74.       uoutbuf := '';
  75.    end;
  76.  
  77.    procedure addchar;
  78.    begin
  79.       inc(uoutbuf[0]);
  80.       uoutbuf[length(uoutbuf)] := chr(c);
  81.    end;
  82.  
  83.    procedure not_text;
  84.    begin
  85.       newline;
  86.       displn('This is not a text file!');
  87.       skip_rest;
  88.    end;
  89.    
  90. begin
  91.  
  92.    case c of
  93.    13:  begin
  94.            if linenum < 1000 then
  95.            begin
  96.               flushbuf;
  97.               newline;
  98.            end;
  99.  
  100.            if nomore then
  101.               skip_rest;
  102.         end;
  103.  
  104.    10: ;              
  105.  
  106.    26: skip_rest;         {jump to nomore mode on ^z}
  107.  
  108.    8,9,32..255:
  109.        begin
  110.           if length(uoutbuf) >= max_linelen then
  111.           begin
  112.              flushbuf;
  113.              if fsize > 10 then
  114.                 not_text;
  115.           end;
  116.  
  117.           if linenum < 1000 then   {stop display on nomore}
  118.              addchar;
  119.        end;
  120.  
  121.    else
  122.       begin
  123.          if binary_count < max_binary then
  124.             inc(binary_count)
  125.          else
  126.          if fsize > 10 then
  127.             not_text;
  128.       end;
  129.    end;
  130.  
  131. end;
  132.  
  133.  
  134. (* ------------------------------------------------------------- *)
  135.  
  136. procedure abortme;
  137.    { terminate the program with an error message }
  138. begin
  139.    displn('Abort: Invalid archive');
  140.    arc_eof := true;
  141. end;
  142.  
  143.  
  144. (* ------------------------------------------------------------- *)
  145.  
  146. function fn_to_str (fn:             fntype): string;
  147.                            { convert strings from c format (trailing 0)
  148.                              to turbo pascal format (leading length byte). }
  149. var
  150.    s:                  string;
  151.    i:                  integer;
  152.  
  153. begin
  154.    s := '';
  155.    i := 0;
  156.  
  157.    while fn [i]<> #0 do
  158.    begin
  159.       s := s + fn [i];
  160.       inc(i);
  161.    end;
  162.  
  163.    fn_to_str := s
  164. end;
  165.  
  166.  
  167.  
  168. (* ------------------------------------------------------------- *)
  169.  
  170. procedure get_arc(var i: integer);  { read 1 byte from the archive file }
  171. begin
  172.    if arc_eof then
  173.       i := 0
  174.    else
  175.    begin
  176.       if (uinpos < 1) or (uinpos > uinmax) then
  177.       begin
  178.          uinmax := dos_read(arcfile,uinbuf,uinbufsize);
  179.          uinpos := 1;
  180.          if uinmax < 1 then
  181.          begin
  182.             i := 0;
  183.             arc_eof := true;
  184.             exit;
  185.          end;
  186.       end;
  187.       
  188.       i := uinbuf[uinpos];
  189.       inc(uinpos);
  190.       inc(ufilepos);
  191.    end;
  192. end;
  193.  
  194. procedure bread(var buffer; size: integer);
  195.    {block read from buffered file}
  196. var
  197.    buf:  array[1..maxint] of byte absolute buffer;
  198.    c,i:  integer;
  199. begin
  200.    for i := 1 to size do
  201.    begin
  202.       get_arc(c);
  203.       if arc_eof then 
  204.          exit;
  205.       buf[i] := c;
  206.    end;
  207. end;
  208.  
  209.  
  210. (* ------------------------------------------------------------- *)
  211.  
  212. procedure close_arc;       { close the archive file }
  213. begin
  214.    dos_close(arcfile);
  215. end;
  216.  
  217.  
  218. (* ------------------------------------------------------------- *)
  219.  
  220. function read_header: boolean;
  221.                            { read a file header from the archive file }
  222.                            { false = eof found; true = header found }
  223. var
  224.    name:               fntype;
  225.    try:                integer;
  226.    c:                  integer;
  227.  
  228. begin
  229.    read_header := false;
  230.  
  231.    if arc_eof then
  232.       exit;
  233.  
  234.    resync;
  235.    try := 100;
  236.    get_arc(c);
  237.    while (c <> arcmarc) and (try > 0) do
  238.    begin
  239.       get_arc(c);
  240.       dec(try);
  241.    end;
  242.  
  243.    get_arc(hdrver);
  244.    if (try = 0) or (hdrver < 0) then
  245.    begin
  246.       abortme;
  247.       exit;
  248.    end;
  249.  
  250.    if hdrver = 0 then         { special end of file marker }
  251.       exit;
  252.  
  253.    if hdrver > arcver then
  254.    begin
  255.       bread(name,fnlen);
  256.       abortme;
  257.       exit;
  258.    end;
  259.  
  260.    if hdrver = 1 then
  261.    begin
  262.       bread(hdr,sizeof(heads)-sizeof(longint));
  263.       hdrver := 2;
  264.       hdr.length := hdr.size;
  265.    end
  266.    else
  267.       bread(hdr,sizeof(heads));
  268.  
  269.    read_header := true;
  270. end;
  271.  
  272.  
  273. (* ------------------------------------------------------------- *)
  274.  
  275. procedure putc_unrle (c:                  integer);
  276. begin
  277.  
  278.    case state of
  279.       nohist:
  280.             if c = dle then
  281.                state := inrep
  282.             else
  283.             begin
  284.                lastc := c;
  285.                putc_unp(c);
  286.             end;
  287.  
  288.       inrep:
  289.             begin
  290.                if c = 0 then
  291.                   putc_unp(dle)
  292.                else
  293.                begin
  294.                   dec(c);
  295.                   while (c <> 0) do
  296.                   begin
  297.                      putc_unp(lastc);
  298.                      dec(c);
  299.                   end
  300.                end;
  301.  
  302.                state := nohist;
  303.             end;
  304.    end;
  305. end;
  306.  
  307.  
  308. (* ------------------------------------------------------------- *)
  309.  
  310. procedure getc_unp(var i: integer);
  311. begin
  312.    if fsize = 0 then
  313.       i := -1
  314.    else
  315.    begin
  316.       dec(fsize);
  317.       get_arc(i);
  318.    end;
  319. end;
  320.  
  321.  
  322. (********************************************************************)
  323.  
  324. procedure unsqueeze;
  325.  
  326. { definitions for unsqueeze }
  327.  
  328. const
  329.    error =            -1;
  330.    speof =             256;
  331.    numvals =           256;   { 1 less than the number of values }
  332.  
  333. type
  334.    nd =                record
  335.          child:              array [0..1] of integer;
  336.    end;
  337.  
  338. var
  339.    node:               array [0.. numvals] of nd;
  340.    bpos:               integer;
  341.    curin:              integer;
  342.    numnodes:           integer;
  343.  
  344.    procedure init_usq;        { initialize for unsqueeze }
  345.    var
  346.       i:                integer;
  347.  
  348.    begin
  349.       bpos := 99;
  350.       bread(numnodes,sizeof(numnodes));
  351.       if (numnodes < 0) or (numnodes > numvals) then
  352.       begin
  353.          abortme;
  354.          exit;
  355.       end;
  356.  
  357.       node[0].child [0]:=-(speof + 1);
  358.       node[0].child [1]:=-(speof + 1);
  359.  
  360.       for i := 0 to numnodes - 1 do
  361.       begin
  362.          bread(node [i].child [0], sizeof (integer));
  363.          bread(node [i].child [1], sizeof (integer));
  364.       end;
  365.    end;
  366.  
  367.  
  368. (* ------------------------------------------------------------- *)
  369.  
  370.    procedure getc_usq(var i: integer);
  371.                               { unsqueeze }
  372.    begin
  373.       i := 0;
  374.  
  375.       while i >= 0 do
  376.       begin
  377.          inc(bpos);
  378.  
  379.          if bpos > 7 then
  380.          begin
  381.             getc_unp(curin);
  382.  
  383.             if curin = error then
  384.             begin
  385.                i := error;
  386.                exit;
  387.             end;
  388.  
  389.             bpos := 0;
  390.             i := node [i].child [1 and curin]
  391.          end
  392.          else
  393.          begin
  394.             curin := curin shr 1;
  395.             i := node [i].child [1 and curin]
  396.          end
  397.       end;
  398.  
  399.       i := -(i + 1);
  400.  
  401.       if i = speof then
  402.          i := -1;
  403.    end;
  404.  
  405. var
  406.    c: integer;
  407. begin
  408.    init_usq;
  409.    getc_usq(c);
  410.  
  411.    while c <> -1 do
  412.    begin
  413.       putc_unrle(c);
  414.       getc_usq(c);
  415.    end;
  416. end;
  417.  
  418.  
  419. (********************************************************************)
  420.  
  421. procedure old_uncrunch;
  422.  
  423. { definitions for uncrunch }
  424.  
  425. const
  426.    tabsize =           4096;
  427.    tabsizem1 =         4095;
  428.    no_pred =           -1;
  429.    empty =             -1;
  430.  
  431. type
  432.    entry =             record
  433.          used:               boolean;
  434.          next:               integer;
  435.          predecessor:        integer;
  436.          follower:           byte;
  437.    end;
  438.  
  439.    string_tab_rec      = array [0..tabsizem1] of entry;
  440.    stack_rec           = array [0.. tabsizem1] of byte;
  441.  
  442. var
  443.    sp:                 integer;
  444.    string_tab:         ^string_tab_rec;
  445.    stack:              ^stack_rec;
  446.  
  447. var
  448.    code_count:         integer;
  449.    code:               integer;
  450.    firstc:             boolean;
  451.    oldcode:            integer;
  452.    finchar:            integer;
  453.    inbuf:              integer;
  454.    outbuf:             integer;
  455.    newhash:            boolean;
  456.  
  457.  
  458. (* ------------------------------------------------------------- *)
  459.  
  460.    function eolist (index:              integer): integer;
  461.    var
  462.       temp:               integer;
  463.  
  464.    begin
  465.       temp := string_tab^ [index].next;
  466.       while temp <> 0 do
  467.       begin
  468.          index := temp;
  469.          temp := string_tab^ [index].next;
  470.       end;
  471.  
  472.       eolist := index;
  473.    end;
  474.  
  475.  
  476. (* ------------------------------------------------------------- *)
  477.  
  478.    function hash (pred,
  479.                   foll: integer): integer;
  480.                               { calculate hash value }
  481.                               { thanks to bela lubkin }
  482.    var
  483.       local2:             longint;
  484.       h:                  integer;
  485.       tempnext:           integer;
  486.    begin
  487.  
  488.       if newhash then
  489.          local2 := longint(pred + foll) * 15073
  490.       else
  491.       begin
  492.          local2 := word( (pred + foll) or $0800) and $FFFF;
  493.          local2 := local2 * local2;
  494.          local2 := (local2 shr 6) and $0FFF;
  495.       end;
  496.  
  497.       h := local2 mod tabsize;
  498.  
  499.       if string_tab^ [h].used then
  500.       begin
  501.          h := eolist (h);
  502.          tempnext :=(h + 101) mod tabsize;
  503.  
  504.          while string_tab^ [tempnext].used do
  505.          begin
  506.             inc(tempnext);
  507.             if tempnext = tabsize then
  508.                tempnext := 0;
  509.          end;
  510.  
  511.          string_tab^ [h].next := tempnext;
  512.          h := tempnext;
  513.       end;
  514.  
  515.       hash := h;
  516.    end;
  517.  
  518.  
  519. (* ------------------------------------------------------------- *)
  520.  
  521.    procedure upd_tab (pred,
  522.                       foll:  integer);
  523.    begin
  524.       with string_tab^ [hash (pred, foll)] do
  525.       begin
  526.          used := true;
  527.          next := 0;
  528.          predecessor := pred;
  529.          follower := foll;
  530.       end
  531.    end;
  532.  
  533.  
  534. (* ------------------------------------------------------------- *)
  535.  
  536.    procedure gocode(var i: integer);
  537.    var
  538.       localbuf:           integer;
  539.       returnval:          integer;
  540.  
  541.    begin
  542.  
  543.       if inbuf = -1 then
  544.       begin
  545.          getc_unp(localbuf);
  546.          if localbuf = -1 then
  547.          begin
  548.             i := -1;
  549.             exit;
  550.          end;
  551.  
  552.          localbuf := localbuf and $00ff;
  553.  
  554.          getc_unp(inbuf);
  555.          if inbuf = -1 then
  556.          begin
  557.             i := -1;
  558.             exit;
  559.          end;
  560.  
  561.          inbuf := inbuf and $00ff;
  562.          returnval :=((localbuf shl 4) and $0ff0)+((inbuf shr 4) and $000f);
  563.          inbuf := inbuf and $000f
  564.       end
  565.       else
  566.  
  567.       begin
  568.          getc_unp(localbuf);
  569.          if localbuf = -1 then
  570.          begin
  571.             i := -1;
  572.             exit;
  573.          end;
  574.  
  575.          localbuf := localbuf and $00ff;
  576.          returnval := localbuf +((inbuf shl 8) and $0f00);
  577.          inbuf := -1;
  578.       end;
  579.  
  580.       i := returnval;
  581.    end;
  582.  
  583.  
  584. (* ------------------------------------------------------------- *)
  585.  
  586.    procedure push (c:                  integer);
  587.    begin
  588.       stack^[sp] := c;
  589.       inc(sp);
  590.  
  591.       if sp >= tabsize then
  592.          abortme;
  593.    end;
  594.  
  595.  
  596.  
  597. (* ------------------------------------------------------------- *)
  598.  
  599.    procedure init_tab;
  600.    var
  601.       i:                  integer;
  602.  
  603.    begin
  604.       fillchar(string_tab^, sizeof (string_tab^), 0);
  605.  
  606.       for i := 0 to 255 do
  607.          upd_tab(no_pred, i);
  608.  
  609.       inbuf := -1;
  610.    end;
  611.  
  612.  
  613. (* ------------------------------------------------------------- *)
  614.  
  615.    procedure init_ucr (i:                  integer);
  616.    begin
  617.       newhash := i = 1;
  618.       sp := 0;
  619.       init_tab;
  620.       code_count := tabsize - 256;
  621.       firstc := true;
  622.    end;
  623.  
  624.  
  625. (* ------------------------------------------------------------- *)
  626.  
  627.    procedure getc_ucr(var i: integer);
  628.    var
  629.       c:                  integer;
  630.       code:               integer;
  631.       newcode:            integer;
  632.  
  633.    begin
  634.  
  635.       if firstc then
  636.       begin
  637.          firstc := false;
  638.          gocode(oldcode);
  639.          finchar := string_tab^ [oldcode].follower;
  640.          i := finchar;
  641.          exit;
  642.       end;
  643.  
  644.       if sp = 0 then
  645.       begin
  646.          gocode(newcode);
  647.          code := newcode;
  648.  
  649.          if code = -1 then
  650.          begin
  651.             i := -1;
  652.             exit;
  653.          end;
  654.  
  655.          if not string_tab^ [code].used then
  656.          begin
  657.             code := oldcode;
  658.             push(finchar)
  659.          end;
  660.  
  661.          while string_tab^ [code].predecessor <> no_pred do
  662.             with string_tab^ [code] do
  663.             begin
  664.                push(follower);
  665.                code := predecessor;
  666.             end;
  667.  
  668.          finchar := string_tab^ [code].follower;
  669.          push(finchar);
  670.  
  671.          if code_count <> 0 then
  672.          begin
  673.             upd_tab(oldcode, finchar);
  674.             dec(code_count);
  675.          end;
  676.  
  677.          oldcode := newcode
  678.       end;
  679.  
  680.       if sp > 0 then
  681.       begin
  682.          dec(sp);
  683.          i := stack^ [sp]
  684.       end
  685.       else
  686.          i := -1;
  687.    end;
  688.  
  689.  
  690. (* ------------------------------------------------------------- *)
  691.  
  692. { old_uncrunch }
  693. var
  694.    c: integer;
  695.  
  696. begin
  697.    new(string_tab);
  698.    new(stack);
  699.  
  700.    case hdrver of
  701.       5:    begin   {old crunch 1}
  702.                init_ucr(0);
  703.                getc_ucr(c);
  704.  
  705.                while c <> -1 do
  706.                begin
  707.                   putc_unp(c);
  708.                   getc_ucr(c);
  709.                end;
  710.             end;
  711.  
  712.       6:    begin  {crunch 2}
  713.                init_ucr(0);
  714.                getc_ucr(c);
  715.  
  716.                while c <> -1 do
  717.                begin
  718.                   putc_unrle(c);
  719.                   getc_ucr(c);
  720.                end;
  721.             end;
  722.  
  723.       7:    begin  {new crunch 1}
  724.                init_ucr(1);
  725.                getc_ucr(c);
  726.  
  727.                while c <> -1 do
  728.                begin
  729.                   putc_unrle(c);
  730.                   getc_ucr(c);
  731.                end;
  732.             end;
  733.    end;
  734.  
  735.    dispose(string_tab);
  736.    dispose(stack);
  737. end;
  738.  
  739.  
  740.  
  741. (************************************************************)
  742.  
  743. procedure uncrunch(squash: integer);
  744.  
  745. { definitions for dynamic uncrunch }
  746.  
  747. const
  748.    crunch_bits =       12;
  749.    squash_bits =       13;
  750.    hsize =             8192;
  751.    hsizem1 =           8191;
  752.    init_bits =         9;
  753.    first =             257;
  754.    clear =             256;
  755.    bitsm1 =            12;
  756.    rmask : array [0..8] of byte =
  757.       ($00, $01, $03, $07, $0f, $1f, $3f, $7f, $ff);
  758.    
  759. type
  760.    hsize_array_integer = array [0..hsizem1] of integer;
  761.    hsize_array_byte    = array [0..hsizem1] of byte;
  762.  
  763. var
  764.    bits,
  765.    n_bits,
  766.    maxcode:            integer;
  767.    buf:                array [0.. bitsm1] of byte;
  768.    clear_flg:          integer;
  769.    free_ent:           integer;
  770.    maxcodemax:         integer;
  771.    offset,
  772.    sizex:              integer;
  773.    firstch:            boolean;
  774.    prefix:             ^hsize_array_integer;
  775.    suffix:             ^hsize_array_byte;
  776.    stack1:             ^hsize_array_byte;
  777.  
  778.  
  779. (* ------------------------------------------------------------- *)
  780.  
  781.    procedure getcode(var res: integer);
  782.  
  783.    label next;
  784.    var
  785.       code,
  786.       r_off,
  787.       bitsx:              integer;
  788.       bp:                 byte;
  789.       ii:                 integer;
  790.  
  791.    begin
  792.  
  793.       if firstch then
  794.       begin
  795.          offset := 0;
  796.          sizex := 0;
  797.          firstch := false;
  798.       end;
  799.  
  800.       bp := 0;
  801.  
  802.       if (clear_flg > 0) or (offset >= sizex) or (free_ent > maxcode) then
  803.       begin
  804.  
  805.          if free_ent > maxcode then
  806.          begin
  807.             inc(n_bits);
  808.  
  809.             if n_bits = bits then
  810.                maxcode := maxcodemax
  811.             else
  812.                maxcode :=(1 shl n_bits)- 1;
  813.          end;
  814.  
  815.          if clear_flg > 0 then
  816.          begin
  817.             n_bits := init_bits;
  818.             maxcode :=(1 shl n_bits)- 1;
  819.             clear_flg := 0;
  820.          end;
  821.  
  822.          for ii := 0 to n_bits - 1 do
  823.          begin
  824.             sizex := ii;
  825.             getc_unp(code);
  826.             if code = -1 then
  827.                goto next
  828.             else
  829.                buf[sizex] := code;
  830.          end;
  831.  
  832.          inc(sizex);
  833.  
  834.    next :;
  835.          if sizex <= 0 then
  836.          begin
  837.             res := -1;
  838.             exit;
  839.          end;
  840.  
  841.          offset := 0;
  842.          sizex :=(sizex shl 3)-(n_bits - 1);
  843.       end;
  844.  
  845.       r_off := offset;
  846.       bitsx := n_bits;           { get first byte }
  847.  
  848.       bp := bp +(r_off shr 3);
  849.       r_off := r_off and 7;      { get first parft (low order bits) }
  850.       code := buf [bp] shr r_off;
  851.       inc(bp);
  852.       bitsx := bitsx -(8 - r_off);
  853.       r_off := 8 - r_off;
  854.  
  855.       if bitsx >= 8 then
  856.       begin
  857.          code := code or (buf [bp] shl r_off);
  858.          inc(bp);
  859.          r_off := r_off + 8;
  860.          bitsx := bitsx - 8;
  861.       end;
  862.  
  863.       code := code or ((buf [bp] and rmask [bitsx]) shl r_off);
  864.       offset := offset + n_bits;
  865.       res := code;
  866.    end;
  867.  
  868.  
  869. (* ------------------------------------------------------------- *)
  870.  
  871.    procedure decomp (squashflag:         integer);
  872.    label next;
  873.    var
  874.       stackp,
  875.       finchar:            integer;
  876.       code,
  877.       oldcode,
  878.       incode:             integer;
  879.  
  880.    begin                         { init var }
  881.       if squashflag = 0 then
  882.          bits := crunch_bits
  883.       else
  884.          bits := squash_bits;
  885.  
  886.       if firstch then
  887.          maxcodemax := 1 shl bits;
  888.  
  889.       if squashflag = 0 then
  890.       begin
  891.          getc_unp(code);
  892.          if code <> bits then
  893.          begin
  894.             abortme;
  895.             exit;
  896.          end;
  897.       end;
  898.  
  899.       clear_flg := 0;
  900.       n_bits := init_bits;
  901.       maxcode :=(1 shl n_bits)- 1;
  902.  
  903.       for code := 255 downto 0 do
  904.       begin
  905.          prefix^[code]:= 0;
  906.          suffix^[code]:= code;
  907.       end;
  908.  
  909.       free_ent := first;
  910.       getcode(oldcode);
  911.       finchar := oldcode;
  912.  
  913.       if oldcode = -1 then
  914.          exit;
  915.  
  916.       if squashflag = 0 then
  917.          putc_unrle(finchar)
  918.       else
  919.          putc_unp(finchar);
  920.  
  921.       stackp := 0;
  922.       getcode(code);
  923.  
  924.       while (code > -1) do
  925.       begin
  926.          if code = clear then
  927.          begin
  928.             for code := 255 downto 0 do
  929.                prefix^[code]:= 0;
  930.  
  931.             clear_flg := 1;
  932.             free_ent := first - 1;
  933.             getcode(code);
  934.  
  935.             if code = -1 then
  936.                goto next;
  937.          end;
  938.  
  939.    next:
  940.          incode := code;
  941.  
  942.          if code >= free_ent then
  943.          begin
  944.             stack1^[stackp]:= finchar;
  945.             inc(stackp);
  946.             code := oldcode;
  947.          end;
  948.  
  949.          while (code >= 256) do
  950.          begin
  951.             stack1^[stackp]:= suffix^ [code];
  952.             inc(stackp);
  953.             code := prefix^ [code];
  954.          end;
  955.  
  956.          finchar := suffix^ [code];
  957.          stack1^[stackp]:= finchar;
  958.          inc(stackp);
  959.  
  960.          repeat
  961.             dec(stackp);
  962.             if squashflag = 0 then
  963.                putc_unrle(stack1^ [stackp])
  964.             else
  965.                putc_unp(stack1^ [stackp]);
  966.          until stackp <= 0;
  967.  
  968.          code := free_ent;
  969.  
  970.          if code < maxcodemax then
  971.          begin
  972.             prefix^[code]:= oldcode;
  973.             suffix^[code]:= finchar;
  974.             free_ent := code + 1;
  975.          end;
  976.  
  977.          oldcode := incode;
  978.          getcode(code);
  979.       end;
  980.    end;
  981.  
  982. (* ------------------------------------------------------------- *)
  983.  
  984. begin
  985.    {allocate heap storage}
  986.    new(stack1);
  987.    new(suffix);
  988.    new(prefix);
  989.  
  990.    firstch := true;
  991.    decomp(squash);
  992.  
  993.    {release heap storage}
  994.    dispose(prefix);
  995.    dispose(suffix);
  996.    dispose(stack1);
  997. end;
  998.  
  999. (**************************************************************)
  1000.  
  1001.  
  1002. procedure viewfile;
  1003. var
  1004.    c:                  integer;
  1005.    filestart:          longint;
  1006.  
  1007. begin
  1008.    disp(WHITE);
  1009.    
  1010.    binary_count := 0;
  1011.    uoutbuf := '';
  1012.    fsize := hdr.size;
  1013.    state := nohist;
  1014.    filestart := ufilepos;
  1015.  
  1016.    case hdrver of
  1017.       1, 2: begin   {store 1, store 2}
  1018.                getc_unp(c);
  1019.                while c <> -1 do
  1020.                begin
  1021.                   putc_unp(c);
  1022.                   getc_unp(c);
  1023.                end
  1024.             end;
  1025.  
  1026.       3:    begin  {packed}
  1027.                getc_unp(c);
  1028.                while c <> -1 do
  1029.                begin
  1030.                   putc_unrle(c);
  1031.                   getc_unp(c);
  1032.                end;
  1033.             end;
  1034.  
  1035.       4:    unsqueeze;
  1036.  
  1037.       5..7: old_uncrunch;
  1038.  
  1039.       8:    uncrunch(0);  {new crunch 2}
  1040.  
  1041.       9:    uncrunch(1);  {squash}
  1042.  
  1043.       else  begin
  1044.                displn('I dont know how to unpack file '+ fn_to_str (hdr.name));
  1045.                displn('I think you need a newer version of '+comfile);
  1046.             end;
  1047.    end;
  1048.  
  1049.    newline;
  1050.  
  1051.    {rewind to start of viewed file}
  1052.    ufilepos := filestart;
  1053.    resync;
  1054. end;
  1055.  
  1056.  
  1057. (* ------------------------------------------------------------- *)
  1058.  
  1059. {$IFNDEF DISABLE_EXTRACT}  
  1060.  
  1061.    procedure xtract;
  1062.       (* extract the current member into a scratch file *)
  1063.  
  1064.    const
  1065.       bufmax = $F000;  {maximum buffer size in bytes}
  1066.       extra = $1000;   {extra heap to leave free}
  1067.    var
  1068.       bufsize: word;   {actual buffer size}
  1069.       ifd:     dos_handle;
  1070.       ofd:     dos_handle;
  1071.       buf:     ^byte;
  1072.       n,w:     word;
  1073.       ver:     byte;
  1074.       ulspace: real;
  1075.  
  1076.    begin
  1077.  
  1078. {$IFNDEF IN_ARCTV}
  1079.       (* see if enough space is free on the upload directory *)
  1080.       if disk_space(upload_dir[1]) < pcbsetup.min_upload_free then
  1081.       begin
  1082.          newline;
  1083.          make_log_entry('Sorry, no space for '+remove_path(scratchfile),true);
  1084.          exit;
  1085.       end;
  1086. {$ENDIF}
  1087.  
  1088.       (* see if enough RAM space is free for copy buffer *)
  1089.       bufsize := bufmax;
  1090.       if bufsize > maxavail-extra then
  1091.          bufsize := maxavail-extra;
  1092.          
  1093.       if bufsize < extra then
  1094.       begin
  1095.          displn('?ram');
  1096.          exit;
  1097.       end;
  1098.  
  1099.  
  1100.       (* create SCRATCH archive if needed, otherwise position for append *)
  1101.       if exists(scratchfile) then
  1102.       begin
  1103.          ofd := dos_open(scratchfile,open_update);
  1104.          dos_lseek(ofd,-2,seek_end);               {rewrite eof header}
  1105.  
  1106. {$IFNDEF IN_ARCTV}
  1107.          inc(user.downloads);    {charge for all files after the first
  1108.                                   (which will be counted by the actual d/l}
  1109. {$ENDIF}
  1110.       end
  1111.       else
  1112.  
  1113.       begin
  1114. {$IFNDEF IN_ARCTV}
  1115.          display_file(extract_help_file);
  1116.          header_present := false;
  1117. {$ENDIF}
  1118.          ofd := dos_create(scratchfile);           {else create file if needed}
  1119.       end;
  1120.  
  1121.       if ofd = dos_error then
  1122.       begin
  1123.          displn('?create');
  1124.          dos_close(ifd);
  1125.          exit;
  1126.       end;
  1127.  
  1128.  
  1129.       (* write the header for this new member *)
  1130.       ver := arcmarc;
  1131.       dos_write(ofd,ver,1);
  1132.       ver := hdrver;
  1133.       dos_write(ofd,ver,1);
  1134.       dos_write(ofd,hdr,sizeof(hdr));
  1135.  
  1136.  
  1137.       (* copy the member file to the scratchfile *)
  1138.       fsize := hdr.size;
  1139.       getmem(buf,bufsize);
  1140.  
  1141.       resync;
  1142.  
  1143.       repeat
  1144.          if fsize > bufsize then
  1145.             n := bufsize
  1146.          else
  1147.             n := fsize;
  1148.          fsize := fsize - n;
  1149.  
  1150.          disp('.');
  1151.          n := dos_read(arcfile,buf^,n);
  1152.          inc(ufilepos,n);
  1153.  
  1154.          disp(^H' '^H);
  1155.          dos_write(ofd,buf^,n);
  1156.          w := dos_regs.ax;
  1157.       until w < bufsize;
  1158.  
  1159.  
  1160.       (* write an eof marker (header with method=0) *)
  1161.       ver := arcmarc;
  1162.       dos_write(ofd,ver,1);
  1163.       ver := 0;
  1164.       dos_write(ofd,ver,1);
  1165.       dos_close(ofd);
  1166.  
  1167.       if n <> w then
  1168.       begin
  1169.          displn('?write');
  1170.          dos_unlink(scratchfile);
  1171.       end;
  1172.  
  1173.       freemem(buf,bufsize);
  1174.       resync;
  1175.    end;
  1176.  
  1177. {$ENDIF}
  1178.  
  1179.  
  1180. (* ------------------------------------------------------------- *)
  1181.  
  1182. procedure describe;
  1183.    (* print a verbose description of the current archive header *)
  1184.  
  1185.    function itoa2(i: integer): anystring;
  1186.    begin
  1187.       itoa2 := chr(ord('0') + i div 10) +
  1188.                chr(ord('0') + i mod 10);
  1189.    end;
  1190.  
  1191.    function format_date(bin: integer): anystring;
  1192.        (* format archive member date *)
  1193.    begin
  1194.       if bin = 0 then
  1195.          format_date := '        '
  1196.       else
  1197.          format_date := itoa2( (bin shr 5) and  15)      + '-' + {month}
  1198.                         itoa2( (bin      ) and  31)      + '-' + {day}
  1199.                         itoa2( (bin shr 9) and 127 + 80);        {year}
  1200.    end;
  1201.  
  1202.    function format_time(bin: integer): anystring;
  1203.        (* format archive member time *)
  1204.    begin
  1205.       if bin = 0 then
  1206.          format_time := '        '
  1207.       else
  1208.          format_time := itoa2( (bin shr 11) and 31) + ':' +  {hour}
  1209.                         itoa2( (bin shr  5) and 63) + ':' +  {minute}
  1210.                         itoa2( (bin shl  1) and 63);         {second}
  1211.    end;
  1212.  
  1213. begin
  1214.    if not header_present then
  1215.    begin
  1216.       displn(WHITE);
  1217.  
  1218.     {$IFNDEF DISABLE_EXTRACT}  
  1219.       displn('File Name     Length    Date     Time    (Enter) or (S)kip, (V)iew, (X)tract');
  1220.       displn('---------     ------   ------   ------   -----------------------------------');
  1221.     {$ELSE}
  1222.       displn('File Name     Length    Date     Time    (Enter) or (S)kip, (V)iew');
  1223.       displn('---------     ------   ------   ------   -------------------------');
  1224.     {$ENDIF}
  1225.  
  1226.       header_present := true;
  1227.    end;
  1228.  
  1229.    with hdr do
  1230.    disp( MAGENTA + extname+ copy('             ',1,12-ord(extname[0]) )+
  1231.          RED     + ftoa(length,8,0)+'  '+
  1232.          GREEN   + format_date(date)+' '+
  1233.          CYAN    + format_time(time)+'   ');
  1234. end;
  1235.  
  1236.  
  1237. (* ------------------------------------------------------------- *)
  1238. procedure view_archive_text(arcname: anystring);
  1239.  
  1240. (* ------------------------------------------------------------- *)
  1241.  
  1242. procedure open_arc;        { open the archive file for input processing }
  1243.  
  1244. begin
  1245.    arcfile := dos_open(arcname,open_read);
  1246.    arc_eof := arcfile = dos_error;
  1247.    ufilepos := 0;
  1248.    uinpos := 0;
  1249. end;
  1250.  
  1251.  
  1252. (* ------------------------------------------------------------- *)
  1253.  
  1254. procedure process_file;
  1255. var
  1256.    ext:      anystring;
  1257.    i:        integer;
  1258.    view:     anystring;
  1259.    istext:   boolean;
  1260.    done:     boolean;
  1261.  
  1262. begin
  1263.  
  1264. (* skip the file if it does not match the selection wildcard *)
  1265.    extname := fn_to_str (hdr.name);
  1266.    if not wildcard_match(pattern,extname) then
  1267.    begin
  1268.       inc(ufilepos,hdr.size);
  1269.       resync;
  1270.       exit;
  1271.    end;
  1272.  
  1273. (* find out if it is a non-text file based on extention *)
  1274.    ext := ext_only(extname);
  1275.    istext := true;
  1276.    for i := 1 to nexclude do
  1277.       if copy(ext,1,length(exclude[i])) = exclude[i] then
  1278.          istext := false;
  1279.  
  1280. (* ask user what to do with the file *)
  1281.    repeat
  1282.       describe;
  1283.       disp(YELLOW+'Action? ');
  1284.       view := 'S';
  1285.       input(view,1);
  1286.       done := false;
  1287.  
  1288.       case upcase(view[1]) of
  1289.       'Y','V','D':                (* view/display file *)
  1290.          begin
  1291.             if istext then
  1292.             begin
  1293.                displn(' [View]');
  1294.                newline;
  1295.  
  1296.                linenum := 1;
  1297.                viewfile;        (* view file and rewind to see it again *)
  1298.  
  1299.                header_present := false;
  1300.                make_log_entry('View ARC member ('+extname
  1301.                                         +') from ('+remove_path(arcname)
  1302.                                         +')',false);
  1303.                done := false;
  1304.             end
  1305.             else
  1306.                displn(' [Not a textfile!]');
  1307.          end;
  1308.  
  1309.     {$IFNDEF DISABLE_EXTRACT}  
  1310.       'X','E':                  (* extract to scratch.arc *)
  1311.          begin
  1312.             if arcname = scratchfile then
  1313.                displn(' [Cant!]')
  1314.             else
  1315.             begin
  1316.                disp(' [Extract]');
  1317.                xtract;
  1318.                newline;
  1319.                make_log_entry('Extract ARC member ('+extname
  1320.                                         +') from ('+remove_path(arcname)
  1321.                                         +')',false);
  1322.                done := true;
  1323.             end;
  1324.          end;
  1325.     {$ENDIF}
  1326.  
  1327.       'S':                      (* skip to next entry *)
  1328.          begin
  1329.             displn(' [Skip]');
  1330.             inc(ufilepos,hdr.size);
  1331.             resync;
  1332.             done := true;
  1333.          end;
  1334.  
  1335.       'Q':                      (* quit, skip rest of arc *)
  1336.          begin
  1337.             displn(' [Quit]');
  1338.             arc_eof := true;
  1339.             done := true;
  1340.          end;
  1341.  
  1342.       else
  1343.           {$IFNDEF DISABLE_EXTRACT}  
  1344.             displn(' [Type Q, S, V or X!]');
  1345.           {$ELSE}
  1346.             displn(' [Type Q, S, or V!]');
  1347.           {$ENDIF}
  1348.       end;
  1349.  
  1350.    until done or dump_user;
  1351.  
  1352. end;
  1353.  
  1354. (* ------------------------------------------------------------- *)
  1355.  
  1356.    { extract and view text files in the archive - main entry }
  1357.  
  1358. begin
  1359.  
  1360. {$IFNDEF DISABLE_EXTRACT}  
  1361.    disp(YELLOW+'Text extract/view filespec: (wildcards are OK) (Enter)='+
  1362.                                     default_pattern+'? ');
  1363. {$ELSE}
  1364.    disp(YELLOW+'Text view filespec: (wildcards are OK) (Enter)='+
  1365.                                     default_pattern+'? ');
  1366.   {$ENDIF}
  1367.    input(pattern,13);
  1368.    newline;
  1369.  
  1370.    if length(pattern) = 0 then
  1371.       pattern := default_pattern;
  1372.    stoupper(pattern);
  1373.  
  1374.    open_arc;
  1375.    if arc_eof then
  1376.       exit;
  1377.  
  1378.    header_present := false;
  1379.    while read_header do
  1380.       process_file;
  1381.  
  1382.    close_arc;
  1383. end;
  1384.  
  1385. { $R+}
  1386.  
  1387.