'===========================================================================
' Subject: Search for Files                   Date: 06-11-03 (  :  )       
'  Author: Dieter Folger                      Code: PB                     
'  Origin: ba2049@bnv-bamberg.de            Packet: DISK.ABC
'===========================================================================
 '----------------------------------------------
 ' PBSEARCH.BAS is a utility for text search
 ' in one or more files with many options.
 ' Converted to PowerBasic by Dieter Folger
 ' Adapted from a QuickBasic program by an
 ' unnamed author.
 '----------------------------------------------
 $STACK 32766
 $LIB ALL OFF
 DEFINT A-Z

 %FALSE = 0
 %TRUE = NOT %FALSE
 %MaxExcl = 10

 DIM Buff$(100): DIM p$(100)
 DIM Excl$(%MaxExcl)

 ' Set default options
 Root = %FALSE
 Subdirs = %FALSE
 IgnoreCase = %TRUE
 ExclCount = 0
 Pause = %TRUE
 MatchCount = 0
 ON ERROR RESUME NEXT
 '**********************
 ' Program starts here
 '**********************
 OrigDir$ = CurDir$
 GetParms
 IF IgnoreCase THEN
    SearchText$ = UCASE$(SearchText$)
 END IF
 IF Root THEN
    Path$ = LEFT$(OrigDir$,3)
 ELSE
    Path$ = OrigDir$ +"\"
 END IF
 OPEN "report.lst" for output AS #1
 FindFile Path$, FileSpec$
 Finish
 COLOR 7,0
 END
 '**********************
 '  Get information from
 '  the user
 '**********************
 SUB GetParms PUBLIC SHARED
    COLOR 15,0 : CLS
    LOCATE 1,1
    PRINT "File(s) to search:"
    PRINT "(You may use wildcards)"
    LOCATE 4,1
    PRINT "Text to find:"
    LOCATE 1,19: LINE INPUT " --> ", FileSpec$

    IF LEN(FileSpec$) = 0 THEN END
    IF INSTR(FileSpec$, ".") = 0 THEN
       FileSpec$ = FileSpec$ + ".*"
    END IF

    LOCATE 4,15
    LINE INPUT " --> ", SearchText$
    IF LEN(SearchText$) = 0 THEN END

    LOCATE 7, 1
    PRINT "Enter number of option you want to change."
    PRINT "Press <RETURN> when you are done or <ESC> to end program"
    PRINT
    PRINT
    PRINT "       1. Include subdirectories in the search"
    PRINT "       2. Start search in root directory"
    PRINT "       3. Ignore case while searching"
    PRINT "       4. Exclude .EXE and .COM files from the search"
    PRINT "       5. Exclude other files from the search"
    PRINT "       6. Pause to display each match found"
    DO
       LOCATE 11, 3
       IF Subdirs THEN PRINT "=>";  ELSE PRINT "  ";
       LOCATE 12, 3
       IF Root THEN PRINT "=>";  ELSE PRINT "  ";
       LOCATE 13, 3
       IF IgnoreCase THEN PRINT "=>";  ELSE PRINT "  ";
       LOCATE 14, 3
       IF Excluded("EXE") AND Excluded("COM") THEN PRINT "=>";  ELSE PRINT "  ";
       LOCATE 16, 3
       IF Pause THEN PRINT "=>";  ELSE PRINT "  ";

       LOCATE 18, 1 : PRINT "Excluded files: ";
       PRINT STRING$(%MaxExcl * 6, " ");
       LOCATE 18, 17
       FOR Lp = 1 TO ExclCount
          PRINT "*."; Excl$(Lp); " ";
       NEXT Lp

       DO
          Char$ = INPUT$(1)
       LOOP UNTIL INSTR(CHR$(13) + CHR$(27) + "123456", Char$)
       SELECT CASE Char$
          CASE "1"
          Subdirs = Subdirs XOR %TRUE
          CASE "2"
          Root = Root XOR %TRUE
          IF Root THEN
             Subdirs = %TRUE
          END IF
          CASE "3"
          IgnoreCase = IgnoreCase XOR %TRUE
          CASE "4"
          IF Excluded("EXE") THEN
             DelExclude ("EXE")
             DelExclude ("COM")
          ELSE
             IF ExclCount + 2 <= %MaxExcl THEN
                AddExclude ("EXE")
                AddExclude ("COM")
             ELSE
                Temp$ = Prompt$("Excluded file list is full")
             END IF
          END IF
          CASE "5"
          FileExt$ = Prompt$("Enter file extension to add or remove from the list")
          FileExt$ = LTRIM$(RTRIM$(UCASE$(FileExt$)))
          WHILE INSTR(FileExt$, ".")
             FileExt$ = MID$(FileExt$, INSTR(FileExt$, ".") + 1)
          WEND
          IF LEN(FileExt$) > 0 AND LEN(FileExt$) <= 3 THEN
             IF Excluded(FileExt$) THEN
                DelExclude (FileExt$)
             ELSE
                IF ExclCount < %MaxExcl THEN
                   AddExclude (FileExt$)
                ELSE
                   FileExt$ = Prompt$("Excluded file list is full")
                END IF
             END IF
          END IF
          CASE "6" : Pause = Pause XOR %TRUE
          CASE CHR$(27) : END
          CASE ELSE
       END SELECT
    LOOP UNTIL Char$ = CHR$(13)
    CLS
 END SUB

 FUNCTION Prompt$ (Text$) PUBLIC SHARED
    OrigLine = CSRLIN
    OrigPosn = POS(0)
    LOCATE 23, 1
    PRINT Text$;
    LINE INPUT " ==> ", Temp$
    LOCATE 23, 1
    FOR Lp = 1 TO LEN(Text$) + LEN(Temp$) + 5
       PRINT " ";
    NEXT Lp
    LOCATE OrigLine, OrigPosn
    Prompt$ = Temp$
 END FUNCTION
 '**********************
 '  Maintain list of
 '  excluded files
 '**********************
 SUB AddExclude (Ext$) PUBLIC SHARED
    IF Excluded(Ext$) = %FALSE AND ExclCount < %MaxExcl THEN
       INCR ExclCount
       Excl$(ExclCount) = Ext$
    END IF
 END SUB

 SUB DelExclude (Ext$) PUBLIC SHARED
    Found = %FALSE
    FOR Lp = 1 TO ExclCount
       IF Excl$(Lp) = Ext$ THEN
          Found = %TRUE
          FOR lp2 = Lp TO ExclCount - 1
             Excl$(lp2) = Excl$(lp2 + 1)
          NEXT lp2
       END IF
    NEXT Lp
    IF Found THEN
       ExclCount = ExclCount - 1
    END IF
 END SUB

 FUNCTION Excluded (Ext$) PUBLIC SHARED
    FOR Lp = 1 TO ExclCount
       IF Excl$(Lp) = Ext$ THEN
          Excluded = %TRUE
          EXIT FUNCTION
       END IF
    NEXT Lp
    Excluded = %FALSE
 END FUNCTION
 '**********************
 '  Find files to search
 '**********************
 '---------------------------------------------
 SUB FindFile (Path$, FileSpec$) PUBLIC SHARED
 '---------------------------------------------
  Again:
    SaveDta
    f$ = DIR$ (Path$ + FileSpec$, 7)
    WHILE LEN (f$)
       SearchFile Path$ + F$
       f$ = DIR$
    WEND
    d$ = DIR$ (Path$ + "*.*", 55)
    INCR d: p$(d) = Path$
    DO
       IF LEN (d$) AND (Attr% AND 16) = 16 THEN
          Path$ = RTRIM$(Path$,"\") + "\" + d$ + "\"
          GOTO Again
       END IF
       d$ = DIR$
       IF d$ = "" THEN
          DECR d
          Path$ = p$ (d)
          RestoreDta
       END IF
    LOOP UNTIL d = 0
 END SUB
 '----------------------------------
 SUB GetDTA PUBLIC SHARED
 '----------------------------------
  REG 1, &H2F00: CALL INTERRUPT &H21
  DtaSeg& = REG(9): DtaOfs& = REG(2)
 END SUB
 '----------------------------------
 FUNCTION Attr% PUBLIC SHARED
 '----------------------------------
  GetDta
  DEF SEG = DtaSeg&
  Attr% = PEEK(DtaOfs& + 21)
  DEF SEG
 END FUNCTION
 '----------------------------------------
 SUB SaveDta PUBLIC SHARED
 '----------------------------------------
  GetDta
  DEF SEG = DtaSeg&
  Buff$(d) = PEEK$(DtaOfs&, 21)
  DEF SEG
 END SUB
 '---------------------------------------
 SUB RestoreDta PUBLIC SHARED
 '---------------------------------------
  GetDta
  DEF SEG = DtaSeg&
  POKE$ DtaOfs&, Buff$(d)
  DEF SEG
 END SUB
 '**********************
 ' Search a file for text
 ' These routines make no
 ' assumptions about the
 ' file format
 '**********************
 SUB SearchFile (File$) PUBLIC SHARED
    BufLen = 8192
    MatchFlag = 0
    FOR Lp = 1 TO ExclCount
       IF INSTR(File$, "." + Excl$(Lp)) THEN
          EXIT SUB
       END IF
    NEXT Lp
    CLS : PRINT "Searching "; File$
    FileNum = FREEFILE
    OPEN File$ FOR BINARY AS FileNum
    INCR FilesSearched
    FileLen& = LOF(FileNum)
    FilePosn& = 1
    KeepLen = LEN(SearchText$) - 1
    FileBuf$ = STRING$(BufLen, 0)
    MatchFlag = %FALSE
    DO WHILE FilePosn& < FileLen&
       SEEK FileNum, FilePosn&
       GET$ FileNum, BufLen, FileBuf$
       IF IgnoreCase THEN
          FileBuf$ = UCASE$(FileBuf$)
       END IF
       Posn = INSTR(FileBuf$, SearchText$)
       IF Posn THEN
          IF MatchFlag THEN
          ELSE
             MatchFlag = %TRUE
             INCR MatchCount
             Temp$ = File$
          END IF
          INCR MatchEntry
          CLS
          PRINT "Found in "; File$; " at byte"; FilePosn& + Posn - 1
          PRINT #1 ,"Found in ";FILE$;" at byte"; Fileposn& + Posn - 1
          IF Pause THEN
             Display FileNum, FilePosn& + Posn - 1, FileLen&
             COLOR 15, 1
             PRINT " (C)ontinue  (N)ext File  (E)nd "
             DO
               Char$ = UCASE$(INPUT$(1))
               IF Char$ = CHR$(13) THEN Char$ = "C"
             LOOP UNTIL INSTR("CNE", Char$)
             COLOR 15, 0
             IF Char$ = "E" THEN
                Finish
             ELSEIF Char$ = "N" THEN
                CLOSE FileNum
                EXIT SUB
             END IF
          END IF
          FilePosn& = FilePosn& + Posn + KeepLen
       ELSE
          FilePosn& = FilePosn& + BufLen - KeepLen
       END IF
    LOOP
    CLOSE FileNum
 END SUB

 SUB Display (FileNum, DisplayPosn&, FileLen&) PUBLIC SHARED
    Prefix& = DisplayPosn& - 1
    IF Prefix& > 200 THEN Prefix& = 200
    IF Prefix& THEN
       Disp$ = STRING$(Prefix&, 32)
       StartPosn& = DisplayPosn& - Prefix&
       SEEK FileNum, StartPosn&
       GET FileNum, , Disp$
       NicePrint Disp$
    ELSE
       SEEK FileNum, 1
    END IF

    COLOR 15, 1
    Disp$ = SearchText$
    GET FileNum, , Disp$
    NicePrint Disp$
    COLOR 15, 0

    Suffix& = FileLen& - SEEK(FileNum) + 1
    IF Suffix& > 200 THEN Suffix& = 200
    IF Suffix& THEN
       Disp$ = STRING$(Suffix&, 32)
       GET FileNum, , Disp$
       NicePrint Disp$
    END IF
    NicePrint CHR$(13)
 END SUB

 SUB NicePrint (Text$) PUBLIC SHARED
    PrntPosn = 1
    DO
       Char$ = MID$(Text$, PrntPosn, 1)
       IF Char$ => " " OR Char$ = CHR$(9) THEN
          PRINT Char$;
          INCR PrntPosn
       ELSEIF Char$ = CHR$(13) OR Char$ = CHR$(10) THEN
          PRINT
          INCR PrntPosn
          DO WHILE MID$(Text$, PrntPosn, 1) = CHR$(13) OR MID$(Text$, PrntPosn, 1) = CHR$(10)
             INCR PrntPosn
          LOOP
       ELSE
          PRINT ".";
          INCR PrntPosn
       END IF
    LOOP UNTIL PrntPosn > LEN(Text$)
 END SUB
 '**********************
 ' Report final statistics
 '**********************
 SUB Finish PUBLIC SHARED
    PRINT SearchText$; " found in"; MatchCount; "of"; FilesSearched; "files"
    PRINT "See REPORT.LST for details"
    CLOSE
    IF Root THEN
       CHDIR OrigDir$
    END IF
 END SUB


