{$R-}
{$U-}
{$C-}
{$K-}

program dearc512;

{ REVISION -  Now supports ARC 5.12 and earlier files - 6-10-86 by DWC }

{ DEARC.PAS - Program to extract all files from an archive created by version
  5.12 or earlier of the ARC utility.

  ARC is COPYRIGHT 1985 by System Enhancement Associates.

  This program requires Turbo Pascal Version 3.01A. It should work in all
  supported environments (PCDOS, CPM, etc.) but I have only tested it on
  an IBM PC running PC DOS version 3.10.

  Usage:

    DEARC arcname

    arcname is the path/file name of the archive file. All files contained
    in the archive will be extracted into the current directory.

   *** ORIGINAL AUTHOR UNKNOWN ***

  Version 1.01 - 10/19/85. Changed end-of-file processing to, hopefully, be
                           more compatible with CPM (whatever that is).
  Version 1.01A - 12/19/85 By Roy Collins
                           Mail: TechMail BBS @ 703-430-2535
                                 - or -
                                 P.O.Box 1192, Leesburg, Va 22075
                           Modified V1.01 to work with Turbo Pascal Version 2
                           Added functions ARGC (argument count) and ARGV
                           (argument value)
                           Modified all references to "EXIT" command to be
                           GOTO EXIT, with EXIT defined as a LABEL, at the
                           end of the function/procedure involved.
                           Will not accept path names - archives must be in
                           the current directory.
  Version 2.00 - 6/11/86   By David W. Carroll
                           Mail: High Sierra RBBS-PC @ 209/296-3534
                           Now supports ARC version 5.12 files, compression
                           types 7 and 8.
}
(************************* ARGC & ARGV functions **************************)
type
    arglist_string = string[100];
const
    arglist_max = 20;
    arglist_number : integer = -1;
var
    argvlist : array[1..arglist_max] of ^arglist_string;

function argv(num : integer) : arglist_string;
var
  argument : arglist_string absolute cseg:$80;
  newparm,
  parmline : arglist_string;
  i,
  j        : integer;
  state    : (leading_ws, non_quote, quoted, end_quote);
  inchar   : char;

  procedure saveparm;
  begin
    if arglist_number < arglist_max then begin
      arglist_number := arglist_number+1;
      new(argvlist[arglist_number]);
      argvlist[arglist_number]^ := newparm;
      newparm := '';
      end;
  end; (* proc saveparm *)

begin
  if arglist_number = -1 then begin
    arglist_number := 0;
    parmline := argument+' ';
    state := leading_ws;
    newparm := '';
    for i := 1 to length(parmline) do begin
      inchar := parmline[i];
       case state of
         leading_ws: begin
             if inchar = '''' then
               state := quoted
             else
             if inchar <> ' ' then begin
               newparm := newparm+inchar;
               state := non_quote;
               end;
             end; (* leading_ws *)
         non_quote: begin
             if inchar = ' ' then begin
               saveparm;
               state := leading_ws;
               end
             else
               newparm := newparm+inchar;
             end; (* non_quote *)
         quoted: begin
             if inchar = '''' then
               state := end_quote
             else
                newparm := newparm+inchar;
             end; (* quoted *)
         end_quote: begin
             if inchar = '''' then begin
               newparm := newparm+inchar;
               state := quoted;
               end
             else
             if inchar <> ' ' then begin
               newparm := newparm+inchar;
               state := non_quote;
               end
             else begin
               saveparm;
               state := leading_ws;
               end;
             end; (* end_quote *)
            end; (* case state *)
        end; (* for *)
    end; (* if arglist_number = -1 *)
  if (num > 0) and (num <= arglist_number) then
    argv := argvlist[num]^
  else
    argv := '';
end; (* func argv *)

function argc : integer;
var
  dummy : arglist_string;
begin
  if arglist_number = -1 then
    dummy := argv(1); {force evaluation}
  argc := arglist_number;
end; (* func argc *)
(****************** end of ARGC & ARGV functions **************************)

const BLOCKSIZE = 128;
      arcmarc   = 26;              { special archive marker }
      arcver    = 8;               { max archive header version code }
      strlen    = 100;             { standard string length }
      fnlen     = 12;              { file name length - 1 }

const crctab : array [0..255] of integer =
  ( $0000, $C0C1, $C181, $0140, $C301, $03C0, $0280, $C241,
    $C601, $06C0, $0780, $C741, $0500, $C5C1, $C481, $0440,
    $CC01, $0CC0, $0D80, $CD41, $0F00, $CFC1, $CE81, $0E40,
    $0A00, $CAC1, $CB81, $0B40, $C901, $09C0, $0880, $C841,
    $D801, $18C0, $1980, $D941, $1B00, $DBC1, $DA81, $1A40,
    $1E00, $DEC1, $DF81, $1F40, $DD01, $1DC0, $1C80, $DC41,
    $1400, $D4C1, $D581, $1540, $D701, $17C0, $1680, $D641,
    $D201, $12C0, $1380, $D341, $1100, $D1C1, $D081, $1040,
    $F001, $30C0, $3180, $F141, $3300, $F3C1, $F281, $3240,
    $3600, $F6C1, $F781, $3740, $F501, $35C0, $3480, $F441,
    $3C00, $FCC1, $FD81, $3D40, $FF01, $3FC0, $3E80, $FE41,
    $FA01, $3AC0, $3B80, $FB41, $3900, $F9C1, $F881, $3840,
    $2800, $E8C1, $E981, $2940, $EB01, $2BC0, $2A80, $EA41,
    $EE01, $2EC0, $2F80, $EF41, $2D00, $EDC1, $EC81, $2C40,
    $E401, $24C0, $2580, $E541, $2700, $E7C1, $E681, $2640,
    $2200, $E2C1, $E381, $2340, $E101, $21C0, $2080, $E041,
    $A001, $60C0, $6180, $A141, $6300, $A3C1, $A281, $6240,
    $6600, $A6C1, $A781, $6740, $A501, $65C0, $6480, $A441,
    $6C00, $ACC1, $AD81, $6D40, $AF01, $6FC0, $6E80, $AE41,
    $AA01, $6AC0, $6B80, $AB41, $6900, $A9C1, $A881, $6840,
    $7800, $B8C1, $B981, $7940, $BB01, $7BC0, $7A80, $BA41,
    $BE01, $7EC0, $7F80, $BF41, $7D00, $BDC1, $BC81, $7C40,
    $B401, $74C0, $7580, $B541, $7700, $B7C1, $B681, $7640,
    $7200, $B2C1, $B381, $7340, $B101, $71C0, $7080, $B041,
    $5000, $90C1, $9181, $5140, $9301, $53C0, $5280, $9241,
    $9601, $56C0, $5780, $9741, $5500, $95C1, $9481, $5440,
    $9C01, $5CC0, $5D80, $9D41, $5F00, $9FC1, $9E81, $5E40,
    $5A00, $9AC1, $9B81, $5B40, $9901, $59C0, $5880, $9841,
    $8801, $48C0, $4980, $8941, $4B00, $8BC1, $8A81, $4A40,
    $4E00, $8EC1, $8F81, $4F40, $8D01, $4DC0, $4C80, $8C41,
    $4400, $84C1, $8581, $4540, $8701, $47C0, $4680, $8641,
    $8201, $42C0, $4380, $8341, $4100, $81C1, $8081, $4040 );

type long    = record           { used to simulate long (4 byte) integers }
                 l, h : integer
               end;

type strtype = string[strlen];
     fntype  = array [0..fnlen] of char;
     buftype = array [1..BLOCKSIZE] of byte;
     heads   = record
                 name   : fntype;
                 size   : long;
                 date   : integer;
                 time   : integer;
                 crc    : integer;
                 length : long
               end;

var hdrver   : byte;
    arcfile  : file;
    arcbuf   : buftype;
    arcptr   : integer;
    arcname  : strtype;
    endfile  : boolean;

    extfile  : file;
    extbuf   : buftype;
    extptr   : integer;
    extname  : strtype;

{ definitions for unpack }

const DLE = $90;

var state  : (NOHIST, INREP);
    crcval : integer;
    size   : real;
    lastc  : integer;

{ definitions for unsqueeze }

const ERROR   = -1;
      SPEOF   = 256;
      NUMVALS = 256;               { 1 less than the number of values }

type nd = record
            child : array [0..1] of integer
          end;

var node     : array [0..NUMVALS] of nd;
    bpos     : integer;
    curin    : integer;
    numnodes : integer;

{ definitions for uncrunch }

const TABSIZE   = 4096;
      TABSIZEM1 = 4095;
      NO_PRED   = $FFFF;
      EMPTY     = $FFFF;

type entry = record
               used         : boolean;
               next         : integer;
               predecessor  : integer;
               follower     : byte
             end;

var stack       : array [0..TABSIZEM1] of byte;
    sp          : integer;
    string_tab  : array [0..TABSIZEM1] of entry;

var code_count : integer;
    code       : integer;
    firstc     : boolean;
    oldcode    : integer;
    finchar    : integer;
    inbuf      : integer;
    outbuf     : integer;
    newhash    : boolean;

{ definitions for dynamic uncrunch }

const
  BITS = 12;
  HSIZE = 5003;
  INIT_BITS = 9;
  FIRST = 257;
  CLEAR = 256;
  HSIZEM1 = 5002;
  BITSM1 = 11;

  RMASK : array[0..8] of byte =
  ($00, $01, $03, $07, $0f, $1f, $3f, $7f, $ff);

var
  n_bits,
  maxcode : integer;
  prefix : array[0..HSIZEM1] of integer;
  suffix : array[0..TABSIZEM1] of byte;
  buf : array[0..BITSM1] of byte;
  clear_flg : integer;
  stack1 : array[0..HSIZEM1] of byte;
  free_ent : integer;
  maxcodemax : integer;
  offset, sizex : integer;
  firstch : boolean;

procedure abort(s : strtype);
{ terminate the program with an error message }
begin
  writeln('ABORT: ', s);
  halt;
end; (* proc abort *)

function fn_to_str(var fn : fntype) : strtype;
{ convert strings from C format (trailing 0) to Turbo Pascal format (leading
    length byte). }
var s : strtype;
    i : integer;
begin
  s := '';
  i := 0;
  while fn[i] <> #0 do begin
    s := s + fn[i];
    i := i + 1
    end;
  fn_to_str := s
end; (* func fn_to_str *)

function unsigned_to_real(u : integer) : real;
{ convert unsigned integer to real }
{ note: INT is a function that returns a REAL!!!}
begin
  if u >= 0 then
    unsigned_to_real := Int(u)
  else
  if u = $8000 then
    unsigned_to_real := 32768.0
  else
    unsigned_to_real := 65536.0 + u
end; (* func unsigned_to_real *)

function long_to_real(l : long) : real;
{ convert long integer to a real }
{ note: INT is a function that returns a REAL!!! }
var r : real;
    s : (POS, NEG);
const rcon = 65536.0;
begin
  if l.h >= 0 then begin
    r := Int(l.h) * rcon;
    s := POS
    end
  else begin
    s := NEG;
    if l.h = $8000 then
      r := rcon * rcon
    else
      r := Int(-l.h) * rcon
    end;
  r := r + unsigned_to_real(l.l);
  if s = NEG then
    long_to_real := -r
  else
    long_to_real := r
end; (* func long_to_real *)

procedure Read_Block;
{ read a block from the archive file }
begin
  if EOF(arcfile) then
    endfile := TRUE
  else
    BlockRead(arcfile, arcbuf, 1);
  arcptr := 1
end; (* proc read_block *)

procedure Write_Block;
{ write a block to the extracted file }
begin
  BlockWrite(extfile, extbuf, 1);
  extptr := 1
end; (* proc write_block *)

procedure open_arc;
{ open the archive file for input processing }
begin
  {$I-} assign(arcfile, arcname); {$I+}
  if ioresult <> 0 then
    abort('Cannot open archive file.');
  {$I-} reset(arcfile); {$I+}
  if ioresult <> 0 then
    abort('Cannot open archive file.');
  endfile := FALSE;
  Read_Block
end; (* proc open_arc *)

procedure open_ext;
{ open the extracted file for writing }
begin
  {$I-} assign(extfile, extname); {$I+}
  if ioresult <> 0 then
    abort('Cannot open extract file.');
  {$I-} rewrite(extfile); {$I+}
  if ioresult <> 0 then
    abort('Cannot open extract file.');
  extptr := 1;
end; (* proc open_ext *)

function get_arc : byte;
{ read 1 character from the archive file }
begin
  if endfile then
    get_arc := 0
  else begin
    get_arc := arcbuf[arcptr];
    if arcptr = BLOCKSIZE then
      Read_Block
    else
      arcptr := arcptr + 1
    end
end; (* func get_arc *)

procedure put_ext(c : byte);
{ write 1 character to the extracted file }
begin
  extbuf[extptr] := c;
  if extptr = BLOCKSIZE then
    Write_Block
  else
    extptr := extptr + 1
end; (* proc put_ext *)

procedure close_arc;
{ close the archive file }
begin
  close(arcfile)
end; (* proc close_arc *)

procedure close_ext;
{ close the extracted file }
begin
  while extptr <> 1 do
    put_ext(Ord(^Z));          { pad last block w/ Ctrl-Z (EOF) }
  close(extfile)
end; (* proc close_ext *)

procedure fseek(offset : real; base : integer);
{ re-position the current pointer in the archive file }
var b           : real;
    i, ofs, rec : integer;
    c           : byte;
begin
  case base of
    0 : b := offset;
    1 : b := offset + (unsigned_to_real(FilePos(arcfile)) - 1.0) * BLOCKSIZE
              + arcptr - 1.0;
    2 : b := offset + unsigned_to_real(FileSize(arcfile)) * BLOCKSIZE - 1.0
    else
      abort('Invalid parameters to fseek')
    end;
  rec := Trunc(b / BLOCKSIZE);
  ofs := Trunc(b - (Int(rec) * BLOCKSIZE));  { Int converts to Real }
  seek(arcfile, rec);
  Read_Block;
  for i := 1 to ofs do
    c := get_arc
end; (* proc fseek *)

procedure fread(var buf; reclen : integer);
{ read a record from the archive file }
var i : integer;
    b : array [1..MaxInt] of byte absolute buf;
begin
  for i := 1 to reclen do
    b[i] := get_arc
end; (* proc fread *)

procedure GetArcName;
{ get the name of the archive file }
var i : integer;
begin
(*****************************************
  if ParamCount > 1 then
    abort('Too many parameters');
  if ParamCount = 1 then
    arcname := ParamStr(1)
*****************************************)
  if argc > 1 then
    abort('Too many parameters');
  if argc = 1 then
    arcname := argv(1)
  else begin
    write('Enter archive filename: ');
    readln(arcname);
    if arcname = '' then
      abort('No file name entered');
    writeln;
    writeln;
    end;
  for i := 1 to length(arcname) do
    arcname[i] := UpCase(arcname[i]);
  if pos('.', arcname) = 0 then
    arcname := arcname + '.ARC'
end; (* proc GetArcName *)

function readhdr(var hdr : heads) : boolean;
{ read a file header from the archive file }
{ FALSE = eof found; TRUE = header found }
label exit;
var name : fntype;
    try  : integer;
begin
  try := 10;
  if endfile then begin
    readhdr := FALSE;
    goto exit               (******** was "exit" ************)
    end;
  while get_arc <> arcmarc do begin
    if try = 0 then
      abort(arcname + ' is not an archive');
    try := try - 1;
    writeln(arcname, ' is not an archive, or is out of sync');
    if endfile then
      abort('Archive length error')
    end; (* while *)

  hdrver := get_arc;
  if hdrver < 0 then
    abort('Invalid header in archive ' + arcname);
  if hdrver = 0 then begin   { special end of file marker }
    readhdr := FALSE;
    goto exit               (******** was "exit" ************)
    end;
  if hdrver > arcver then begin
    fread(name, fnlen);
    writeln('I dont know how to handle file ', fn_to_str(name),
            ' in archive ', arcname);
    writeln('I think you need a newer version of DEARC.');
    halt;
    end;

  if hdrver = 1 then begin
    fread(hdr, sizeof(heads) - sizeof(long));
    hdrver := 2;
    hdr.length := hdr.size
    end
  else
    fread(hdr, sizeof(heads));

  readhdr := TRUE;
exit:
end; (* func readhdr *)

procedure putc_unp(c : integer);
begin
  crcval := ((crcval shr 8) and $00FF) xor crctab[(crcval xor c) and $00FF];
  put_ext(c)
end; (* proc putc_unp *)

procedure putc_ncr(c : integer);
begin
  case state of
    NOHIST : if c = DLE then
               state := INREP
             else begin
               lastc := c;
               putc_unp(c)
               end;
    INREP  : begin
             if c = 0 then
               putc_unp(DLE)
             else begin
               c := c - 1;
               while (c <> 0) do begin
                 putc_unp(lastc);
                 c := c - 1
                 end
               end;
             state := NOHIST
             end;
    end; (* case *)
end; (* proc putc_ncr *)

function getc_unp : integer;
begin
  if size = 0.0 then
    getc_unp := -1
  else begin
    size := size - 1.0;
    getc_unp := get_arc
    end;
end; (* func getc_unp *)

procedure init_usq;
{ initialize for unsqueeze }
var i : integer;
begin
  bpos := 99;
  fread(numnodes, sizeof(numnodes));
  if (numnodes < 0) or (numnodes > NUMVALS) then
    abort('File has an invalid decode tree');
  node[0].child[0] := -(SPEOF + 1);
  node[0].child[1] := -(SPEOF + 1);
  for i := 0 to numnodes-1 do begin
    fread(node[i].child[0], sizeof(integer));
    fread(node[i].child[1], sizeof(integer))
    end;
end; (* proc init_usq; *)

function getc_usq : integer;
{ unsqueeze }
label exit;
var i : integer;
begin
  i := 0;
  while i >= 0 do begin
    bpos := bpos + 1;
    if bpos > 7 then begin
      curin := getc_unp;
      if curin = ERROR then begin
        getc_usq := ERROR;
        goto exit                   (******** was "exit" ************)
        end;
      bpos := 0;
      i := node[i].child[1 and curin]
      end
    else begin
      curin := curin shr 1;
      i := node[i].child[1 and curin]
      end
    end; (* while *)
  i := - (i + 1);
  if i = SPEOF then
    getc_usq := -1
  else
    getc_usq := i;
  exit:
end; (* func getc_usq *)

function h(pred, foll : integer) : integer;
{ calculate hash value }
{ thanks to Bela Lubkin }
var Local : Real;
    S     : String[20];
    I, V  : integer;
    C     : char;

begin
if not newhash then
begin
  Local := (pred + foll) or $0800;
  if Local < 0.0 then
    Local := Local + 65536.0;
  Local := (Local * Local) / 64.0;

{ convert Local to an integer, truncating high order bits. }
{ there ***MUST*** be a better way to do this!!! }
  Str(Local:15:5, S);
  V := 0;
  I := 1;
  C := S[1];
  while C <> '.' do begin
    if (C >= '0') and (C <= '9') then
      V := V * 10 + (Ord(C) - Ord('0'));
    I := I + 1;
    C := S[I]
    end;
  h := V and $0FFF
end (* func h *)
else
begin
  Local := (pred + foll) * 15073;

{ convert Local to an integer, truncating high order bits. }
{ there ***MUST*** be a better way to do this!!! }
  Str(Local:15:5, S);
  V := 0;
  I := 1;
  C := S[1];
  while C <> '.' do begin
    if (C >= '0') and (C <= '9') then
      V := V * 10 + (Ord(C) - Ord('0'));
    I := I + 1;
    C := S[I]
    end;
  h := V and $0FFF

end;
end;

function eolist(index : integer) : integer;
var temp : integer;
begin
  temp := string_tab[index].next;
  while temp <> 0 do begin
    index := temp;
    temp := string_tab[index].next
    end;
  eolist := index
end; (* func eolist *)

function hash(pred, foll : integer) : integer;
var local     : integer;
    tempnext  : integer;
begin
  local := h(pred, foll);
  if not string_tab[local].used then
    hash := local
  else begin
    local := eolist(local);
    tempnext := (local + 101) and $0FFF;
    while string_tab[tempnext].used do begin
      tempnext := tempnext + 1;
      if tempnext = TABSIZE then
        tempnext := 0
      end;
    string_tab[local].next := tempnext;
    hash := tempnext
    end;
end; (* func hash *)

procedure upd_tab(pred, foll : integer);
begin
  with string_tab[hash(pred, foll)] do begin
    used := TRUE;
    next := 0;
    predecessor := pred;
    follower := foll
    end
end; (* proc upd_tab *)

function gocode : integer;
label exit;
var localbuf  : integer;
    returnval : integer;
begin
  if inbuf = EMPTY then begin
    localbuf := getc_unp;
    if localbuf = -1 then begin
      gocode := -1;
      goto exit                       (******** was "exit" ************)
      end;
    localbuf := localbuf and $00FF;
    inbuf := getc_unp;
    if inbuf = -1 then begin
      gocode := -1;
      goto exit                       (******** was "exit" ************)
      end;
    inbuf := inbuf and $00FF;
    returnval := ((localbuf shl 4) and $0FF0) + ((inbuf shr 4) and $000F);
    inbuf := inbuf and $000F
    end
  else begin
    localbuf := getc_unp;
    if localbuf = -1 then begin
      gocode := -1;
      goto exit                       (******** was "exit" ************)
      end;
    localbuf := localbuf and $00FF;
    returnval := localbuf + ((inbuf shl 8) and $0F00);
    inbuf := EMPTY
    end;
  gocode := returnval;
exit:
end; (* func gocode *)

procedure push(c : integer);
begin
  stack[sp] := c;
  sp := sp + 1;
  if sp >= TABSIZE then
    abort('Stack overflow')
end; (* proc push *)

function pop : integer;
begin
  if sp > 0 then begin
    sp := sp - 1;
    pop := stack[sp]
  end else
    pop := EMPTY
end; (* func pop *)

procedure init_tab;
var i : integer;
begin
  FillChar(string_tab, sizeof(string_tab), 0);
  for i := 0 to 255 do
    upd_tab(NO_PRED, i);
  inbuf := EMPTY;
  { outbuf := EMPTY }
end; (* proc init_tab *)

procedure init_ucr(i:integer);
begin
  newhash := i = 1;
  sp := 0;
  init_tab;
  code_count := TABSIZE - 256;
  firstc := TRUE
end; (* proc init_ucr *)

function getc_ucr : integer;
label exit;
var c       : integer;
    code    : integer;
    newcode : integer;
begin
  if firstc then begin
    firstc := FALSE;
    oldcode := gocode;
    finchar := string_tab[oldcode].follower;
    getc_ucr := finchar;
    goto exit                         (******** was "exit" ************)
    end;
  if sp = 0 then begin
    newcode := gocode;
    code := newcode;
    if code = -1 then begin
      getc_ucr := -1;
      goto exit                       (******** was "exit" ************)
      end;
    if not string_tab[code].used then begin
      code := oldcode;
      push(finchar)
      end;
    while string_tab[code].predecessor <> NO_PRED do
      with string_tab[code] do begin
        push(follower);
        code := predecessor
        end;
    finchar := string_tab[code].follower;
    push(finchar);
    if code_count <> 0 then begin
      upd_tab(oldcode, finchar);
      code_count := code_count - 1
      end;
    oldcode := newcode
    end;
  getc_ucr := pop;
exit:
end; (* func getc_ucr *)

function getcode : integer;
label
  next, exit;
var
  code, r_off, bitsx : integer;
  bp : byte;
begin
  if firstch then
  begin
    offset := 0;
    sizex := 0;
    firstch := false;
  end;
  bp := 0;
  if (clear_flg > 0) or (offset >= sizex) or (free_ent > maxcode) then
  begin
    if free_ent > maxcode then
    begin
      n_bits := n_bits + 1;
      if n_bits = BITS then
        maxcode := maxcodemax
      else
        maxcode := (1 shl n_bits) - 1;
    end;
    if clear_flg > 0 then
    begin
      n_bits := INIT_BITS;
      maxcode := (1 shl n_bits) - 1;
      clear_flg := 0;
    end;
    for sizex := 0 to n_bits-1 do
    begin
      code := getc_unp;
      if code = -1 then
        goto next
      else
        buf[sizex] := code;
    end;
    sizex := sizex + 1;
next:
    if sizex <= 0 then
    begin
      getcode := -1;
      goto exit;
    end;
    offset := 0;
    sizex := (sizex shl 3) - (n_bits - 1);
  end;
  r_off := offset;
  bitsx := n_bits;

  { get first byte }
  bp := bp + (r_off shr 3);
  r_off := r_off and 7;

  { get first parft (low order bits) }
  code := buf[bp] shr r_off;
  bp := bp + 1;
  bitsx := bitsx - (8 - r_off);
  r_off := 8 - r_off;

  if bitsx >= 8 then
  begin
    code := code or (buf[bp] shl r_off);
    bp := bp + 1;
    r_off := r_off + 8;
    bitsx := bitsx - 8;
  end;

  code := code or ((buf[bp] and rmask[bitsx]) shl r_off);
  offset := offset + n_bits;
  getcode := code;
exit:
end;

procedure decomp;
label
  next,exit;
var
  stackp,
  finchar :integer;
  code, oldcode, incode : integer;

begin
  { INIT var }
  if firstch then
    maxcodemax := 1 shl bits;

  code := getc_unp;
  if code <> BITS then
  begin
    writeln('File packed with ',code,' bits, I can only handle ',BITS);
    halt;
  end;
  clear_flg := 0;

  n_bits := INIT_BITS;
  maxcode := (1 shl n_bits ) - 1;
  for code := 255 downto 0 do
  begin
    prefix[code] := 0;
    suffix[code] := code;
  end;

  free_ent := FIRST;
  oldcode := getcode;
  finchar := oldcode;
  if oldcode = -1 then
    goto exit;
  putc_ncr(finchar);
  stackp := 0;

  code := getcode;
  while code  > -1 do
  begin
    if code = CLEAR then
    begin
      for code := 255 downto 0 do
        prefix[code] := 0;
      clear_flg := 1;
      free_ent := FIRST - 1;
      code := getcode;
      if code = -1 then
        goto next;
    end;
next:
    incode := code;
    if code >= free_ent then
    begin
      stack1[stackp] := finchar;
      stackp := stackp + 1;
      code := oldcode;
    end;
    while code >= 256 do
    begin
      stack1[stackp] := suffix[code];
      stackp := stackp + 1;
      code := prefix[code];
    end;
    finchar := suffix[code];
    stack1[stackp] := finchar;
    stackp := stackp + 1;
    repeat
      stackp := stackp - 1;
      putc_ncr(stack1[stackp]);
    until stackp <= 0;
    code := free_ent;
    if code < maxcodemax then
    begin
      prefix[code] := oldcode;
      suffix[code] := finchar;
      free_ent := code + 1;
    end;
    oldcode := incode;
    code := getcode;
  end;
exit:
end;

procedure unpack(var hdr : heads);
label exit;
var c : integer;
begin
  crcval := 0;
  size := long_to_real(hdr.size);
  state := NOHIST;
  case hdrver of
    1, 2 : begin
           c := getc_unp;
           while c <> -1 do begin
             putc_unp(c);
             c := getc_unp
             end
           end;
    3    : begin
           c := getc_unp;
           while c <> -1 do begin
             putc_ncr(c);
             c := getc_unp
             end
           end;
    4    : begin
           init_usq;
           c := getc_usq;
           while c <> -1 do begin
             putc_ncr(c);
             c := getc_usq
             end
           end;
    5    : begin
           init_ucr(0);
           c := getc_ucr;
           while c <> -1 do begin
             putc_unp(c);
             c := getc_ucr
             end
           end;
    6    : begin
           init_ucr(0);
           c := getc_ucr;
           while c <> -1 do begin
             putc_ncr(c);
             c := getc_ucr
             end
           end;
    7    : begin
           init_ucr(1);
           c := getc_ucr;
           while c <> -1 do begin
             putc_ncr(c);
             c := getc_ucr
             end
           end;

    8    : begin
             decomp;
           end;
    else
           writeln('I dont know how to unpack file ', fn_to_str(hdr.name));
           writeln('I think you need a newer version of DEARC');
           fseek(long_to_real(hdr.size), 1);
           goto exit                         (******** was "exit" ************)
    end; (* case *)
  if crcval <> hdr.crc then
    writeln('WARNING: File ', fn_to_str(hdr.name), ' fails CRC check');
exit:
end; (* proc unpack *)

procedure extract_file(var hdr : heads);
begin
  extname := fn_to_str(hdr.name);
  writeln('Extracting file : ', extname);
  open_ext;
  unpack(hdr);
  close_ext
end; (* proc extract *)

procedure extarc;
var hdr : heads;
begin
  open_arc;
  while readhdr(hdr) do
    extract_file(hdr);
  close_arc
end; (* proc extarc *)

procedure PrintHeading;
begin
  writeln;
  writeln('Turbo Pascal DEARC Utility');
  writeln('Version 2.0, 6/11/86');
  writeln('Supports ARC version 5.12 files');
  writeln;
end; (* proc PrintHeading *)

begin
  firstch := true;
  PrintHeading; { print a heading }
  GetArcName;   { get the archive file name }
  extarc        { extract all files from the archive }
end.
