home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / INFO / TURBOPAS / DEARC.ZIP / DEARC.PAS
Encoding:
Pascal/Delphi Source File  |  1985-12-08  |  18.3 KB  |  686 lines

  1. {$R-}
  2. {$U-}
  3. {$C-}
  4. {$K-}
  5.  
  6. program dearc;
  7.  
  8. { DEARC.PAS - Program to extract all files from an archive created by version
  9.   4.30 or earlier of the ARC utility.
  10.  
  11.   ARC is COPYRIGHT 1985 by System Enhancement Associates.
  12.  
  13.   This program requires Turbo Pascal Version 3.01A. It should work in all
  14.   supported environments (PCDOS, CPM, etc.) but I have only tested it on
  15.   an IBM PC running PC DOS version 3.10.
  16.  
  17.   Usage:
  18.  
  19.     DEARC arcname
  20.  
  21.     arcname is the path/file name of the archive file. All files contained
  22.     in the archive will be extracted into the current directory.
  23.  
  24.   Version 1.01 - 10/19/85. Changed end-of-file processing to, hopefully, be
  25.                            more compatible with CPM (whatever that is).
  26. }
  27.  
  28. const BLOCKSIZE = 128;
  29.       arcmarc   = 26;              { special archive marker }
  30.       arcver    = 6;               { archive header version code }
  31.       strlen    = 100;             { standard string length }
  32.       fnlen     = 12;              { file name length - 1 }
  33.  
  34. const crctab : array [0..255] of integer =
  35.   ( $0000, $C0C1, $C181, $0140, $C301, $03C0, $0280, $C241,
  36.     $C601, $06C0, $0780, $C741, $0500, $C5C1, $C481, $0440,
  37.     $CC01, $0CC0, $0D80, $CD41, $0F00, $CFC1, $CE81, $0E40,
  38.     $0A00, $CAC1, $CB81, $0B40, $C901, $09C0, $0880, $C841,
  39.     $D801, $18C0, $1980, $D941, $1B00, $DBC1, $DA81, $1A40,
  40.     $1E00, $DEC1, $DF81, $1F40, $DD01, $1DC0, $1C80, $DC41,
  41.     $1400, $D4C1, $D581, $1540, $D701, $17C0, $1680, $D641,
  42.     $D201, $12C0, $1380, $D341, $1100, $D1C1, $D081, $1040,
  43.     $F001, $30C0, $3180, $F141, $3300, $F3C1, $F281, $3240,
  44.     $3600, $F6C1, $F781, $3740, $F501, $35C0, $3480, $F441,
  45.     $3C00, $FCC1, $FD81, $3D40, $FF01, $3FC0, $3E80, $FE41,
  46.     $FA01, $3AC0, $3B80, $FB41, $3900, $F9C1, $F881, $3840,
  47.     $2800, $E8C1, $E981, $2940, $EB01, $2BC0, $2A80, $EA41,
  48.     $EE01, $2EC0, $2F80, $EF41, $2D00, $EDC1, $EC81, $2C40,
  49.     $E401, $24C0, $2580, $E541, $2700, $E7C1, $E681, $2640,
  50.     $2200, $E2C1, $E381, $2340, $E101, $21C0, $2080, $E041,
  51.     $A001, $60C0, $6180, $A141, $6300, $A3C1, $A281, $6240,
  52.     $6600, $A6C1, $A781, $6740, $A501, $65C0, $6480, $A441,
  53.     $6C00, $ACC1, $AD81, $6D40, $AF01, $6FC0, $6E80, $AE41,
  54.     $AA01, $6AC0, $6B80, $AB41, $6900, $A9C1, $A881, $6840,
  55.     $7800, $B8C1, $B981, $7940, $BB01, $7BC0, $7A80, $BA41,
  56.     $BE01, $7EC0, $7F80, $BF41, $7D00, $BDC1, $BC81, $7C40,
  57.     $B401, $74C0, $7580, $B541, $7700, $B7C1, $B681, $7640,
  58.     $7200, $B2C1, $B381, $7340, $B101, $71C0, $7080, $B041,
  59.     $5000, $90C1, $9181, $5140, $9301, $53C0, $5280, $9241,
  60.     $9601, $56C0, $5780, $9741, $5500, $95C1, $9481, $5440,
  61.     $9C01, $5CC0, $5D80, $9D41, $5F00, $9FC1, $9E81, $5E40,
  62.     $5A00, $9AC1, $9B81, $5B40, $9901, $59C0, $5880, $9841,
  63.     $8801, $48C0, $4980, $8941, $4B00, $8BC1, $8A81, $4A40,
  64.     $4E00, $8EC1, $8F81, $4F40, $8D01, $4DC0, $4C80, $8C41,
  65.     $4400, $84C1, $8581, $4540, $8701, $47C0, $4680, $8641,
  66.     $8201, $42C0, $4380, $8341, $4100, $81C1, $8081, $4040 );
  67.  
  68. type long    = record           { used to simulate long (4 byte) integers }
  69.                  l, h : integer
  70.                end;
  71.  
  72. type strtype = string[strlen];
  73.      fntype  = array [0..fnlen] of char;
  74.      buftype = array [1..BLOCKSIZE] of byte;
  75.      heads   = record
  76.                  name   : fntype;
  77.                  size   : long;
  78.                  date   : integer;
  79.                  time   : integer;
  80.                  crc    : integer;
  81.                  length : long
  82.                end;
  83.  
  84. var hdrver   : byte;
  85.     arcfile  : file;
  86.     arcbuf   : buftype;
  87.     arcptr   : integer;
  88.     arcname  : strtype;
  89.     endfile  : boolean;
  90.  
  91.     extfile  : file;
  92.     extbuf   : buftype;
  93.     extptr   : integer;
  94.     extname  : strtype;
  95.  
  96. { definitions for unpack }
  97.  
  98. const DLE = $90;
  99.  
  100. var state  : (NOHIST, INREP);
  101.     crcval : integer;
  102.     size   : real;
  103.     lastc  : integer;
  104.  
  105. { definitions for unsqueeze }
  106.  
  107. const ERROR   = -1;
  108.       SPEOF   = 256;
  109.       NUMVALS = 256;               { 1 less than the number of values }
  110.  
  111. type nd = record
  112.             child : array [0..1] of integer
  113.           end;
  114.  
  115. var node     : array [0..NUMVALS] of nd;
  116.     bpos     : integer;
  117.     curin    : integer;
  118.     numnodes : integer;
  119.  
  120. { definitions for uncrunch }
  121.  
  122. const TABSIZE   = 4096;
  123.       TABSIZEM1 = 4095;
  124.       NO_PRED   = $FFFF;
  125.       EMPTY     = $FFFF;
  126.  
  127. type entry = record
  128.                used         : boolean;
  129.                next         : integer;
  130.                predecessor  : integer;
  131.                follower     : byte
  132.              end;
  133.  
  134. var stack       : array [0..TABSIZEM1] of byte;
  135.     sp          : integer;
  136.     string_tab  : array [0..TABSIZEM1] of entry;
  137.  
  138. var code_count : integer;
  139.     code       : integer;
  140.     firstc     : boolean;
  141.     oldcode    : integer;
  142.     finchar    : integer;
  143.     inbuf      : integer;
  144.     outbuf     : integer;
  145.  
  146. procedure abort(s : strtype);
  147. { terminate the program with an error message }
  148.   begin
  149.     writeln('ABORT: ', s);
  150.     halt;
  151.   end;
  152.  
  153. function fn_to_str(var fn : fntype) : strtype;
  154. { convert strings from C format (trailing 0) to Turbo Pascal format (leading
  155.     length byte). }
  156.   var s : strtype;
  157.       i : integer;
  158.   begin
  159.     s := '';
  160.     i := 0;
  161.     while fn[i] <> #0 do begin
  162.       s := s + fn[i];
  163.       i := i + 1
  164.     end;
  165.     fn_to_str := s
  166.   end;
  167.  
  168. function unsigned_to_real(u : integer) : real;
  169. { convert unsigned integer to real }
  170. { note: INT is a function that returns a REAL!!!}
  171.   begin
  172.     if u >= 0 then unsigned_to_real := Int(u)
  173.     else if u = $8000 then unsigned_to_real := 32768.0
  174.     else unsigned_to_real := 65536.0 + u
  175.   end;
  176.  
  177. function long_to_real(l : long) : real;
  178. { convert long integer to a real }
  179. { note: INT is a function that returns a REAL!!! }
  180.   var r : real;
  181.       s : (POS, NEG);
  182.   const rcon = 65536.0;
  183.   begin
  184.     if l.h >= 0 then begin
  185.       r := Int(l.h) * rcon;
  186.       s := POS
  187.     end else begin
  188.       s := NEG;
  189.       if l.h = $8000 then r := rcon * rcon
  190.       else r := Int(-l.h) * rcon
  191.     end;
  192.     r := r + unsigned_to_real(l.l);
  193.     if s = NEG then long_to_real := -r
  194.     else long_to_real := r
  195.   end;
  196.  
  197. procedure Read_Block;
  198. { read a block from the archive file }
  199.   begin
  200.     if EOF(arcfile) then endfile := TRUE
  201.     else BlockRead(arcfile, arcbuf, 1);
  202.     arcptr := 1
  203.   end;
  204.  
  205. procedure Write_Block;
  206. { write a block to the extracted file }
  207.   begin
  208.     BlockWrite(extfile, extbuf, 1);
  209.     extptr := 1
  210.   end;
  211.  
  212. procedure open_arc;
  213. { open the archive file for input processing }
  214.   begin
  215.     {$I-} assign(arcfile, arcname); {$I+}
  216.     if ioresult <> 0 then abort('Cannot open archive file.');
  217.     {$I-} reset(arcfile); {$I+}
  218.     if ioresult <> 0 then abort('Cannot open archive file.');
  219.     endfile := FALSE;
  220.     Read_Block
  221.   end;
  222.  
  223. procedure open_ext;
  224. { open the extracted file for writing }
  225.   begin
  226.     {$I-} assign(extfile, extname); {$I+}
  227.     if ioresult <> 0 then abort('Cannot open extract file.');
  228.     {$I-} rewrite(extfile); {$I+}
  229.     if ioresult <> 0 then abort('Cannot open extract file.');
  230.     extptr := 1;
  231.   end;
  232.  
  233. function get_arc : byte;
  234. { read 1 character from the archive file }
  235.   begin
  236.     if endfile then get_arc := 0
  237.     else begin
  238.       get_arc := arcbuf[arcptr];
  239.       if arcptr = BLOCKSIZE then Read_Block
  240.       else arcptr := arcptr + 1
  241.     end
  242.   end;
  243.  
  244. procedure put_ext(c : byte);
  245. { write 1 character to the extracted file }
  246.   begin
  247.     extbuf[extptr] := c;
  248.     if extptr = BLOCKSIZE then Write_Block
  249.     else extptr := extptr + 1
  250.   end;
  251.  
  252. procedure close_arc;
  253. { close the archive file }
  254.   begin
  255.     close(arcfile)
  256.   end;
  257.  
  258. procedure close_ext;
  259. { close the extracted file }
  260.   begin
  261.     while extptr <> 1 do put_ext(Ord(^Z));  { pad last block w/ Ctrl-Z (EOF) }
  262.     close(extfile)
  263.   end;
  264.  
  265. procedure fseek(offset : real; base : integer);
  266. { re-position the current pointer in the archive file }
  267.   var b           : real;
  268.       i, ofs, rec : integer;
  269.       c           : byte;
  270.   begin
  271.     case base of
  272.       0 : b := offset;
  273.       1 : b := offset + (unsigned_to_real(FilePos(arcfile)) - 1.0) * BLOCKSIZE
  274.                 + arcptr - 1.0;
  275.       2 : b := offset + unsigned_to_real(FileSize(arcfile)) * BLOCKSIZE - 1.0
  276.     else
  277.       abort('Invalid parameters to fseek')
  278.     end;
  279.     rec := Trunc(b / BLOCKSIZE);
  280.     ofs := Trunc(b - (Int(rec) * BLOCKSIZE));  { Int converts to Real }
  281.     seek(arcfile, rec);
  282.     Read_Block;
  283.     for i := 1 to ofs do c := get_arc
  284.   end;
  285.  
  286. procedure fread(var buf; reclen : integer);
  287. { read a record from the archive file }
  288.   var i : integer;
  289.       b : array [1..MaxInt] of byte absolute buf;
  290.   begin
  291.     for i := 1 to reclen do b[i] := get_arc
  292.   end;
  293.  
  294. procedure GetArcName;
  295. { get the name of the archive file }
  296.   var i : integer;
  297.   begin
  298.     if ParamCount > 1 then abort('Too many parameters');
  299.     if ParamCount = 1 then arcname := ParamStr(1)
  300.     else begin
  301.       write('Enter archive filename: ');
  302.       readln(arcname);
  303.       if arcname = '' then abort('No file name entered');
  304.       writeln;
  305.       writeln;
  306.     end;
  307.     for i := 1 to length(arcname) do
  308.       arcname[i] := UpCase(arcname[i]);
  309.     if pos('.', arcname) = 0 then arcname := arcname + '.ARC'
  310.   end;
  311.  
  312. function readhdr(var hdr : heads) : boolean;
  313. { read a file header from the archive file }
  314. { FALSE = eof found; TRUE = header found }
  315.   var name : fntype;
  316.       try  : integer;
  317.   begin
  318.     try := 10;
  319.     if endfile then begin
  320.       readhdr := FALSE;
  321.       exit
  322.     end;
  323.     while get_arc <> arcmarc do begin
  324.       if try = 0 then abort(arcname + ' is not an archive');
  325.       try := try - 1;
  326.       writeln(arcname, ' is not an archive, or is out of sync');
  327.       if endfile then abort('Archive length error')
  328.     end;
  329.  
  330.     hdrver := get_arc;
  331.     if hdrver < 0 then abort('Invalid header in archive ' + arcname);
  332.     if hdrver = 0 then begin   { special end of file marker }
  333.       readhdr := FALSE;
  334.       exit
  335.     end;
  336.     if hdrver > arcver then begin
  337.       fread(name, fnlen);
  338.       writeln('I dont know how to handle file ', fn_to_str(name),
  339.         ' in archive ', arcname);
  340.       writeln('I think you need a newer version of DEARC.');
  341.       halt;
  342.     end;
  343.  
  344.     if hdrver = 1 then begin
  345.       fread(hdr, sizeof(heads) - sizeof(long));
  346.       hdrver := 2;
  347.       hdr.length := hdr.size
  348.     end else
  349.       fread(hdr, sizeof(heads));
  350.  
  351.     readhdr := TRUE
  352.   end;
  353.  
  354. procedure putc_unp(c : integer);
  355.   begin
  356.     crcval := ((crcval shr 8) and $00FF) xor crctab[(crcval xor c) and $00FF];
  357.     put_ext(c)
  358.   end;
  359.  
  360. procedure putc_ncr(c : integer);
  361.   begin
  362.     case state of
  363.       NOHIST : if c = DLE then state := INREP
  364.                else begin
  365.                  lastc := c;
  366.                  putc_unp(c)
  367.                end;
  368.       INREP  : begin
  369.                  if c = 0 then putc_unp(DLE)
  370.                  else begin
  371.                    c := c - 1;
  372.                    while (c <> 0) do begin
  373.                      putc_unp(lastc);
  374.                      c := c - 1
  375.                    end
  376.                  end;
  377.                  state := NOHIST
  378.                end
  379.     end
  380.   end;
  381.  
  382. function getc_unp : integer;
  383.   begin
  384.     if size = 0.0 then getc_unp := -1
  385.     else begin
  386.       size := size - 1.0;
  387.       getc_unp := get_arc
  388.     end
  389.   end;
  390.  
  391. procedure init_usq;
  392. { initialize for unsqueeze }
  393.   var i : integer;
  394.   begin
  395.     bpos := 99;
  396.     fread(numnodes, sizeof(numnodes));
  397.     if (numnodes < 0) or (numnodes > NUMVALS) then
  398.       abort('File has an invalid decode tree');
  399.     node[0].child[0] := -(SPEOF + 1);
  400.     node[0].child[1] := -(SPEOF + 1);
  401.     for i := 0 to numnodes-1 do begin
  402.       fread(node[i].child[0], sizeof(integer));
  403.       fread(node[i].child[1], sizeof(integer))
  404.     end
  405.   end;
  406.  
  407. function getc_usq : integer;
  408. { unsqueeze }
  409.   var i : integer;
  410.   begin
  411.     i := 0;
  412.     while i >= 0 do begin
  413.       bpos := bpos + 1;
  414.       if bpos > 7 then begin
  415.         curin := getc_unp;
  416.         if curin = ERROR then begin
  417.           getc_usq := ERROR;
  418.           exit
  419.         end;
  420.         bpos := 0;
  421.         i := node[i].child[1 and curin]
  422.       end else begin
  423.         curin := curin shr 1;
  424.         i := node[i].child[1 and curin]
  425.       end
  426.     end;
  427.     i := - (i + 1);
  428.     if i = SPEOF then getc_usq := -1
  429.     else getc_usq := i
  430.   end;
  431.  
  432. function h(pred, foll : integer) : integer;
  433. { calculate hash value }
  434. { thanks to Bela Lubkin }
  435.   var Local : Real;
  436.       S     : String[20];
  437.       I, V  : integer;
  438.       C     : char;
  439.   begin
  440.     Local := (pred + foll) or $0800;
  441.     if Local < 0.0 then Local := Local + 65536.0;
  442.     Local := (Local * Local) / 64.0;
  443.  
  444. { convert Local to an integer, truncating high order bits. }
  445. { there ***MUST*** be a better way to do this!!! }
  446.     Str(Local:15:5, S);
  447.     V := 0;
  448.     I := 1;
  449.     C := S[1];
  450.     while C <> '.' do begin
  451.       if (C >= '0') and (C <= '9') then V := V * 10 + (Ord(C) - Ord('0'));
  452.       I := I + 1;
  453.       C := S[I]
  454.     end;
  455.     h := V and $0FFF
  456.   end;
  457.  
  458. function eolist(index : integer) : integer;
  459.   var temp : integer;
  460.   begin
  461.     temp := string_tab[index].next;
  462.     while temp <> 0 do begin
  463.       index := temp;
  464.       temp := string_tab[index].next
  465.     end;
  466.     eolist := index
  467.   end;
  468.  
  469. function hash(pred, foll : integer) : integer;
  470.   var local     : integer;
  471.       tempnext  : integer;
  472.   begin
  473.     local := h(pred, foll);
  474.     if not string_tab[local].used then hash := local
  475.     else begin
  476.       local := eolist(local);
  477.       tempnext := (local + 101) and $0FFF;
  478.       while string_tab[tempnext].used do begin
  479.         tempnext := tempnext + 1;
  480.         if tempnext = TABSIZE then tempnext := 0
  481.       end;
  482.       string_tab[local].next := tempnext;
  483.       hash := tempnext
  484.     end
  485.   end;
  486.  
  487. procedure upd_tab(pred, foll : integer);
  488.   begin
  489.     with string_tab[hash(pred, foll)] do begin
  490.       used := TRUE;
  491.       next := 0;
  492.       predecessor := pred;
  493.       follower := foll
  494.     end
  495.   end;
  496.  
  497. function getcode : integer;
  498.   var localbuf  : integer;
  499.       returnval : integer;
  500.   begin
  501.     if inbuf = EMPTY then begin
  502.       localbuf := getc_unp;
  503.       if localbuf = -1 then begin
  504.         getcode := -1;
  505.         exit
  506.       end;
  507.       localbuf := localbuf and $00FF;
  508.       inbuf := getc_unp;
  509.       if inbuf = -1 then begin
  510.         getcode := -1;
  511.         exit
  512.       end;
  513.       inbuf := inbuf and $00FF;
  514.       returnval := ((localbuf shl 4) and $0FF0) + ((inbuf shr 4) and $000F);
  515.       inbuf := inbuf and $000F
  516.     end else begin
  517.       localbuf := getc_unp;
  518.       if localbuf = -1 then begin
  519.         getcode := -1;
  520.         exit
  521.       end;
  522.       localbuf := localbuf and $00FF;
  523.       returnval := localbuf + ((inbuf shl 8) and $0F00);
  524.       inbuf := EMPTY
  525.     end;
  526.     getcode := returnval
  527.   end;
  528.  
  529. procedure push(c : integer);
  530.   begin
  531.     stack[sp] := c;
  532.     sp := sp + 1;
  533.     if sp >= TABSIZE then abort('Stack overflow')
  534.   end;
  535.  
  536. function pop : integer;
  537.   begin
  538.     if sp > 0 then begin
  539.       sp := sp - 1;
  540.       pop := stack[sp]
  541.     end else
  542.       pop := EMPTY
  543.   end;
  544.  
  545. procedure init_tab;
  546.   var i : integer;
  547.   begin
  548.     FillChar(string_tab, sizeof(string_tab), 0);
  549.     for i := 0 to 255 do upd_tab(NO_PRED, i);
  550.     inbuf := EMPTY;
  551.     outbuf := EMPTY
  552.   end;
  553.  
  554. procedure init_ucr;
  555.   begin
  556.     sp := 0;
  557.     init_tab;
  558.     code_count := TABSIZE - 256;
  559.     firstc := TRUE
  560.   end;
  561.  
  562. function getc_ucr : integer;
  563.   var c       : integer;
  564.       code    : integer;
  565.       newcode : integer;
  566.   begin
  567.     if firstc then begin
  568.       firstc := FALSE;
  569.       oldcode := getcode;
  570.       finchar := string_tab[oldcode].follower;
  571.       getc_ucr := finchar;
  572.       exit
  573.     end;
  574.     if sp = 0 then begin
  575.       newcode := getcode;
  576.       code := newcode;
  577.       if code = -1 then begin
  578.         getc_ucr := -1;
  579.         exit
  580.       end;
  581.       if not string_tab[code].used then begin
  582.         code := oldcode;
  583.         push(finchar)
  584.       end;
  585.       while string_tab[code].predecessor <> NO_PRED do
  586.         with string_tab[code] do begin
  587.           push(follower);
  588.           code := predecessor
  589.         end;
  590.       finchar := string_tab[code].follower;
  591.       push(finchar);
  592.       if code_count <> 0 then begin
  593.         upd_tab(oldcode, finchar);
  594.         code_count := code_count - 1
  595.       end;
  596.       oldcode := newcode
  597.     end;
  598.     getc_ucr := pop
  599.   end;
  600.  
  601. procedure unpack(var hdr : heads);
  602.   var c : integer;
  603.   begin
  604.     crcval := 0;
  605.     size := long_to_real(hdr.size);
  606.     state := NOHIST;
  607.     case hdrver of
  608.       1, 2 : begin
  609.                c := getc_unp;
  610.                while c <> -1 do begin
  611.                  putc_unp(c);
  612.                  c := getc_unp
  613.                end
  614.              end;
  615.       3    : begin
  616.                c := getc_unp;
  617.                while c <> -1 do begin
  618.                  putc_ncr(c);
  619.                  c := getc_unp
  620.                end
  621.              end;
  622.       4    : begin
  623.                init_usq;
  624.                c := getc_usq;
  625.                while c <> -1 do begin
  626.                  putc_ncr(c);
  627.                  c := getc_usq
  628.                end
  629.              end;
  630.       5    : begin
  631.                init_ucr;
  632.                c := getc_ucr;
  633.                while c <> -1 do begin
  634.                  putc_unp(c);
  635.                  c := getc_ucr
  636.                end
  637.              end;
  638.       6    : begin
  639.                init_ucr;
  640.                c := getc_ucr;
  641.                while c <> -1 do begin
  642.                  putc_ncr(c);
  643.                  c := getc_ucr
  644.                end
  645.              end
  646.     else
  647.       writeln('I dont know how to unpack file ', fn_to_str(hdr.name));
  648.       writeln('I think you need a newer version of DEARC');
  649.       fseek(long_to_real(hdr.size), 1);
  650.       exit
  651.     end;
  652.     if crcval <> hdr.crc then
  653.       writeln('WARNING: File ', fn_to_str(hdr.name), ' fails CRC check')
  654.   end;
  655.  
  656. procedure extract_file(var hdr : heads);
  657.   begin
  658.     extname := fn_to_str(hdr.name);
  659.     writeln('Extracting file : ', extname);
  660.     open_ext;
  661.     unpack(hdr);
  662.     close_ext
  663.   end;
  664.  
  665. procedure extarc;
  666.   var hdr : heads;
  667.   begin
  668.     open_arc;
  669.     while readhdr(hdr) do extract_file(hdr);
  670.     close_arc
  671.   end;
  672.  
  673. procedure PrintHeading;
  674.   begin
  675.     writeln;
  676.     writeln('Turbo Pascal DEARC Utility');
  677.     writeln('Version 1.01, 10/19/85');
  678.     writeln
  679.   end;
  680.  
  681. begin
  682.   PrintHeading; { print a heading }
  683.   GetArcName;   { get the archive file name }
  684.   extarc        { extract all files from the archive }
  685. end.
  686.