
DEFINT A-Z

'FindEm.BAS -- code to do directory searches with QuickBASIC 4.x

 TYPE RegType
      AX    AS INTEGER
      BX    AS INTEGER
      CX    AS INTEGER
      DX    AS INTEGER
      BP    AS INTEGER
      SI    AS INTEGER
      DI    AS INTEGER
      Flags AS INTEGER
 END TYPE

 TYPE RegType2
      AX    AS INTEGER
      BX    AS INTEGER
      CX    AS INTEGER
      DX    AS INTEGER
      BP    AS INTEGER
      SI    AS INTEGER
      DI    AS INTEGER
      Flags AS INTEGER
      DS AS INTEGER
      ES AS INTEGER
 END TYPE

 TYPE FileType
      Attr AS INTEGER
      FileTime AS STRING * 5
      FileDate AS STRING * 8
      FSize AS LONG
      FName AS STRING * 12
 END TYPE

    'Must link with QB.LIB (QB.QLB) for Interrupt functions

DECLARE SUB Interrupt (Intnum%, InReg AS RegType, OutReg AS RegType)
DECLARE SUB InterruptX (Intnum%, InReg2 AS RegType2, OutReg2 AS RegType2)

DECLARE FUNCTION FindFirst% (FileName$, Attr%, FileData AS FileType,_
 FirstOrNext%)


'Constants for use with FindFirst (just to make things easier to read):

'Actions:

CONST FIRST = 0
CONST AGAIN = 1

'Attributes:

CONST NORMAL = 0
CONST READONLY = 1
CONST HIDDEN = 2
CONST SYSTM = 4
CONST LABEL = 8
CONST SUBDIR = 16
CONST ARCHIVE = 32

'Error returns:

CONST INVALIDPATH = 2
CONST NOMATCH = 18


DIM FileData AS FileType
DIM FileSize AS STRING * 8

  'Demonstration program for FindFirst Function--print DOS-like directory

  CLS
  A$ = "\*.*"                                 'Search mask
  A = FindFirst(A$, SUBDIR, FileData, FIRST)  'Findfirst
  IF A THEN                                   'Error
    IF A = INVALIDPATH THEN
        PRINT "Invalid path."
      ELSE
        PRINT "No matching files."
    END IF
    END
  END IF
  Counter = 0                                 'Set counter
 
  DO                                          'Start looping...
   
    PRINT FileData.FName; "  ";
    IF FileData.Attr AND SUBDIR THEN
        FileSize = "<DIR>"
      ELSE
        FileSize = MID$(STR$(FileData.FSize), 2)
    END IF
    PRINT FileSize; "  "; FileData.FileDate; "  "; FileData.FileTime
    Counter = Counter + 1
 
  LOOP WHILE FindFirst("", NORMAL, FileData, AGAIN) = 0  'Until Findnext fails
 
  PRINT "There were"; Counter; "matching file(s) found." 'Finished

END

'
FUNCTION FindFirst% (FileName$, Attr%, FileData AS FileType, FirstOrNext%)

  'Function to do directory searches
  'FileName$ contains a pattern to match (*.*, *.DOC, Thisfile.EXE, etc.)
  'Attr% is the attribute for which to search (0=normal files)
  'FileData is Type FileType into which we will put data on matched file
  'FirstOrNext% determines action; 0=Find first match, non-zero=Find next

  DIM InRegs AS RegType2
  DIM OutRegs AS RegType2
  DIM FDate AS LONG
  DIM FTime AS LONG
  STATIC DTASeg AS INTEGER
  STATIC DTAOff AS INTEGER

  FindFirst% = 1   'A non-zero return indicates an error

  'Set up for findfile interrupt call
 
  InRegs.AX = &H4F * 256          'FindNext
  IF FirstOrNext = 0 THEN         'FindFirst
    InRegs.AX = &H2F * 256         'Find current DTA (probably in PSP)
    InterruptX &H21, InRegs, OutRegs
    DTASeg = OutRegs.ES            'Segment of current DTA
    DTAOff = OutRegs.BX            'Offs of current DTA
    FLName$ = FileName$ + CHR$(0) 'ASCIIZ filename
    InRegs.AX = &H4E * 256
    InRegs.CX = Attr%             'Attribute
    InRegs.DS = VARSEG(FLName$)   'Segment of ASCIIZ filename
    InRegs.DX = SADD(FLName$)     'Offset of ASCIIZ filename
  END IF
 
  InterruptX &H21, InRegs, OutRegs 'Actually DO IT

  IF OutRegs.Flags AND 1 THEN   'Error!
    FindFirst = OutRegs.AX      '2h=Invalid path
                                '12h=No (more) matching file(s)
    EXIT FUNCTION
  END IF
  FindFirst% = 0                'Ok, we've got something...

  DEF SEG = DTASeg              'Set PEEK up to get info from DTA

  'Now fill in our FileType structure with info from DTA

  FileData.Attr = PEEK(DTAOff + 21)   'Attribute of file
 
  'Calculate the file size in bytes
 
  FileData.FSize = PEEK(DTAOff + 26) + (PEEK(DTAOff + 27) * 256&)
  FileData.FSize = FileData.FSize + ((PEEK(DTAOff + 28) +_
   (PEEK(DTAOff + 29) * 256)) * 65536)
 
  'Get the filename
 
  FLName$ = ""
  FOR X = 30 TO 42
    IF PEEK(DTAOff + X) = 0 THEN EXIT FOR
    FLName$ = FLName$ + CHR$(PEEK(DTAOff + X))
  NEXT
  FileData.FName = FLName$
 
  'Convert date/timestamp into something we can use directly

  FTime = PEEK(DTAOff + 22) + (PEEK(DTAOff + 23) * 256&)
  FDate = PEEK(DTAOff + 24) + (PEEK(DTAOff + 25) * 256&)
  Temp = FTime / 32
  Min = Temp MOD 64
  Hour = Temp / 64
  Temp = FDate
  Day = Temp MOD 32
  Temp = Temp / 32
  Month = Temp MOD 16
  Year = Temp / 16 + 79
  FileData.FileTime = RIGHT$("0" + MID$(STR$(Hour), 2), 2) + ":" +_
   RIGHT$("0" + MID$(STR$(Min), 2), 2)
  FileData.FileDate = RIGHT$("0" + MID$(STR$(Month), 2), 2) + "/" +_
   RIGHT$("0" + MID$(STR$(Day), 2), 2) + "/" + RIGHT$("0" +_
   MID$(STR$(Year), 2), 2)
     
END FUNCTION
