'===========================================================================
' Subject: RECURSIVE FILE FIND ROUTINE        Date: 03-10-99 (19:43)       
'  Author: Dieter Folger                      Code: PB                     
'  Origin: folger@bamberg.baynet.de         Packet: PB.ABC
'===========================================================================
'----------------------------------------------------------------------------
' FINDFILE.BAS for PowerBasic
' Fast file search in specified dir and its subdirs.
' Found files are shown with name, size, date, time, attrib, and long
' filename (if possible). Summery reports number of found files, total
' bytes of files and number of subdirs.
' "C:\" as Path$ and "*.*" as Mask$ show all files on this drive (any
' attribute).
' Freeware (c) 1997-99 by Dieter Folger
'-- Note: -------------------------------------------------------------------
' A former version of this program worked recursivly, but the increasing
' harddisk capacity multiplied the number of subdirectories on a drive
' enormously. There was a stack error when too many recursions had to be
' done (= subdirs scanned), even with the stack set to maximum size (32766).
' This version uses the GOTO command instead, which is less elegant but
' avoids a stack error.
' Demo program searches for all files (*.*) in current directory and its
' subdirs.
'----------------------------------------------------------------------------
DEFINT A-Z
 Path$ = CURDIR$ + "\"
 P$ = Path$             ' save path name as it is changed by SearchTree
 Mask$ = "*.*"          ' all files, any attribute
 CLS
 SearchTree Path$, Mask$
 PRINT "Path ";P$;Mask$;FileFound;"files found -";DirFound;"subdirs scanned"
 DSize$=LTRIM$(USING$(",############",DSize&))
 REPLACE "," WITH "." IN DSize$  ' German conversion
 PRINT DSize$;" bytes used by these files"
END
'----------------------------
SUB SearchTree (Path$, Mask$)
'----------------------------
DIM DTABuffer$(100), P$(100)
SHARED FileFound, DirFound, DSize&, d
IF RIGHT$(Path$,1) <> "\" THEN Path$ = Path$ + "\" ' makes sure that Path$
                                                   ' ends with backlash
LNames = Lfn ' Check if long filenames are supported
             ' Comment the line if you don't want long names
Again:       ' Jump here for more dirs and files search
  SaveDTA                     ' save current DTA for later use
  F$ = DIR$(Path$ + Mask$, 7) ' get all types of files, any attr
  IF LEN(F$) THEN
     PRINT "DIR "; Path$;
     IF LNames THEN PRINT "  (";DirLn$;")" ELSE PRINT ' show long name of dir
  END IF
  WHILE LEN(F$)         ' repeat until no more files are found in this dir
      INCR FileFound
      FileStuff Fs&, Fd$, Ft$, Att$
      PRINT USING "  \            \";F$;
      FSize$ = USING$(",##########",Fs&)
      REPLACE "," WITH "." IN FSize$ ' German conversion
      PRINT FSize$;" ";Fd$;" ";Ft$;" ";Att$; " ";
      IF LEN(F$) THEN IF Lnames THEN PRINT LongName$(Path$+F$) ELSE PRINT
      Dsize& = Dsize& + Fs&    ' add filesize to dirsize
      F$ = DIR$ 'get next file
      IF INKEY$ = CHR$(27) THEN END 'if user wants to cancel
  WEND
  D$ = DIR$(Path$ + "*.*", 55) ' search for all subdirs, any attr
  INCR d: P$(d) = Path$        ' save current path
  DO
     IF LEN(D$) AND (Attr? AND 16) = 16 THEN ' directory found
        IF Lnames THEN DirLn$ = LongName$(Path$+"\"+D$) ' long name of dir
        Path$ = RTRIM$(Path$,"\") + "\" + D$ + "\"
        INCR DirFound ' count dir for summery
        GOTO Again    ' start search loop again in this dir
     END IF
     D$ = DIR$        ' next dir
     IF D$ = "" THEN  ' no more subdirs
        DECR d : Path$ = P$(d) ' restore old path and
        RestoreDTA             '   old DTA to continue search
     END IF
     IF INKEY$ = CHR$(27) THEN END
  LOOP UNTIL d = 0
END SUB
'----------------------------
SUB GetDTA (DTASeg&, DTAOfs&)
'----------------------------
  ! mov ax, &h2F00
  ! int &h21
  ! mov s&,es
  ! mov o&,bx
 DTAOfs& = o&:DTASeg& = s&
END SUB
'-------------
FUNCTION Attr?
'-------------
 GetDTA DTASeg&, DTAOfs&
 DEF SEG = DTASeg&
   Attr? = PEEK(DTAOfs& + 21)
 DEF SEG
END FUNCTION
'-------------------------------------------
SUB FileStuff (FSize&, FDate$, FTime$, Att$)
'-------------------------------------------
 GetDTA DTASeg&, DTAOfs&
 DEF SEG = DTASeg&
   FSize& = PEEKL(DTAOfs& + 26)
   FDate$ = FILEDATE$(PEEKI(DTAOfs& + 24))
   FTime$ = FILETIME$(PEEKI(DTAOfs& + 22))
   Att?   = PEEK(DTAOfs& + 21)
 DEF SEG
 Att$ = "...."
 IF BIT(Att?, 0) THEN MID$(Att$, 2, 1) = "R" 'read only
 IF BIT(Att?, 1) THEN MID$(Att$, 3, 1) = "H" 'hidden
 IF BIT(Att?, 2) THEN MID$(Att$, 4, 1) = "S" 'system
 IF BIT(Att?, 5) THEN MID$(Att$, 1, 1) = "A" 'archive
END SUB
'----------
SUB SaveDTA
'----------
 SHARED d, DTABuffer$()
 GetDTA DTASeg&, DTAOfs&
 DEF SEG = DTASeg&
   DTABuffer$(d) = PEEK$(DTAofs&, 21)
 DEF SEG
END SUB
'-------------
SUB RestoreDTA
'-------------
 SHARED d, DTABuffer$()
 GetDTA DTASeg&, DTAOfs&
 DEF SEG = DTASeg&
   POKE$ DTAofs&, DTABuffer$(d)
 DEF SEG
END SUB
'--------------------------
FUNCTION FileDate$ (FDate)
'--------------------------
 'Convert DOS date to string
 DateY = (FDate \ 512) + 80
 Year$ = RIGHT$("00" + MID$(STR$(DateY), 2), 2)
 DateM = (FDate AND 511) \ 32
 Month$ = RIGHT$("00" + MID$(STR$(DateM), 2), 2)
 DateD = FDate AND 31
 Day$ = RIGHT$("00" + MID$(STR$(DateD), 2), 2)
 FileDate$ = Day$ + "." + Month$ + "." + Year$
END FUNCTION
'--------------------------
FUNCTION FileTime$ (FTime)
'--------------------------
 'Convert DOS time to string
 TimeH = FTime \ 2048
 IF TimeH < 0 THEN INCR TimeH, 31
 Hour$ = RIGHT$("00" + MID$(STR$(TimeH), 2), 2)
 TimeM = (FTime AND 2047) \ 32
 Minute$ = RIGHT$("00" + MID$(STR$(TimeM), 2), 2)
 FileTime$ = Hour$ + ":" + Minute$
END FUNCTION
'-----------
FUNCTION Lfn
'-----------
' checks if Windows is installed
' (= if long file names are supported)
  ! mov ax, &h1600
  ! int &h2F
  ! mov LfMode, al
 IF LfMode > 3 THEN FUNCTION = LfMode
END FUNCTION
'-------------------------
FUNCTION LongName$ (File$)
'-------------------------
' Returns long filename of File$
 DIM Ldta AS STRING * 500
 DIM FindHandle AS Byte
 F$ = File$ + CHR$(0)
 LSeg = VARSEG(Ldta)
 LOff = VARPTR(Ldta)
 FSeg = STRSEG(F$)
 FOff = STRPTR(F$)
  ! push ds
  ! mov ax, &h714E     ;find first matching file
  ! mov si, 0          ;see Ralph Brown's
  ! mov cx, &hFF       ;interrupt list
  ! mov es, LSeg       ;for further details
  ! mov di, LOff
  ! mov dx, FOff
  ! mov ds, FSeg
  ! int &h21
  ! pop ds
  ! mov FindHandle, ax
  ! mov ax, &h71A1     ;terminate search and
  ! mov bx, FindHandle ;reset file handle
  ! int &h21
 FUNCTION  = RTRIM$(MID$(Ldta,45,260), CHR$(0))
END FUNCTION
'==== eof ==================================================================
