
;			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
