CODE_SEG        SEGMENT
        ASSUME  CS:CODE_SEG,DS:CODE_SEG,ES:CODE_SEG
        ORG     100H                    ;Start off right for a .COM file
ENTRY:  JMP     LOCATE                  ;Skip over Data area

        COPY_RIGHT DB '(C)1985 S.Holzner'       ;Author's Mark
        FOUND_MSG DB 13,10,13,10,'FOUND IN $'   ;Like it says
        LEN             DW 1                    ;The file length (low word)
        PATH_LEN        DW 0                    ;Length of Path.Dat
        NUMBER          DW 0                    ;Number of bytes read from file
        EXTRA_PATHS     DB 0                    ;=1 if we open & use Path.Dat
        OLD_BX          DW 0                    ;Save pointer to path at CS:DBH
        OLD_SI          DW 0                    ;Save SI as pointer also
        START_FLAG      DB 0                    ;For searches in Path.Dat
        PATH_DAT        DB "\PATH.DAT",0        ;ASCIIZ string of Path.Dat

LOCATE  PROC    NEAR            ;Here we go

        MOV     DX,0B0H         ;Move Disk Transfer Area to CS:0B0H
        MOV     AH,1AH          ;Matched file information goes there
        INT     21H

        MOV     DI,5CH          ;Use CS:5CH to put '*.*'0 at for search
        CALL    PUT             ; in current directory
        MOV     DX,5CH          ;Point to '*.*'0 for search
        MOV     AH,4EH          ; and find first matching file
        INT     21H             ;Match now at DTA, 0B0H
LOOP1:                          ;Loop over matches now
        MOV     BX,0CAH         ;Get file length, came from match
        MOV     DX,[BX]
        MOV     LEN,DX          ;Store in Len
        CMP     DX,60*1024      ;Don't write over stack, allow < 64K files
        JB      NOT_BIG         ;Range extender (Find > 127 bytes ahead)
        JMP     FIND
NOT_BIG:CMP     DX,0            ;Was this a 0 length file (disk dir or label)?
        JA      FILE_OK         ;No, go on and read it
        JMP     FIND            ;Yes, find next file and skip this one
FILE_OK:CALL    READ_FILE       ;Get the file into memory
        MOV     CX,NUMBER       ;Prepare to loop over all read bytes
        MOV     DI,OFFSET PATHS+300     ;File starts at Offset Paths+300
SEARCH:                                 ;Use Repne Scasb & DI to search for the
        MOV     BX,82H          ;first letter of the string, which is at CS:82H
        MOV     AL,BYTE PTR [BX]        ;Load into AL for Repne Scasb
REPNE   SCASB                           ;Find first letter
        JCXZ    FIND            ;If out of file to search, find next file
        MOV     BX,80H          ;How many chars in string? Get from CS:80H
        XOR     DX,DX           ;Set DX to zero
        MOV     DL,[BX]         ;Get # of chars in string
        DEC     DX              ;Get rid of space typed after 'Locate'
        MOV     SI,83H          ;Search from second typed letter (1st matched)
CPLOOP: DEC     DX              ;Loop counter
        CMPSB                   ;See how far we get until no match
        JZ      CPLOOP
        DEC     DI              ;At end, reset DI (Scasb increments 1 too much)
        CMP     DX,0            ;If DX is not zero, all letters did not match
        JA      SEARCH          ;If not a match, go back and get next one
        LEA     DX,FOUND_MSG    ;FILE HAS BEEN FOUND, so say so.
        MOV     AH,9            ;Request string search
        INT     21H
        MOV     AH,2            ;Now to print filename. Without Path.Dat, at
        MOV     BX,0DBH         ; CS:CEH, with Path.Dat at CS:DBH
        CMP     EXTRA_PATHS,1   ; Using Path.Dat yet?
        JE      PRINT           ;Yes, print
        MOV     BX,0CEH         ;No, reset BX to point to CS:CEH
PRINT:  MOV     DL,[BX]         ;Print out the filename until 0 found
        CMP     DL,0            ;Is it 0?
        JE      MORE            ;If yes,print out sample at More:
        INT     21H             ;Print filename character
        INC     BX              ;Point to next character
        JMP     PRINT           ;Go back relentlessly until done
MORE:   PUSH    DI              ;Save DI,BX,CX
        PUSH    BX
        PUSH    CX
        MOV     CX,40           ;Prepare to type out total of 40 characters
        MOV     AH,2            ;With Int 21H service 2
        MOV     DL,':'          ;But first, add ':' to filename
        INT     21H             ;And a carriage return linefeed
        MOV     DL,13
        INT     21H
        MOV     DL,10
        INT     21H
        SUB     DI,20           ;DI points to end of found string, move back
        MOV     BX,OFFSET PATHS+300     ;Beginning of file
        CMP     DI,BX           ;If before beginning, start at beginning
        JA      GO
        MOV     DI,BX
GO:     ADD     BX,LEN          ;Now BX=end of file (to check if we're past it)
SHOW:   MOV     DL,[DI]         ;Get character from file
        INC     DI              ;And point to next one
        CMP     DI,BX           ;Past end?
        JA      SHOWS_OVER      ;Yes, jump out, look for next match
        CMP     DL,30           ;Unprintable character?
        JA      POK             ;No, OK
        MOV     DL,' '          ;Yes, make it a space
POK:    INT     21H             ;Print Character
        LOOP    SHOW            ;And return for the next one
SHOWS_OVER:                     ;End of printout
        POP     CX              ;Restore the registers used above
        POP     BX
        POP     DI
        JMP     SEARCH          ;Return to search more of the file
FIND:   CALL    FIND_FILE       ;This file done, find next match
        CMP     AL,18           ;AL=18 --> no match
        JE      OUTX            ;And so we leave
        JMP     LOOP1           ;If match found, go back once again
OUTX:   INT     20H             ;End of Main Program
LOCATE  ENDP

PUT     PROC    NEAR                    ;This little gem puts a '*.*'0
        MOV     BYTE PTR [DI],'*'       ;Wherever you want it--just send
        MOV     BYTE PTR [DI+1],'.'     ; it a value in DI. '*.*'0 is used as
        MOV     BYTE PTR [DI+2],'*'     ; a universal wilcard in searches
        MOV     BYTE PTR [DI+3],0
        RET
PUT     ENDP

WS      PROC    NEAR                    ;Strip the bits for WordStar
        CMP     CX,0                    ;If there is a length of 0, e.g.
        JE      FIN                     ;Directory entries, etc. do nothing.
WSLOOP: AND BYTE PTR [BX],127           ;Set top bit to zero
        INC     BX                      ;Point to next unsuspecting byte
        LOOP    WSLOOP                  ;And get it too.
FIN:    RET                             ;To use, send this program BX and CX
WS      ENDP

FIND_FILE       PROC    NEAR    ;The file finder
        MOV     AH,4FH          ;Try service 4FH, find next match, first
        INT     21H
        CMP     AL,18           ;AL = 18 --> no match
        JE      CHECK           ;Range extender.
        JMP     NEW
CHECK:  CMP     EXTRA_PATHS,1   ;Have we used path.Dat?
        JE      NEXT_PATH       ;Yes, get next path, this one's used up
        INC     EXTRA_PATHS     ;No, set it to 1
        MOV     AX,3D00H        ;Request file opening for Path.Dat
        LEA     DX,PATH_DAT     ;Point to '\PATH.DAT'0
        INT     21H
        JNC     READ            ;If there was a carry, Path.Dat not found
DONE:   MOV     AL,18           ;And so we exit with AL=18
        JMP     ENDX
READ:   MOV     CX,300          ;Assume the max length for Path.Dat, 300.
        MOV     BX,AX           ;Move found file handle into BX for read
        MOV     AH,3FH          ;Set up for file read
        LEA     DX,PATHS        ;Put the file at location Paths (at end)
        INT     21H             ;Read in the file
        ADD     AX,OFFSET PATHS         ;Full offset of end of Path.Dat
        MOV     PATH_LEN,AX     ;Get Path.Dat end point for loop
        MOV     AH,3EH          ;Now close the file
        INT     21H             ;Close file
        MOV     OLD_SI,OFFSET PATHS     ;Save for future path-readings
        MOV     CX,300                  ;Get ready to Un-WordStar
        MOV     BX,OFFSET PATHS         ;300 bytes at location Paths
        CALL    WS                      ;Strip high bit for WS
NEXT_PATH:              ;Come here to find next path to search for files
        MOV     SI,OLD_SI               ;Point to start of next path
        MOV     DI,5CH  ;Move will be to CS:5CH for '\path\*.*0' file find
        MOV     BX,0DBH ;Also to CS:DBH; will assemble full path & filename
        MOV     START_FLAG,0    ;Start the path search
CHAR:   CMP     SI,PATH_LEN     ;Past end of possible path names?
        JGE     DONE            ;Yes, we're done. Leave with AL=18
        CMP     BYTE PTR [SI],30        ;Carriage return or linefeed?
        JB      NEXT                    ;Yes, get next char
        MOV     START_FLAG,1            ;First char, stop skipping chars
        MOV     AL,[SI]                 ;Get char from Path.Dat
        MOV     [BX],AL                 ;Move char to DBH
        INC     BX                      ;And increment to next location there
        MOVSB                           ;Also move to 5CH area
        JMP     CHAR                    ;And go back for more
NEXT:   CMP     START_FLAG,1    ;Bad char, have we been reading a real pathname?
        JE      PDONE           ;Yes, we've reached the end of it.
        INC     SI              ;No, keep skipping chars to find pathname
        JMP     CHAR
PDONE:  MOV     OLD_SI,SI       ;Save SI for the next path.
        MOV     BYTE PTR [DI],'\'       ;Add '\' to both paths
        MOV     BYTE PTR [BX],'\'
        INC     BX                      ;Move BX on for next time
        MOV     OLD_BX,BX               ;And save it.
        INC     DI                      ;Move to next location at 5CH and
        CALL    PUT                     ;Put '*.*'0 there to find all files.
        MOV     DX,5CH                  ;Start the search for all files in
        MOV     AH,4EH                  ; the new path.
        MOV     CX,0                    ;Set the file attribute to 0
        INT     21H
        CMP     AL,18                   ;Did we find any new files in new path?
        JE      NEXT_PATH               ;No, get the next path.
NEW:    CMP     EXTRA_PATHS,1           ;Yes,Move found filename to DBH area to
        JNE     ENDX                    ; read it in-only if DBH area is active
        MOV     BX,OLD_BX               ; (i.e. Extra_Paths=1). Restore BX
        MOV     SI,0CDH                 ;And point to found filename in DTA
CLOOP:  INC     SI                      ;Next letter from found filename
        MOV     AH,[SI]                 ;Move it to the DBH area so we can read
        MOV     [BX],AH                 ; in the file (needs pathname\filename)
        INC     BX                      ;Next character in 5CH area.
        CMP     BYTE PTR [SI],0         ;Is this the last character?
        JNE     CLOOP                   ;Nope, get next one
ENDX:   RET                             ;After path & filename assembled, return
FIND_FILE       ENDP

READ_FILE       PROC    NEAR    ;Looks for filename at CEH or DBH & reads it
        PUSH    AX              ;Push everything to save it.
        PUSH    BX
        PUSH    CX
        PUSH    DX
        MOV     DX,0DBH         ;Try the DBH area
        CMP     EXTRA_PATHS,1   ;Has it been used?
        JE      OK              ;Yes
        MOV     DX,0CEH  ;No, not using paths yet, use filename only, at CEH
OK:     MOV     AX,3D00H        ;Prepare for file reading
        INT     21H             ;And do so.
        MOV     BX,AX           ;Move file handle into BX to read
        MOV     DX,OFFSET PATHS+300     ;Read into data area at Paths+300 bytes
        MOV     CX,LEN                  ;Read the full file's length in bytes
        MOV     AH,3FH                  ;Read it in at last
        INT     21H
        MOV     NUMBER,AX               ;Number of bytes actually read.
        MOV     AH,3EH                  ;Close file
        INT     21H
        MOV     BX,OFFSET PATHS+300     ;Clean up the Word Star high bit.
        MOV     CX,LEN                  ;For the full file
        CALL    WS                      ;Strip high bit for ws
        POP     DX                      ;Pop evrything and return
        POP     CX
        POP     BX
        POP     AX
        RET                             ;Fin of Read_File
READ_FILE       ENDP
PATHS:                                  ;Here's the end of program marker

CODE_SEG        ENDS
        END     ENTRY                   ;End 'Entry' so DOS starts at 'Entry'
