[Back to FILES SWAG index]  [Back to Main SWAG index]  [Original]

{$A+,B-,D+,E-,F+,I-,L+,N-,O+,R-,S+,V-}

Unit CWare;

(* Version 1.1 - CollisionWare Premium SoftWare - Compiled by Kito Mann *)
(* This unit is a simple collection of some some procedures aquired     *)
(* from other programs and myself. New versions will have added         *)
(* procedures, and the present ones will be improved. Comments, bugs,   *)
(* and questions accepted.                                              *)
(* Keep in mind that there is NO WARANTY! It IS NOT GAURANTEED that all *)
(* these procedures will work!                                          *)
(* If you modify the procedures included, or add your own, I request    *)
(* that you send me a copy of the new unit and source code.             *)

(* It'd probably be helpful if you declare ErrorCode: byte in your main *)
(* program. It is used as an Error variable much like the DosError used *)
(* in the DOS unit.                                                     *)

(* The Collision Theory pm-BBS *)
(*         24 hours            *)
(*      (703)503-9441          * <--- NUMBER AND HOUR CHANGE! *)
(*         Burke, VA           *)
(* "Dedicated to Intelligent   *)
(*        Conversation"        *)

INTERFACE

Uses Crt,
     Dos;

const
  MaxDirEnteries=       20;    { Maximum number of directories that can be specified to search }
                               { This doesn't include those searched "below" ones specified.   }

type
  FullNameStr=          string[12];                 { Type for storing name+dot+extention                                 }
  DirSearchEntry=       record                      { This data type is used to store all the paths that will be searched }
                          Dir:         DirStr;      {   <-- Path to search                                                }
                          Name:        FullNameStr; {   <-- File spec to search                                           }
                          Below:       boolean;     {   <-- TRUE=search directories below the specified one               }
                        end;
  ProcType=             procedure(var S: SearchRec; P: PathStr);
  AnyStr=               string[255];


var
  EngineMask:           FullNameStr;
  EngineAttr:           byte;
  EngineProc:           ProcType;
  EngineCode:           byte;

  Reg:                  Registers;   { Register storage for DOS calls }
  OldSeg,OldOfs:        word;
  BufData:              longint;
  BufferSeg:            word;
  BufferOfs:            word;
  BufferLen:            word;
  BufferPtr:            pointer;
  T:                    text;
  P:                    PathStr;


(* File and Keyboard Buffer procedures *)

function FileFound(F: ComStr): boolean;

procedure SearchEngine(Mask: PathStr; Attr: byte; Proc: ProcType; var ErrorCode: byte);

function GoodDirectory(S: SearchRec): boolean;

procedure SearchOneDir(var S: SearchRec; P: PathStr);

procedure SearchEngineAll(Path: PathStr; Mask: FullNameStr; Attr: byte;
                          Proc: ProcType; var ErrorCode: byte);

procedure IPP;

procedure NewExitProc2;

procedure ResetBuffer;

function BufSize: word;

function InBuffer(S: string): integer;

procedure InstallInterruptHandler;

procedure DeleteFiles(P: string);

procedure DeleteDir(P:string);

procedure ListFiles(P: string; complete:boolean; pausenum:integer);

(* Misc. String procedures *)

function DateString: string;

function TimeString: string;

procedure Tab(s1,s2:AnyStr; i:integer);

Function UpCaseString(StrIn : String) : String;
{ Convert a string to upper case }

Function PathOnly(FileName : String) : String;
{ Strip any filename information from a file specification }

Function NameOnly(FileName : String) : String;
{ Strip any path information from a file specification }

Function BaseNameOnly(FileName : String) : String;
{ Strip any path and extension information from a file specification }

Function ExtOnly(FileName : String) : String;
{ Return only the extension portion of a filename }

Function IntStr(Int : LongInt; Form : Integer) : String;
{ Convert an Integer variable to a string }

Function Strr(Int:LongInt) : String;
{ Same as IntStr but does not use the variable "Form" }

Function SameFile(File1, File2 : String) : Boolean;
{ Call to find out if File1 has a name equivalent to File2.  Both filespecs }
{ may contain wildcards.                                                    }


IMPLEMENTATION

{ -------------------------------------------------------------------------- }

function FileFound(F: ComStr): boolean;
{
  This returns TRUE if the file F exists, FALSE otherwise.  F can contain
  wildcard characters.
}
var
  SRec:                 SearchRec;
begin
  SRec.Name := '*';
  FindFirst(F,0,SRec);
  if SRec.Name='*' then FileFound := false else FileFound := true;
end;


(********* The following search engine routines are sneakly swiped *********)
(********* from Turbo Technix v1n6.  See there for further details *********)

procedure SearchEngine(Mask: PathStr; Attr: byte; Proc: ProcType;
                       var ErrorCode: byte);
var
  S:                    SearchRec;
  P:                    PathStr;
  Ext:                  ExtStr;
begin
  FSplit(Mask, P, Mask, Ext);
  Mask := Mask+Ext;
  FindFirst(P+Mask,Attr,S);
  if DosError<>0 then
  begin
    ErrorCode := DosError;
    exit;
  end;
  while DosError=0 do
  begin
    Proc(S, P);
    FindNext(S);
  end;
  if DosError=18 then ErrorCode := 0
  else ErrorCode := DosError;
end;

{ -------------------------------------------------------------------------- }

function GoodDirectory(S: SearchRec): boolean;
begin
  GoodDirectory := (S.name<>'.') and (S.Name<>'..') and
  (S.Attr and Directory=Directory);
end;

{ -------------------------------------------------------------------------- }

procedure SearchOneDir(var S: SearchRec; P: PathStr);
begin
  if GoodDirectory(S) then
  begin
    P := P+S.Name;
    SearchEngine(P+'\'+EngineMask,EngineAttr,EngineProc,EngineCode);
    SearchEngine(P+'\*.*',Directory or Archive, SearchOneDir,EngineCode);
  end;
end;

{ -------------------------------------------------------------------------- }

procedure SearchEngineAll(Path: PathStr; Mask: FullNameStr; Attr: byte;
                          Proc: ProcType; var ErrorCode: byte);
begin
  EngineMask := Mask;
  EngineProc := Proc;
  EngineAttr := Attr;
  SearchEngine(Path+Mask,Attr,Proc,ErrorCode);
  SearchEngine(Path+'*.*',Directory or Archive,SearchOneDir,ErrorCode);
  ErrorCode := EngineCode;
end;

(************** Thus ends the sneakly swiped code *************)

{ -------------------------------------------------------------------------- }

procedure IPP;
{ Interrupt pre-processor.  This is a new handler for interrupt 29h which
  provides special functions.  See comments in IHAND.ASM}
begin
  InLine(
      $06/                   {          push    es                      }
      $1E/                   {          push    ds                      }
      $53/                   {          push    bx                      }
      $57/                   {          push    di                      }
      $BB/$3F/$3F/           {          mov     bx, 3f3fh               }
      $8E/$C3/               {          mov     es, bx                  }
      $BB/$3F/$3F/           {          mov     bx, 3f3fh               }
      $26/$8B/$3F/           {          mov     di, word ptr [es:bx]    }
      $26/$8E/$5F/$02/       {          mov     ds, word ptr [es:bx+2]  }
      $88/$05/               {          mov     byte ptr [di], al       }
      $26/$FF/$07/           {          inc     word ptr [es:bx]        }
      $5F/                   {          pop     di                      }
      $5B/                   {          pop     bx                      }
      $1F/                   {          pop     ds                      }
      $07/                   {          pop     es                      }
      $3C/$0A/               {          cmp     al, 10                  }
      $75/$28/               {          jne     looper                  }
      $50/                   {          push    ax                      }
      $52/                   {          push    dx                      }
      $51/                   {          push    cx                      }
      $53/                   {          push    bx                      }
      $B4/$03/               {          mov     ah, 3                   }
      $B7/$00/               {          mov     bh, 0                   }
      $CD/$10/               {          int     10h                     }
      $80/$FE/$18/           {          cmp     dh, 24                  }
      $75/$15/               {          jne     popper                  }
      $FE/$CE/               {          dec     dh                      }
      $B7/$00/               {          mov     bh, 0                   }
      $B4/$02/               {          mov     ah, 2                   }
      $CD/$10/               {          int     10h                     }
      $B8/$01/$06/           {          mov     ax, 0601h               }
      $B7/$07/               {          mov     bh, 7                   }
      $B9/$00/$11/           {          mov     cx, 1100h               }
      $BA/$4F/$18/           {          mov     dx, 184fh               }
      $CD/$10/               {          int     10h                     }
      $5B/                   {  popper: pop     bx                      }
      $59/                   {          pop     cx                      }
      $5A/                   {          pop     dx                      }
      $58/                   {          pop     ax                      }
      $9C/                   {  looper: pushf                           }
      $9A/$00/$00/$00/$00/   {          call    far [0:0]               }
      $CF);                  {          iret                            }
end;

{ -------------------------------------------------------------------------- }

procedure NewExitProc2;
{ This exit procedure removes the interrupt 29h handler from memory and places
  the cursor at the bottom of the screen. }
begin
  Reg.AH := $25;
  Reg.AL := $29;
  Reg.DS := OldSeg;
  Reg.DX := OldOfs;
  MsDos(Reg);
  Window(1,1,80,25);
  GotoXY(1,24);
  TextAttr := $07;
  ClrEol;
end;

{ -------------------------------------------------------------------------- }

procedure ResetBuffer;
{ Reset pointers to the text buffer, effectivly deleting any text in it }
begin
  MemW[seg(BufData):ofs(BufData)] := BufferOfs;    { Set first 2 bytes of BufData to point to buffer offset }
  MemW[seg(BufData):ofs(BufData)+2] := BufferSeg;  { And next two bytes to point to buffer segment }
  MemW[seg(IPP):ofs(IPP)+21] := seg(BufData);    { Now point the interrupt routine to BufData for pointer }
  MemW[seg(IPP):ofs(IPP)+26] := ofs(BufData);    {  to the text buffer }
end;

{ -------------------------------------------------------------------------- }

function BufSize: word;
{ This returns the number of characters in the text buffer.  It's what BufData
  now points to minus what is origionally pointed to, eg, the number of times
  IPP incremented it }
begin
  BufSize := MemW[seg(BufData):ofs(BufData)]-BufferOfs;
end;

{ -------------------------------------------------------------------------- }

function InBuffer(S: string): integer;
{ This searched the text buffer for the string S, and if it's found returns
  the offset in the buffer.  If it's not found a -1 is returned }
var
  L,M:                  word;
  X:                    byte;
begin
  X := 1;
  L := BufferOfs;
  M := BufSize;
  while (X<=length(S)) and (L<=M) do
  begin
    if Mem[BufferSeg:L]=byte(S[X]) then Inc(X) else X := 1;
    Inc(L);
  end;
  if X>length(S) then InBuffer := L-length(S) else InBuffer := -1;
end;

{ -------------------------------------------------------------------------- }

procedure InstallInterruptHandler;
{ Installs the int 29h handler }
begin
  BufferLen := $4000;  { Set up a 16k buffer }
  GetMem(BufferPtr,BufferLen);  { Allocate memory pointed at by BufferPtr }
  BufferSeg := seg(BufferPtr^);  { Read segment and offset of buffer for easy access }
  BufferOfs := ofs(BufferPtr^);
  ResetBuffer;    { Place these values in the IPP routine, resetting buffer }
  Reg.AH := $35;
  Reg.AL := $29;  { DOS service 35h, get interrupt vector for 29h }
  MsDos(Reg);
  OldSeg := Reg.ES;   { Store the segment and offset of the old vector for later use }
  OldOfs := Reg.BX;
  MemW[seg(IPP):ofs(IPP)+90] := Reg.BX;  { And store them so IPP can call the routine }
  MemW[seg(IPP):ofs(IPP)+92] := Reg.ES;
  Reg.AL := $29; { DOS service 25h, set interrupt vector 29h }
  Reg.AH := $25;
  Reg.DS := seg(IPP);    { Store segment and offset for IPP.  The +16 is to skip TP stack }
  Reg.DX := ofs(IPP)+16; { maintainence routines }
  MsDos(Reg);
end;

{ -------------------------------------------------------------------------- }

  procedure DeleteFiles(P: string);
  {
    Delete all files in the directory named, including
    Hidden, Read-only, System and other file types.
  }
  var
    SRec:               SearchRec;
    ErrorCode:          byte;
  begin
    FindFirst(P+'\*.*',0,SRec);
    while DosError=0 do
    begin
      Assign(T, P+'\'+SRec.Name);
      SetFAttr(T,Archive);
      writeln('Deleting ',P,+'\'+Srec.Name);
      {$I-}
      Erase(T);
      {$I+}
      ErrorCode := IOResult;
      FindNext(SRec);
    end;
    ErrorCode := IOResult;
end;

{ -------------------------------------------------------------------------- }

procedure DeleteDir(P:string);

{ Simply deletes specified directory }

var ErrorCode: byte;
begin
  DeleteFiles(P);
  {$I-}
  RmDir(P);
  {$I+}
  ErrorCode := IOResult;
end;

{ -------------------------------------------------------------------------- }

procedure ListFiles(P: string; complete:boolean; pausenum:integer);
  {
   If complete is true then will show the name and file size of every
   file. Otherwise will just show the filename. Numlines is the number
   of files it will display before a pause. 0 means no pause.
  }
  var
    SRec:               SearchRec;
    ErrorCode:          byte;
    Size:               AnyStr;
    Index:              integer;
    TheChar:            char;
    Quit:               boolean;

  begin
    Quit:=false;
    FindFirst(P+'\*.*',0,SRec);
    Index:=1;
    while DosError=0 do
    begin
       if Index=pausenum then 
       begin
        write('[Q=quit, ANY KEY=continue]:');
        TheChar:=UpCase(ReadKey); writeln(TheChar);
        if TheChar='Q' then quit:=true;
        writeln;
        Index:=0;
       end;
      if NOT Quit then 
      if complete then begin
        Size:=strr(Srec.Size);
        tab(Srec.Name,Size,15);
        writeln;
      end else
      writeln(Srec.Name);
      FindNext(SRec);
      Inc(Index);
    end;
    ErrorCode := IOResult;
end;

{ -------------------------------------------------------------------------- }

  function DateString: string;
  {
    Returns the current date in a string of the form:  MON ## YEAR.
    E.g, 21 Feb 1989 or 02 Jan 1988.
  }
  const
    Month:              array[1..12] of string[3]=
                        ('Jan','Feb','Mar','Apr','May','Jun',
                         'Jul','Aug','Sep','Oct','Nov','Dec');
  var
    Y,M,D,Junk:         word;
    DS,YS:              string[5];
  begin
    GetDate(Y,M,D,Junk);
    Str(Y,YS);
    Str(D,DS);
    if length(DS)<2 then DS := '0'+DS;
    DateString := DS+' '+Month[M]+' '+YS;
  end;

{ -------------------------------------------------------------------------- }

  function TimeString: string;
  {
    Returns the current time in the form:  HH:MM am/pm
    E.g, 12:00 am or 09:12 pm.
  }
  var
    H,M,Junk:           word;
    HS,MS:              string[5];
    Am:                 boolean;
  begin
    GetTime(H,M,Junk,Junk);
    case H of
      0:     begin
               Am := true;
               H := 12;
             end;
      1..11: Am := true;
      12:    Am := false;
      else   begin
               Am := false;
               H := H-12;
             end;
    end;
    Str(H,HS);
    Str(M,MS);
    if length(HS)<2 then HS := '0'+HS;
    if length(MS)<2 then MS := '0'+MS;
    if Am then TimeString := HS+':'+MS+' am'
    else TimeString := HS+':'+MS+' pm';
  end;

{ -------------------------------------------------------------------------- }

procedure Tab(s1,s2:AnyStr; i:integer);

{ Writes s1, then goes to i-length(s1) and writes s2 }

var j,k:integer;
begin
  j:=length(s1);
  i:=i-j;
  write(s1);
  for k:=1 to i do write(' ');
  write(s2);
end;

{ -------------------------------------------------------------------------- }

Function UpCaseString(StrIn : String) : String;
Begin
   Inline(                   { Thanks to Phil Burns for this routine }

      $1E/                   {         PUSH    DS                ; Save DS}
      $C5/$76/$06/           {         LDS     SI,[BP+6]         ; Get source string address}
      $C4/$7E/$0A/           {         LES     DI,[BP+10]        ; Get result string address}
      $FC/                   {         CLD                       ; Forward direction for strings}
      $AC/                   {         LODSB                     ; Get length of source string}
      $AA/                   {         STOSB                     ; Copy to result string}
      $30/$ED/               {         XOR     CH,CH}
      $88/$C1/               {         MOV     CL,AL             ; Move string length to CL}
      $E3/$0E/               {         JCXZ    Exit              ; Skip if null string}
                             {;}
      $AC/                   {UpCase1: LODSB                     ; Get next source character}
      $3C/$61/               {         CMP     AL,'a'            ; Check if lower-case letter}
      $72/$06/               {         JB      UpCase2}
      $3C/$7A/               {         CMP     AL,'z'}
      $77/$02/               {         JA      UpCase2}
      $2C/$20/               {         SUB     AL,'a'-'A'        ; Convert to uppercase}
                             {;}
      $AA/                   {UpCase2: STOSB                     ; Store in result}
      $E2/$F2/               {         LOOP    UpCase1}
                             {;}
      $1F);                  {Exit:    POP     DS                ; Restore DS}

end {UpCaseString};

{ -------------------------------------------------------------------------- }

Function PathOnly(FileName : String) : String;
Var
   Dir  : DirStr;
   Name : NameStr;
   Ext  : ExtStr;
Begin
   FSplit(FileName, Dir, Name, Ext);
   PathOnly := Dir;
End {PathOnly};

{ --------------------------------------------------------------------------- }

Function NameOnly(FileName : String) : String;
{ 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 : String) : String;
{ 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 : String) : String;
{ 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 IntStr(Int : LongInt; Form : Integer) : String;
Var
   S : String;
Begin
   If Form = 0 then
      Str(Int, S)
   else
      Str(Int:Form, S);
   IntStr := S;
End {IntStr};

{ --------------------------------------------------------------------------- }

Function Strr(Int : LongInt) : String; { Added for my own sake - KM }
Var
   S : String;
Begin
   Str(Int, S);
   Strr := S;
End {Strr};

{ --------------------------------------------------------------------------- }

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 SameFile(File1, File2 : String) : Boolean;
Var
   Path1, Path2 : String;
Begin

   File1 := FExpand(File1);
   File2 := FExpand(File2);
   Path1 := PathOnly(File1);
   Path2 := PathOnly(File2);

   SameFile := SameName(BaseNameOnly(File1), BaseNameOnly(File2)) AND
               SameName(ExtOnly(File1), ExtOnly(File2))           AND
               (Path1 = Path2);

End {SameFile};

{ ---------------------------------------------------------------------------- }

End {Unit CWARE}.

[Back to FILES SWAG index]  [Back to Main SWAG index]  [Original]