; FIND v4.0 10/10/83 ; ; This program finds a string of characters in an ASCII file. Optional ; line numbers are available by appending [N] (similar to PIP.COM) at ; the end of the string to be located. Example: ; ; B>FIND HELLO.ASM LABEL[N] (also [N may be used; also small n) ; ; May take a generic file name, thus may search all *.ASM files on a ; disk. Also very useful for finding things in MAST.CAT - for example ; if you are looking for all MODEM or BYE Programs you could use: ; ; B>FIND MAST.CAT MOD|BYE ; ; Special Search features of FIND are; ; ; 1. Use "_" to match a tab character ; 2. Use "|" for "or" (example:) ; ; A>FIND B:HELLO.* _IN_|_OUT_ ; ; ; COMMAND FORMATS; ; ; B>FIND HELLO.ASM <--- will prompt for search string. ; ; B>FIND HELLO.ASM MODEM <--- will display upper and lower case of ; string using the file(s) specified. ; B>FIND HELLO.ASM <--- ill ask for the string, and if lower ; case will display only occurances found ; in lower case. ; B>FIND MAST.CAT hello[N] <--- finds all upper/lower case words with ; "HELLO" in them, includes line numbers ; ; FN.FT may be Ambiguous, i.e., *.ASM or *.* ; or A*.* or MDM7??.A?M or CBBS*.ASM, but no ; wildcards may be used in the string area. ; ;======================================================================= ; ; 10/10/83 Major change: If wildcards are used, all file names appear ; as they are checked for contents. Wild card system changed ; to allow proper display of help guide. Enhanced help guide. ; - Irv Hoff ; ; 09/23/83 May now be assembled with any normal assembler including ; v3.0 ASM.COM, LASM.COM, MAC.COM, etc. Optional line numbers by ; adding [N] or [N (accepts either lower or upper case) at the ; end of the string to find. - Irv Hoff ; ; 10/05/82 Some enhancements added (not specified). ; v2.0 - Rich Angelo ; ; v1.0 Originally written by: - Ward Christensen ; ;*********************************************************************** ; ; ; EQUATES ; CR: EQU 0DH ;carriage return LF: EQU 0AH ;line feed EOF: EQU 1AH ;end of file char. LBRKT: EQU 5BH ;left bracket STRLEN: EQU 45 ;max. length of string TAB: EQU 09H ;tab character ; ; ; BDOS EQUATES ; RDCON: EQU 1 ;read console WRCON: EQU 2 ;write to console PRINT: EQU 9 ;print string RSTRING:EQU 10 ;read console buffer string CONST: EQU 11 ;get console status OPEN: EQU 15 ;open a file CLOSE: EQU 16 ;close a file SRCHF: EQU 17 SRCHN: EQU 18 ERASE: EQU 19 ;delete a file READ: EQU 20 WRITE: EQU 21 MAKE: EQU 22 REN: EQU 23 STDMA: EQU 26 ;set DMA to specified address ; BDOS: EQU 5 FCB: EQU 5CH FCB2: EQU 6CH FCBEXT: EQU FCB+12 FCBRNO: EQU FCB+32 TBUFF: EQU 80H ;..... ; ; ORG 100H ; ; JMP START ; ; ; DATA AREA ; PGMID: DB '->FIND v4.0 10/10/83',CR,LF,CR,LF,'$' ; ABORT: DB CR,LF,'+++ ABORTED +++',CR,LF,'$' INDENT: DB '->','$' NOFILE: DB '+++ No file named ' FNAME: DB 'xxxxxxxx.xxx' CRLF: DB CR,LF,'$' PNTLN: DB 0 ;line number option PROMPT: DB 'Enter String -->$' WCARD DB 0 ; ; ; DBYTE FIELDS ; EFCB: DW BUFF ;buffer address EFCBCT: DW 0 ;bytes left DB 20 ;buffer size (in pages) DW FCB ;FCB address ; ; ; MFNAME FIELDS ; MFFLG1: DB 0 ;1st time switch MFREQ: DS 12 ;request name MFCUR: DS 12 ;current name DB '$' ; CONBUF: DB CONLEN ;length of console buffer CONSIZ: DS 1 ;resulting size after read STRING: DS STRLEN ;what to search for CONLEN: EQU $-CONSIZ LINENO: DB ' .',TAB,'$';line number ; DS 32 ;stack area STACK: DS 2 STRPTR: DS 2 ;pointer for "|" scan LINE: DS 133 ;..... ; ; ;======================================================================= ; ; PROGRAM STARTS HERE ; ;======================================================================= ; ; Save stack, print sign-on, look for file specification. ; START: LXI H,0 ;save stack DAD SP SHLD STACK LXI SP,STACK ;use our own stack LXI D,PGMID CALL XPRINT LDA TBUFF ;any file named? ORA A JZ ERRNAM ;if not, display help guide LDA TBUFF+2 CPI '?' ;first chararter typed a '?' JNZ SETEND ;if not, continue LDA TBUFF+3 ;check next character after '?' ORA A JZ ERRNAM ; ; ; Set string pointers, and end delimiter. ; SETEND: LXI D,TBUFF LDAX D ;length MOV C,A ;save length MVI B,0 ;setup bc for move INX D ;=TBUFF+1, start of string MOV L,A ;l=length MVI H,0 ;HL=length DAD D ;HL=last characrter MVI M,0 ;store end delimiter XCHG ;start to HL PUSH H CALL LBRKTN ; ; See if any wildcards were used ; LXI H,TBUFF+1 ; CKWC: INX H MOV A,M ORA A JZ CKWC2 CPI ' ' JZ CKWC2 CPI '?' JZ CKWC1 CPI '*' JNZ CKWC ; CKWC1: STA WCARD ; CKWC2: POP H ; ; ; Look for string, if found save it. ; SCAN: INX H ;to next char MOV A,M ;look for ' ' ORA A ;end? JZ GETSTR ;..yes, then get it from the console CPI ' ' ; JNZ SCAN ;not at ' ' INX H ;to string LXI D,STRING CALL MOVE CALL FRSTFI ;see if file exists JMP OPFILE ;go process it ; ; ; Accept string from console. ; GETSTR: CALL FRSTFI ;look for file LXI D,PROMPT CALL XPRINT ; PUSH B PUSH D PUSH H MVI C,RSTRING LXI D,CONBUF CALL BDOS POP H POP D POP B ; LDA CONSIZ ORA A JZ EXIT MOV L,A ;store delimiter MVI H,0 LXI D,STRING DAD D MVI M,0 PUSH H LXI H,STRING CALL LBRKTN POP H LXI D,CRLF CALL XPRINT CALL XPRINT ;send a 2nd CRLF JMP OPFILE ;process file ;..... ; ; ; Search for initial file and print it's name. ; FRSTFI: CALL MFNAME ;see if file exists RNC ;if yes, go process it LXI H,FCB+1 ;if not, move requested name to 'FNAME' LXI D,FNAME ;..and display error message LXI B,8 CALL MOVE LXI H,FCB+9 LXI D,FNAME+9 LXI B,3 CALL MOVE LXI D,NOFILE CALL XPRINT JMP EXIT ;..... ; ; ; Look for another file, if none then exit. ; NEXTFL: CALL MFNAME JC EXIT LXI D,CRLF CALL XPRINT ; ; ; Open file and print name. ; OPFILE: PUSH B PUSH D PUSH H MVI C,OPEN LXI D,FCB CALL BDOS POP H POP D POP B INR A JZ EXIT ; ; ; See if a wildcard was used, if yes, print file names for each file ; LDA WCARD ORA A JZ OPFIL1 ;no wild cards uses, then exit ; LXI D,INDENT ;else print name of each file checked CALL XPRINT LXI D,MFCUR CALL XPRINT LXI D,CRLF CALL XPRINT OPFIL1: LXI D,MFREQ CALL OPFIL2 ; OPFIL2: DB ' 0' ; OPFIL3: POP H ;get from LXI B,OPFIL3-OPFIL2 ;get length LXI D,LINENO CALL MOVE LXI H,FCB+1 LXI D,FNAME LXI B,8 CALL MOVE LXI H,FCB+9 LXI D,FNAME+9 LXI B,3 CALL MOVE LXI H,0 SHLD EFCBCT ; ; ; Set up next line number. ; NEXTLN: LXI H,LINENO+3 ; NEXT01: MOV A,M ;get digit ORI '0' ;make ascii INR A MOV M,A CPI '9'+1 ;carry? JNZ NEXTNC MVI M,'0' DCX H JMP NEXT01 ; ; ; Read a line from file. ; NEXTNC: LXI H,LINE MVI B,0FFH ;so long line will not blow ; NEXT02: INR B JM LONG ;too long a line ; PUSH B PUSH H LXI H,EFCB CALL RDBYTE POP H POP B ; MOV M,A INX H CPI EOF JZ NEXTFL ;next file CPI LF JNZ NEXT02 JMP EOL ; ; ; Got a long line, chop it off. ; LONG: MVI M,CR INX H MVI M,LF ; ; ; Check for operator abort, point to string. ; EOL: PUSH B PUSH D PUSH H MVI C,CONST CALL BDOS POP H POP D POP B ; ORA A JNZ CHRXIT ;abort requested LXI H,STRING ; ; ; We have a line, now scan for the string. ; ORLINE: SHLD STRPTR LXI H,LINE ; NEXTST: XCHG LHLD STRPTR XCHG PUSH H ; ; ; Replace '_' with a tab. ; NEXTC: LDAX D CPI '_' JNZ NOTAB MVI A,TAB ; NOTAB: INX D ORA A ;end of string? JZ MATCHED CPI '|' JZ MATCHED ;first part MOV C,M ;for lower case test CMP M INX H JZ NEXTC MOV B,A ;save character MOV A,C ;get character CPI 61H ;lower? JC NOTEQ ;no, so no match CPI 7BH JNC NOTEQ ANI 5FH ;make upper case CMP B JZ NEXTC ;matched ; NOTEQ: POP H ;restore address INX H MOV A,M CPI CR JNZ NEXTST LHLD STRPTR ; ; ; If an 'OR' (|) is in the line, scan for it. ; FINDOR: MOV A,M INX H CPI '|' JZ ORLINE ORA A JNZ FINDOR JMP NEXTLN ; ; ; Got a match print it. ; MATCHED:POP H ;kill stacked addrress LDA PNTLN ;going to print line numbers? ORA A JZ MATCHN ;if not, skip next line LXI D,LINENO CALL XPRINT ; MATCHN: LXI H,LINE ; MATCHLP:MOV A,M MOV E,A ; PUSH B PUSH D PUSH H MVI C,WRCON CALL BDOS POP H POP D POP B ; MOV A,M INX H CPI LF JNZ MATCHLP JMP NEXTLN ;..... ; ; ;======================================================================= ; ; SUBROUTINES ; ;======================================================================= ; ; ; Read keyboard, print abort message. ; CHRXIT: PUSH B PUSH D PUSH H MVI C,RDCON CALL BDOS POP H POP D POP B ; LXI D,ABORT CALL XPRINT JMP EXIT ;..... ; ; ; Restore stack and return to cp/m. ; EXIT: LHLD STACK SPHL RET ;to CCP ;..... ; ; ; Error routine if no file name is given ; ERRNAM: MVI C,PRINT LXI D,ERROR$NAME CALL BDOS JMP EXIT ; ERROR$NAME: DB CR,LF,'+++ No file name given +++',CR,LF DB CR,LF,' Examples of how to use:',CR,LF DB CR,LF,' B>FIND MAST.CAT MOD' DB CR,LF,' B>FIND MAST.LST MOD|BYE' DB CR,LF,' B>FIND MAST.ASM CAT[N]' DB CR,LF,' B>FIND MAST.CAT' DB CR,LF,' B>FIND MDM???.ASM' DB CR,LF,' B>FIND *.* SPHL',CR,LF DB CR,LF,' If no string is included, one will be ' DB 'requested;',cr,lf,' this permits a search for ' DB 'lower-case only chars.',CR,LF,CR,LF,' [N] or [N ' DB 'at end of the string adds line numbers.',CR,LF DB CR,LF,'$' ;..... ; ; ; Error routine if using a delimiter (but not followed by N) ; ERRORN: MVI C,PRINT LXI D,ERRORN$MSG CALL BDOS JMP EXIT ; ERRORN$MSG: DB CR,LF,CR,LF,'+++ need [N] or [N to ' DB 'print line numbers +++',CR,LF,'$' ;..... ; ; ; Checks to see if [N] or [N was used to request line numbers ; LBRKTN: MOV A,M ;get the character from TBUFF ORA A RZ ;if zero, all done looking INX H ;next character in string CPI LBRKT ;a left bracket to print line numbers? JNZ LBRKTN ;if not, keep looking MOV A,M ;check character following left bracket CPI 'N' ;want to print line numbers? JZ LBRK1 CPI 'N'+20H ;small 'n' to print line numbers? JNZ ERRORN ;if not, is an error ; LBRK1: DCX H DCX B MVI M,0 ;set the buffer to zero at end of string STA PNTLN JMP LBRKTN ;next character is likely a zero to quit ;..... ; ; ; Mult-file access subroutine. ; ; Allows processing of multiple files (i.e. *.ASM) from disk. This rou- ; tine builds the proper name in the FCB each time it is called. This ; command would be used in such programs as modem transfer, tape save, ; etc. in which you want to process single or multiple files. ; ; Just call 'MFNAME' (multiple file name) and the FCB will be set up ; with the next name, ready to do normal processing (open, read, etc.) ; ; Carry is set if no more names can be found ; MFNAME: LXI D,80H CALL XSTDMA XRA A STA FCBEXT STA FCBRNO LDA MFFLG1 ;if first time ORA A JNZ MFN01 MVI A,1 ;turn off 1st time switch STA MFFLG1 LXI D,MFREQ LXI H,FCB LXI B,12 CALL MOVE LDA FCB STA MFCUR LXI H,MFREQ LXI D,FCB LXI B,12 CALL MOVE CALL XSRCHF JMP MFN02 ;else ; MFN01: LXI H,MFCUR LXI D,FCB LXI B,12 CALL MOVE CALL XSRCHF LXI H,MFREQ LXI D,FCB LXI B,12 CALL MOVE PUSH B PUSH D PUSH H MVI C,SRCHN LXI D,FCB CALL BDOS POP H POP D POP B ; MFN02: INR A ;return carry if not found STC RZ DCR A ;move name found to current ANI 3 ADD A ADD A ADD A ADD A ADD A ADI 81H MOV L,A MVI H,0 PUSH H LXI D,MFCUR+1 LXI B,11 CALL MOVE POP H ;move name found to FCB LXI D,FCB+1 LXI B,11 CALL MOVE XRA A ;setup FCB STA FCBEXT RET ;return ;..... ; ; ; Move a group of characters to another location routine ; MOVE: MOV A,B ORA C RZ ; MOV A,M STAX D INX H INX D DCX B JMP MOVE ;..... ; ; ; Read byte from file. ; HL points to EFCB: ; EFCB: ; 2 byte buffer address ; 2 byte "bytes left" (init to 0) ; 1 byte buffer size (in pages) ; 2 byte fcb address ; RDBYTE: MOV E,M ;de = buffer address INX H ;x MOV D,M ;x INX H ;bc = bytes left MOV C,M ;x INX H ;x MOV B,M ;x MOV A,B ;if byte-count not zero ORA C ;..go read next byte JNZ RDGETB ; else.. INX H ;..read another sector. MOV A,M ;get count ADD A ;multiply by 2 MOV B,A ;sector count in b INX H ;to FCB PUSH H ;save FCB pointer MOV A,M ;get.. INX H ;..FCB.. MOV H,M ;..addrress.. MOV L,A ;..to HL ; RDBLP: MVI A,EOF ;put EOF character in buffer STAX D ;..in case of EOF. ; PUSH D ;save DMA address PUSH H ;save FCB address CALL XSTDMA POP D ;get FCB ; PUSH B PUSH D PUSH H MVI C,READ CALL BDOS POP H POP D POP B ; ORA A ;check for EOF POP H ;LH-DMA, DE=FCB JNZ RDBRET ;got EOF MOV A,L ;bump buffer pointer ADI 80H ;to next buffer MOV L,A ;x MOV A,H ;x ACI 0 ;x MOV H,A ;x XCHG ;DMA to DE, FCB to HL DCR B ;more sectors? JNZ RDBLP ;yes, more ; RDBRET: POP H ;get FCB pointer DCX H ;to length MOV A,M ;get length DCX H ;to count MOV M,A ;set page count DCX H ;to low count DCX H ;to high FCB DCX H ;to EFCB start JMP RDBYTE ;loop through again ; ; RDGETB: INX H ;point to buffer size MOV A,M ;get length (pages) XCHG ;buffer to HL ADD H ;HL = end of buffer MOV H,A ;x MOV A,L ;x SUB C ;HL = data pointer MOV L,A ;x MOV A,H ;x SBB B ;x MOV H,A ;x MOV A,M ;get byte XCHG ;EFCB pointer back to HL CPI EOF ;EOF? RZ ;yes, leave pointers DCX B ;decrement the count DCX H ;point back to "bytes left" MOV M,B ;store back count DCX H ;x MOV M,C ;x RET ;return to caller ;..... ; ; ; Print routine ; XPRINT: PUSH B PUSH D PUSH H MVI C,PRINT CALL BDOS POP H POP D POP B RET ;..... ; ; ; Search for file routine ; XSRCHF: PUSH B PUSH D PUSH H MVI C,SRCHF LXI D,FCB CALL BDOS POP H POP D POP B RET ;..... ; ; ; Set the DMA address ; XSTDMA: PUSH B PUSH D PUSH H MVI C,STDMA CALL BDOS POP H POP D POP B RET ;..... ; ; BUFF: EQU $ ;disk read bufer ; ; END