*/
Know a good article or link that we're missing? Submit it!
*/

View \FASTDIR.PAS

FASTDIR is a PASCAL program that will search directory paths

Submitted By: WEBMASTER
Rating: (Not rated) (Rate It)


{ Default Compiler Directives}
{$S-,R-,V-,I-,N-,B-,F-}

{$IFNDEF Ver40}
  {Allow overlays}
  {$F+,O-,X+,A-}
{$ENDIF}

UNIT FASTDIR;

INTERFACE

USES Dos;

CONST

    MaxDirSize = 4096;

    Erased  : WORD = $09;
    Moved   : WORD = $0F;

    ShowFileType : BOOLEAN = FALSE;
    DoFullSearch : BOOLEAN = TRUE{ used for FIND_FILEPATH to search all DIRS }
    NoShow       : WORD = Directory + Hidden + VolumeID;
    CurrentLess  : CHAR = 'n';
    SilentDirStr : PATHSTR = '';    { hidden directory ?? }

TYPE

    FileTypes = (fARC, fPAK, fZIP, fLZH, fARJ, fZOO, fLBR, fCOM, fEXE, fBAT,
                 fSFX, fDIR, fVOL, fOTHER, fERROR);

    DirPtr   = ^DirRec;
    DirRec   = RECORD
                 fType  : FILETYPES;
                 Attr   : WORD;
                 Time   : LONGINT;
                 PSize,
                 Size   : LONGINT;
                 Method,
                 Name   : STRING [12];
                 Path   : PathStr;
                 Tag    : BOOLEAN;
                 Next,
                 Prev   : DirPtr;
               END;

  LessFunc = FUNCTION (X, Y : DirPtr) : BOOLEAN;
  SortPPtr = ^Sortpage;
  SortPage = ARRAY [0..PRED(MaxDirSize)] OF DirPtr;

  DirList  = RECORD
             Root,
             Last,
             Current  : DirPtr;         { Points to Root,Last,Current items }
             Path     : PathStr;        { Dir Path Or Archive Name }
             Mask     : PathStr;        { Command Line or params }
             ArcType  : FILETYPES;      { DIR or Type of Archive }
             Recurse  : BOOLEAN;        { Include SUBS Too }
             Count,
             Tagged   : INTEGER;
             Space,
             TSpace   : LONGINT;
             Less     : LessFunc;       { Sort function }
             END;

  ExtractorRec = RECORD
                 Extract  : PathStr;
                 Compress : PathStr;
                 ListChar : Char;
                 END;

CONST

  Extractors : ARRAY [fARC .. fARJ] OF ExtractorRec = (

    (Extract  : 'ARC.EXE e';
     Compress : 'ARC.EXE a';
     ListChar : #32),

    (Extract  : 'PAK.EXE e /wa';
     Compress : 'PAK.EXE -a';
     ListChar : #32),

    (Extract  : 'PKUNZIP.EXE -o';
     Compress : 'PKZIP.EXE -ex';
     ListChar : '@'),

    (Extract  : 'LHARC.EXE -cm';
     Compress : 'LHARC.EXE a';
     ListChar : #32),

    (Extract  : 'ARJ.EXE e -y';
     Compress : 'ARJ.EXE a';
     ListChar : '!') );

FUNCTION  LessName (X, Y : DirPtr) : BOOLEAN;
FUNCTION  LessExt  (X, Y : DirPtr) : BOOLEAN;
FUNCTION  LessPath (X, Y : DirPtr) : BOOLEAN;
FUNCTION  LessSize (X, Y : DirPtr) : BOOLEAN;
FUNCTION  LessTime (X, Y : DirPtr) : BOOLEAN;
FUNCTION  LessAttr (X, Y : DirPtr) : BOOLEAN;

FUNCTION  FileTypePerExtension(fName : PathStr) : FileTypes;
FUNCTION  FileTypeString (FT : FileTypes) : STRING;
FUNCTION  GetArcType (FName : PathStr) : FileTypes;

PROCEDURE InitializeDir (VAR Dir : DirList);
PROCEDURE FindFiles (VAR Dir : DirList; SearchPath : PathStr);
PROCEDURE SortFiles (VAR Dir : DirList);
PROCEDURE ReleaseFiles (VAR Dir : DirList);
PROCEDURE SetLess (VAR Dir : DirList; LChar : CHAR);
PROCEDURE GetCommandLine (VAR Mask : PathStr);      { Get MASK from command line     }

PROCEDURE UpdateNextPrev (VAR Dir : DirList);
FUNCTION  NthDirItem (VAR Dir : DirList; Item : INTEGER) : DirPtr;

FUNCTION  IsDir(fName : PathStr) : BOOLEAN;
FUNCTION  IsArchive(fName : PathStr) : BOOLEAN;

PROCEDURE ZipView(VAR Dir : DirList; ZIPFile : String){ handle ZIP File }
PROCEDURE ArjView(VAR Dir : DirList; ArjFile : String){ handle ARJ File }
PROCEDURE LzhView(VAR Dir : DirList; LzhFile : String){ handle LZH File }
PROCEDURE ArcView(VAR Dir : DirList; ArcName : PathStr); { handle ARC,PAK File }

PROCEDURE GetFiles(VAR Dir : DirList; Path,Mask : PathStr; Sort : LessFunc);

{ Interfaced for TEST program }
FUNCTION  PadR (InpStr : STRING; FieldLen : BYTE) : STRING;
FUNCTION  PadL(InpStr : STRING; Len : Byte) : STRING;
FUNCTION  FullPathname (Path, FileMask : PathStr) : PathStr;

IMPLEMENTATION

{ ?????????????????????????????????????????????????????????????????????????? }
{ ?                 STRING FUNCTIONS AND PROCEDURES                        ? }
{ ?????????????????????????????????????????????????????????????????????????? }


Procedure StrUpr(Var S: String); Assembler;
Asm
  push    ds              { Save DS on stack }
  lds     si, S           { Load DS:SI With Pointer to S }
  cld                     { Clear direction flag - String instr. Forward }
  lodsb                   { Load first Byte of S (String length Byte) }
  sub     ah, ah          { Clear high Byte of AX }
  mov     cx, ax          { Move AX in CX }
  jcxz    @Done           { Length = 0, done }
  mov     ax, ds          { Set ES to the value in DS through AX }
  mov     es, ax          { (can't move between two segment Registers) }
  mov     di, si          { DI and SI now point to the first Char. }
@UpCase:
  lodsb                   { Load Character }
  cmp     al, 'a'
  jb      @notLower       { below 'a' -- store as is }
  cmp     al, 'z'
  ja      @notLower       { above 'z' -- store as is }
  sub     al, ('a' - 'A') { convert Character in AL to upper Case }
@notLower:
  stosb                   { Store upCased Character in String }
  loop    @UpCase         { Decrement CX, jump if not zero }
@Done:
  pop     ds              { Restore DS from stack }
end;

FUNCTION Uppercase(S : STRING) : STRING;
BEGIN
StrUpr(S);
Uppercase := S;
END;

FUNCTION LoCase (InChar : CHAR) : CHAR;
BEGIN
   IF InChar IN ['A'..'Z'] THEN
      LoCase := CHR (ORD (Inchar) + 32)
   ELSE
      LoCase := InChar
END;

FUNCTION FixLen (AnyString : STRING; PadChar : CHAR; FldSize : WORD) : STRING;
                                                             assembler;
asm
        PUSH    DS              {Save Data Segment}
        CLD                     {Clear direction flag}
        LDS     SI, AnyString    {DS:SI-->AnyString}
        LES     DI, @Result      {ES:DI-->String to be returned}
        MOV     BX, DI           {Save DI value for later}
        LODSB                   {AL has Length(AnyString)}
        CBW                     {Make AL into word in AX}
        STOSB                   {Put the length into Result & Inc(DI)}
        MOV     CX, AX           {Length in CX}
        REP     MOVSB           {Pad=AnyString}
        MOV     CX, FldSize      {CX has FldSize}
        XOR     CH, CH           {Make FldSize=FldSize mod 256}
        MOV     ES : [BX], CL      {Make Length(Pad)=FldSize}
        SUB     CX, AX           {CX=FldSize-Length(AnyString)}
        JB      @1              {Return truncated string if CX<0}
        MOV     AL, PadChar      {else load character to pad}
        REP     STOSB           {and pad to FldSize}
@1 :                             {Go back}
        POP     DS              {Restore Data Segment}
END;


FUNCTION PadR (InpStr : STRING; FieldLen : BYTE) : STRING;
BEGIN
PadR := FixLen (InpStr, #32, FieldLen);
END;

Procedure RightJustify(Var S: String; Width: Byte); Assembler;
Asm
   push    ds              { Save DS }
   lds     si, S           { Load Pointer to String }
   mov     al, [si]        { Move length Byte  in AL }
   mov     ah, Width       { Move Width in AH }
   sub     ah, al          { Subtract }
   jbe     @Done           { if Length(S) >= Width then Done... }
   push    si              { Save SI on stack }
   mov     cl, al
   sub     ch, ch          { CX = length of the String }
   add     si, cx          { SI points to the last Character }
   mov     dx, ds
   mov     es, dx          { ES = DS }
   mov     di, si          { DI = SI }
   mov     dl, ah
   sub     dh, dh          { DX = number of spaces to padd }
   add     di, dx          { DI points to the new end of the String }
   std                     { String ops backward }
   rep     movsb           { Copy String to the new location }
   pop     si              { SI points to S }
   mov     di, si          { DI points to S }
   add     al, ah          { AL = new length Byte }
   cld                     { String ops Forward }
   stosb                   { Store new length Byte }
   mov     al, ' '
   mov     cx, dx          { CX = number of spaces }
   rep     stosb           { store spaces }
@Done:
   pop     ds              { Restore DS }
end;

FUNCTION PadL(InpStr : STRING; Len : Byte) : STRING;
BEGIN
RightJustify(InpStr,Len);
PadL := InpStr;
END;

FUNCTION TrimB (InpStr : STRING) : STRING;
BEGIN
while (InpStr[0] > #0) and (InpStr[Length(InpStr)] = #32) do
         Dec(InpStr[0]){ trim left }
  while (InpStr[0] > #0) and (InpStr[1] = #32) do
  begin
    Move(InpStr[2], InpStr[1], Pred(Length(InpStr)));
    Dec(InpStr[0]);
  end;
  TrimB := InpStr;
END;

PROCEDURE Replace (VAR S : STRING; NowChar, ReplaceChar : CHAR);
VAR i    : BYTE;
    SLen : BYTE ABSOLUTE S;
BEGIN
FOR i := 1 TO SLen DO
    IF S [i] = NowChar THEN S [i] := ReplaceChar;
END;


FUNCTION GetStr (VAR InpStr : STRING; Delim : CHAR) : STRING;
VAR i : INTEGER;
BEGIN
   i := POS (Delim, InpStr);
   IF i = 0 THEN BEGIN
      GetStr := InpStr;
      InpStr := ''
      END
   ELSE BEGIN
      GetStr := COPY (InpStr, 1, i - 1);
      DELETE (InpStr, 1, i)
      END
END;

{ ?????????????????????????????????????????????????????????????????????????? }
{ ?                        PATH PROCEDURES AND FUNCTIONS                   ? }
{ ?????????????????????????????????????????????????????????????????????????? }

FUNCTION PathOnly (FileName : PathStr) : PathStr;
VAR
   Dir  : DirStr;
   Name : NameStr;
   Ext  : ExtStr;
BEGIN
   FSplit (FileName, Dir, Name, Ext);
   PathOnly := Dir;
END {PathOnly};

FUNCTION RootOnly (FileName : PathStr) : PathStr;
VAR
   Dir  : DirStr;
   Name : NameStr;
   Ext  : ExtStr;
BEGIN
   FSplit (FileName, Dir, Name, Ext);
   RootOnly := COPY (Dir, 1, 2) + '\';
END {RootOnly};

FUNCTION NameOnly (FileName : PathStr) : PathStr;
{ Strip any path information from a file specification }
VAR
   Dir  : DirStr;
   Name : NameStr;
   Ext  : ExtStr;
BEGIN
   FSplit (FileName, Dir, Name, Ext);
   NameOnly := Name + Ext;
END {NameOnly};

FUNCTION BaseNameOnly (FileName : PathStr) : PathStr;
{ Strip any path and extension from a file specification }
VAR
   Dir  : DirStr;
   Name : NameStr;
   Ext  : ExtStr;
BEGIN
   FSplit (FileName, Dir, Name, Ext);
   BaseNameOnly := Name;
END {BaseNameOnly};

FUNCTION ExtOnly (FileName : PathStr) : PathStr;
{ Strip the path and name from a file specification.  Return only the }
{ filename extension.                                                 }
VAR
   Dir  : DirStr;
   Name : NameStr;
   Ext  : ExtStr;
BEGIN
   FSplit (FileName, Dir, Name, Ext);
   IF POS ('.', Ext) <> 0 THEN
      DELETE (Ext, 1, 1);
   ExtOnly := Ext;
END {ExtOnly};

FUNCTION NameLessExt (FileName : PathStr) : PathStr;
{ Strip any extension from a file specification }
VAR
   Dir  : DirStr;
   Name : NameStr;
   Ext  : ExtStr;
BEGIN
   FSplit (FileName, Dir, Name, Ext);
   NameLessExt := Dir + Name;
END;

FUNCTION AddBackSlash(DirName : string) : string;
  {-Add a default backslash to a directory name}
begin
  if DirName[Length(DirName)] in ['\',':',#0] then
    AddBackSlash := DirName
  else
    AddBackSlash := DirName+'\';
end;


FUNCTION NoBackSlash (Path : PathStr) : PathStr;
{ Returns a path name that has its last backslash removed }
BEGIN
  IF (Path [LENGTH (Path) ] = '\') AND     { Last char of path is backslash }
     (Path <> '\') AND                    { Path is not a root directory }
     NOT ( (LENGTH (Path) = 3) AND (COPY (Path, 2, 2) = ':\') ) THEN
    DELETE (Path, LENGTH (Path), 1);                    { Delete backslash }
  NoBackSlash := Path;
END; { Nobackslash }

FUNCTION StripPathName (Path : PathStr) : PathStr;
{If path contains wildcard *.*,??? Then Strip away leaving only path}

VAR Temp, S : PathStr;
    Wild   : BYTE;

BEGIN
Path := NoBackSlash (Path);
S    := PathOnly(Path);
Temp := NameOnly(Path);
Wild := POS ('*', Temp) + POS ('?', Temp) + POS ('.', Temp);
IF Wild <> 0 THEN Path := S;
IF (LENGTH (Path) = 1) AND (UPCASE (Path [1]) IN ['A'..'Z']) THEN Path := Path + ':\';
IF Path [LENGTH (Path) ] <> '\' THEN Path := Path + '\';
StripPathName := Path;
END;


FUNCTION FullPathname (Path, FileMask : PathStr) : PathStr;
 BEGIN                     {FullPathname}
 Path     := TrimB (StripPathName (Path) );
 Filemask := TrimB (Filemask);
 IF POS (':', FileMask) + POS ('.', FileMask) > 0 THEN FileMask := NameOnly (FileMask);
 IF Path [LENGTH (Path) ] = '\' THEN
    DELETE (Path, LENGTH (Path), 1);        { Delete backslash }
 IF FileMask [1] = '\' THEN FileMask := COPY (FileMask, 2, LENGTH (FileMask) );
 FullPathName := FExpand (Path + '\' + FileMask);
 END;                      {FullPathname}

FUNCTION SameName (N1, N2 : STRING) : BOOLEAN;
{
  Function to compare filespecs.

  Wildcards allowed in either name.
  Filenames should be compared seperately from filename extensions by using
     seperate calls to this function
        e.g.  FName1.Ex1
              FName2.Ex2
              are they the same?
              they are if SameName(FName1, FName2) AND SameName(Ex1, Ex2)

  Wildcards work the way DOS should've let them work (eg. *XX.DAT doesn't
  match just any file...only those with 'XX' as the last two characters of
  the name portion and 'DAT' as the extension).

  This routine calls itself recursively to resolve wildcard matches.

}

VAR
   P1, P2 : INTEGER;
   Match  : BOOLEAN;
BEGIN
   P1    := 1;
   P2    := 1;
   Match := TRUE;

   IF (LENGTH (N1) = 0) AND (LENGTH (N2) = 0) THEN
      Match := TRUE
   ELSE
      IF LENGTH (N1) = 0 THEN
         IF N2 [1] = '*' THEN
            Match := TRUE
         ELSE
            Match := FALSE
      ELSE
         IF LENGTH (N2) = 0 THEN
            IF N1 [1] = '*' THEN
               Match := TRUE
            ELSE
               Match := FALSE;

   WHILE (Match = TRUE) AND (P1 <= LENGTH (N1) ) AND (P2 <= LENGTH (N2) ) DO
      IF (N1 [P1] = '?') OR (N2 [P2] = '?') THEN BEGIN
         INC (P1);
         INC (P2);
      END {then}
      ELSE
         IF N1 [P1] = '*' THEN BEGIN
            INC (P1);
            IF P1 <= LENGTH (N1) THEN BEGIN
               WHILE (P2 <= LENGTH (N2) ) AND NOT SameName (COPY (N1, P1, LENGTH (N1) - P1 + 1),COPY(N2,P2,LENGTH(N2)-P2+1)) DO
                  INC (P2);
               IF P2 > LENGTH (N2) THEN
                  Match := FALSE
               ELSE BEGIN
                  P1 := SUCC (LENGTH (N1) );
                  P2 := SUCC (LENGTH (N2) );
               END {if};
            END {then}
            ELSE
               P2 := SUCC (LENGTH (N2) );
         END {then}
         ELSE
            IF N2 [P2] = '*' THEN BEGIN
               INC (P2);
               IF P2 <= LENGTH (N2) THEN BEGIN
                  WHILE (P1 <= LENGTH (N1) ) AND NOT SameName (COPY (N1, P1, LENGTH (N1)-P1+1),COPY(N2, P2,LENGTH(N2)-P2+1)) DO
                     INC (P1);
                  IF P1 > LENGTH (N1) THEN
                     Match := FALSE
                  ELSE BEGIN
                     P1 := SUCC (LENGTH (N1) );
                     P2 := SUCC (LENGTH (N2) );
                  END {if};
               END {then}
               ELSE
                  P1 := SUCC (LENGTH (N1) );
            END {then}
            ELSE
               IF UPCASE (N1 [P1]) = UPCASE (N2 [P2]) THEN BEGIN
                  INC (P1);
                  INC (P2);
               END {then}
               ELSE
                  Match := FALSE;

   IF P1 > LENGTH (N1) THEN BEGIN
      WHILE (P2 <= LENGTH (N2) ) AND (N2 [P2] = '*') DO
         INC (P2);
      IF P2 <= LENGTH (N2) THEN
         Match := FALSE;
   END {if};

   IF P2 > LENGTH (N2) THEN BEGIN
      WHILE (P1 <= LENGTH (N1) ) AND (N1 [P1] = '*') DO
         INC (P1);
      IF P1 <= LENGTH (N1) THEN
         Match := FALSE;
   END {if};

   SameName := Match;

END {SameName};

FUNCTION Exist (FName : PathStr; GoodAttr : WORD) : BOOLEAN;
    {-Return true if file is found and attribute matches }
  VAR
    Regs : REGISTERS;
    FLen : BYTE ABSOLUTE FName;
  BEGIN
    {check for empty string}
    IF LENGTH (FName) = 0 THEN Exist := FALSE
    ELSE WITH Regs DO
    BEGIN
      IF IORESULT = 0 THEN ; {clear IoResult}
      INC (FLen);
      FName [FLen] := #0;
      AX := $4300;           {get file attribute}
      DS := SEG (FName);
      DX := OFS (FName [1]);
      MSDOS (Regs);
      Exist := (NOT ODD (Flags) ) AND (IORESULT = 0) AND
                   (CX AND GoodAttr <> 0);
    END;
  END;


{ ?????????????????????????????????????????????????????????????????????????? }
{ ?                           SORTING FUNCTIONS                            ? }
{ ?????????????????????????????????????????????????????????????????????????? }


FUNCTION LessName (X, Y : DirPtr) : BOOLEAN;
BEGIN
  LessName := X^.Name < Y^.Name;
END;

FUNCTION LessExt (X, Y : DirPtr) : BOOLEAN;
VAR P   : BYTE;
    E, E1 : STRING [3];
BEGIN
    P := POS ('.', X^.Name);
    IF P > 1 THEN E := COPY (X^.Name, P + 1, 3)
    ELSE E := '';

    P := POS ('.', Y^.Name);
    IF P > 1 THEN E1 := COPY (Y^.Name, P + 1, 3)
    ELSE E1 := '';
  LessExt := E < E1;
END;

FUNCTION LessPath (X, Y : DirPtr) : BOOLEAN;
BEGIN
  LessPath := X^.Path < Y^.Path;
END;

FUNCTION LessSize (X, Y : DirPtr) : BOOLEAN;
BEGIN
  LessSize := X^.Size < Y^.Size;
END;

FUNCTION LessTime (X, Y : DirPtr) : BOOLEAN;
BEGIN
  LessTime := X^.Time < Y^.Time;
END;

FUNCTION LessAttr (X, Y : DirPtr) : BOOLEAN;
BEGIN
  LessAttr := X^.Attr < Y^.Attr;
END;

PROCEDURE QuickSort (L, R : INTEGER; VAR Page : SortPage; Less : LessFunc);
VAR
  I, J : INTEGER;
  X    : DirPtr;

  PROCEDURE ExchangeStructs(var I, J; Size : Word);
    inline(
      $FC/                     {cld             ;go forward}
      $8C/$DA/                 {mov dx,ds       ;save DS}
      $59/                     {pop cx          ;CX = Size}
      $5E/                     {pop si}
      $1F/                     {pop ds          ;DS:SI => J}
      $5F/                     {pop di}
      $07/                     {pop es          ;ES:DI => I}
      $D1/$E9/                 {shr cx,1        ;move by words}
      $E3/$0C/                 {jcxz odd}
      $9C/                     {pushf}
                               {start:}
      $89/$F3/                 {mov bx,si}
      $26/$8B/$05/             {mov ax,es:[di]  ;exchange words}
      $A5/                     {movsw}
      $89/$07/                 {mov [bx],ax}
      $E2/$F6/                 {loop start      ;again?}
      $9D/                     {popf}
                               {odd:}
      $73/$07/                 {jnc exit}
      $8A/$04/                 {mov al,[si]     ;exchange the odd bytes}
      $26/$86/$05/             {xchg al,es:[di]}
      $88/$04/                 {mov [si],al}
                               {exit:}
      $8E/$DA);                {mov ds,dx       ;restore DS}

BEGIN
  I := L;
  J := R;
  X := Page [ (L + R) DIV 2];
  REPEAT
    WHILE Less (Page [I], X) DO INC (I);
    WHILE Less (X, Page [J]) DO DEC (J);
    IF I <= J THEN
    BEGIN
      ExchangeStructs (Page [I], Page [J], SIZEOF (DirPtr) );
      INC (I);
      DEC (J);
    END;
  UNTIL I > J;
  IF L < J THEN QuickSort (L, J, Page, Less);
  IF I < R THEN QuickSort (I, R, Page, Less);
END;

{ ?????????????????????????????????????????????????????????????????????????? }
{ ?                 INTERFACED PROCEDURES AND FUNCTIONS                    ? }
{ ?????????????????????????????????????????????????????????????????????????? }

FUNCTION FileTypePerExtension(fName : PathStr) : FileTypes;

VAR
     Ext : ExtStr;

BEGIN
   Ext := ExtOnly(Uppercase(fName));
   IF (fName = '.') OR (fName = '..') OR (fName = '\') OR
   (POS('\.',fName) + POS('..',fName) > 0) THEN
   FileTypePerExtension := fDIR ELSE
   IF (POS(Ext,'.ARC.PAK.ZIP.LZH.ARJ.ZOO.LBR.COM.EXE.BAT') = 0) THEN
   FileTypePerExtension := fOTHER ELSE
   FileTypePerExtension := FILETYPES(POS(Ext,'.ARC.PAK.ZIP.LZH.ARJ.ZOO.LBR.COM.EXE.BAT') div 4);
END;

FUNCTION FileTypeString (FT : FileTypes) : STRING;
BEGIN
CASE FT OF
    fARC   : FileTypeString := 'ARC';
    fPAK   : FileTypeString := 'PAK';
    fZIP   : FileTypeString := 'ZIP';
    fLBR   : FileTypeString := 'LBR';
    fZOO   : FileTypeString := 'ZOO';
    fLZH   : FileTypeString := 'LZH';
    fARJ   : FileTypeString := 'ARJ';
    fCOM   : FileTypeString := 'COM';
    fEXE   : FileTypeString := 'EXE';
    fBAT   : FileTypeString := 'BATCH';
    fSFX   : FileTypeString := 'SFX';
    fDIR   : FileTypeString := 'DIR';
    fVOL   : FileTypeString := 'VOLUME';
    fOTHER : FileTypeString := 'FILE';
    fERROR : FileTypeString := 'ERROR';
    ELSE FileTypeString := '';
    END;
END;

FUNCTION GetArcType (FName : PathStr) : FileTypes;
VAR
  ArcFile : FILE;
  i       : INTEGER;
  Gat     : FileTypes;
  c       : ARRAY [1..5] OF BYTE;
BEGIN
  ASSIGN (ArcFile, FName);
  RESET  (ArcFile,1);
  IF IORESULT <> 0 THEN
    Gat := fError
  ELSE
  IF FILESIZE (ArcFile) < 5 THEN
    Gat := fError
  ELSE
  BEGIN
    BLOCKREAD (ArcFile, c , 5);
    CLOSE (ArcFile);
    IF ( (c [1] = $50) AND (c [2] = $4B) ) THEN
      Gat := fZip
    ELSE
    IF ( (c [1] = $60) AND (c [2] = $EA) ) THEN
      Gat := fArj
    ELSE
    IF ( (c [4] = $6c) AND (c [5] = $68) ) THEN
      Gat := fLzh
    ELSE
    IF ( (c [1] = $5a) AND (c [2] = $4f) AND (c [3] = $4f) ) THEN
      Gat := fZoo
    ELSE
    IF ( (c [1] = $1a) AND (c [2] = $08) ) THEN
      Gat := fArc
    ELSE
    IF ( (c [1] = $1a) AND (c [2] = $0b) ) THEN
      Gat := fPak
    ELSE
      Gat := fOTHER;
  END;

  GetArcType := Gat;
END;

FUNCTION MethodString (Method : BYTE) : STRING;
CONST
    Stowage : ARRAY [0..12] OF STRING [9] =
      ('Stored', 'Shrunk', 'Stored', 'Packed', 'Squeezed', 'LZCrunch', 'LZCrunch',
      'LZW Pack', 'Crunched',