.title find -- find patterns in text .enabl lc ; Search for patterns in text files ; ; D. G. Conroy Sept 7, 1977 .globl $main .mcall call, return, get$s, put$s .mcall fsrsz$, fdbdf$, fdop$a, ofnb$r .mcall close$, csi$ fsrsz$ 3 csi$ ; Equivalences RBSIZE = 120. ;Size of record buffer PBSIZE = 100. ;Size of pattern buffer LBSIZE = 8. ;Size of line number buffer FBSIZE = 40. ;Size of file name buffer ; Pattern matcher opcodes. The order is critical ; but may be changed if the jump table in amatch is ; changed also. END = 0 ;End of pattern CHAR = 2 ;Character BOL = 4 ;Beginning of line EOL = 6 ;End of line ANY = 10 ;Any character CCL = 12 ;Character class NCCL = 14 ;Negated character class CLO = 16 ;Closure ; Local data fdb: fdbdf$ ;Fdb for non stdin files fdop$a 4 count: .word 0 ;Count of matches lno: .blkw 1 ;Line number fflag: .byte 0 ;-f (filename) flag nflag: .byte 0 ;-n (line numbers) flag nfflag: .blkb 1 ;Flag for printing file name rflag: .byte 0 ;-r (reverse) flag cflag: .byte 0 ;-c (count) flag lbuf: .blkb LBSIZE ;Must be just before rbuf!!! rbuf: .blkb RBSIZE ;Working record buffer pbuf: .blkb PBSIZE ;Pattern buffer fmsg: .ascii "File " ;Message for -f option fmsgf: .blkb FBSIZE ; Ascii things msg01: .asciz "x: illegal option" msg02: .asciz "Illegal pattern" msg03: .asciz "Usage: find [-r] [-c] [-f] [-n] pattern [file ...]" msg04: .asciz ": not found" .even ;+ ; ** $main -- mainline ; ; This routine receives control from the standard setup ;- $main: mov $argc,r3 ;Get arg. count mov #$argv,r4 ;Pointer into args. clr r5 ;Number of file args. 10$: dec r3 ;Any more args. ble 50$ ;Br if not tst (r4)+ ;Position to the next arg. mov (r4),r1 ;Get pointer to arg cmpb (r1),#'- ;Is it an option bne 40$ ;Br if not inc r1 ;Skip the '-' 20$: movb (r1)+,r2 ;Pickup option character beq 30$ ;End of list call maplow ;Make lower case (for match) cmpb r2,#'c ;-c bne 22$ ;No incb cflag ;Yes, set count flag br 20$ ;Next 22$: cmpb r2,#'r ;-r bne 24$ ;No incb rflag ;Yes, set reverse flag br 20$ ;Next 24$: cmpb r2,#'f ;-f bne 26$ ;No incb fflag ;Yes, set file name flag br 20$ ;Next 26$: cmpb r2,#'n ;-n bne 28$ ;No incb nflag ;Yes, set line number flag br 20$ ;Next 28$: movb r2,msg01 ;Option is illegal mov #msg01,r0 ;Type out call $msg ;Diagnostic br 20$ ;Get next option 30$: clr (r4) ;Kill this argv entry and br 10$ ;Continue the argv scan 40$: inc r5 ;Count a file argument cmp r5,#1 ;Is this the first one (pattern) bne 10$ ;Br if not clr (r4) ;Kill this argv entry mov r1,r0 ;Compile the call compil ;Pattern bcc 10$ ;If ok, continue mov #msg02,r0 ;Otherwise complain about the pattern call $msg ;And br 100$ ;Exit 50$: dec r5 ;Test if enough arguments bpl 60$ ;Enough mov #msg03,r0 ;Display usage message and call $msg ;Then br 100$ ;Exit 60$: bne 70$ ;Br if files in the argv mov #$stdin,r0 ;Search the call finder ;Standard input br 90$ ;Done 70$: mov #$argv,r4 ;Working argv pointer 80$: dec $argc ;Any more ble 90$ ;Br if not tst (r4)+ ;Get next mov (r4),r0 ;File name beq 80$ ;Killed entry call $mdspt ;Build a dspt bcs 85$ ;Bad file name (or something) mov #fdb,r0 ;Parse the file name mov #fdb+f.fnb,r1 mov #$csibk+c.dsds,r2 mov #$dfnb,r3 call .parse bcs 85$ ;Bad file name bit #nb.svr+nb.stp+nb.snm,n.stat(r1) bne 82$ ;Br on a wildcard ofnb$r ;Open the file bcs 85$ ;Cannot open the file call finder ;Search it close$ ;Close it br 80$ ;And go get another 82$: bit #nb.ver,n.stat(r1) ;Is there an explicit version bne 83$ ;Br if yes bis #nb.svr,n.stat(r1) ;Set ;* 83$: call .find ;Look for a file bcs 80$ ;Error, assume no more files ofnb$r ;Open the file bcs 85$ ;This should never happen call finder ;Search it close$ ;Close it br 83$ ;And try to find another 85$: mov #msg04,r0 ;: not found mov (r4),r1 ;The file name call $fmsg ;Report the error br 80$ ;And continue 90$: tstb cflag ;-c beq 100$ ;No, just leave mov count,r0 ;Write out the count mov #rbuf,r1 call $itoc sub #rbuf,r1 put$s #$stdou,#rbuf,r1 100$: return ;Done ;+ ; ** finder -- perform a find on a file ; ; Inputs: ; r0=pointer to fdb of file ; pbuf=pattern to match ; ; Outputs: ; Matching records are written to standard output ;- finder: mov r0,-(sp) ;Save things mov r1,-(sp) mov r2,-(sp) mov r5,-(sp) mov r0,r5 ;Get fdb pointer in a safe place clrb nfflag ;Is the file message being printed tstb fflag ;Only print if -f beq 10$ cmp r5,#$stdin ;And not standard input beq 10$ incb nfflag ;Need a name 10$: clr lno ;Clear out line number 20$: get$s r5,#rbuf,#RBSIZE;Get a record bcs 90$ ;Error, assume eof inc lno ;Update line number mov f.nrbd(r5),r0 ;Make the record asciz clrb rbuf(r0) tstb rflag ;Reverse sense search bne 30$ ;Br if yes call amatch ;Go back if bcs 20$ ;No match br 40$ 30$: call amatch ;Go back if bcc 20$ ;Match 40$: inc count ;Count a match tstb cflag ;-c bne 20$ ;Yes, don't print tstb nfflag ;Need a file name beq 50$ ;Br if not call filnam ;Put out the file name clrb nfflag 50$: tstb nflag ;Line numbers bne 60$ ;Br if yes put$s #$stdou,#rbuf,f.nrbd(r5) ;Write to standard output br 20$ 60$: mov lno,r0 ;Convert line number mov #lbuf,r1 call $itoc movb #':,(r1)+ ;For looks 70$: cmp r1,#rbuf ;Pad up with blanks bhis 80$ movb #' ,(r1)+ br 70$ 80$: mov f.nrbd(r5),r1 ;Get actual length to write add #LBSIZE,r1 put$s #$stdou,#lbuf,r1;Write it out br 20$ 90$: mov (sp)+,r5 ;Restore registers mov (sp)+,r2 mov (sp)+,r1 mov (sp)+,r0 return ;To caller ;+ ; ** filnam -- put out a file name message ; ; Inputs: ; r5=fdb pointer ;- filnam: mov r0,-(sp) ;Save registers mov r1,-(sp) mov r5,r0 ;Convert name mov #fmsgf,r1 call $fdbta sub #fmsg,r1 ;Compute record size put$s #$stdou,#fmsg,r1;Write it out mov (sp)+,r1 ;Restore, and mov (sp)+,r0 return ;Return ;+ ; ** compil -- compile a text pattern ; ; Inputs: ; r0=pointer to asciz pattern ; ; Outputs: ; pbuf=compiled pattern ; c bit set on error; c bit clear if all ok ;- compil: mov r0,-(sp) ;Save registers mov r1,-(sp) mov r2,-(sp) mov r3,-(sp) mov #pbuf,r1 ;Working pattern pointer 10$: movb (r0)+,r2 ;Get character from pattern beq 110$ ;End of pattern cmpb r2,#'* ;Closure bne 40$ ;Br if not cmp r1,#pbuf ;If nothing to close then beq 120$ ;Error cmpb (r1),#BOL ;^* is illegal beq 120$ cmpb (r1),#EOL ;$* is illegal beq 120$ cmpb (r1),#CLO ;** is illegal beq 120$ mov #END,r2 ;Put end on the closed pattern call putpat call putpat ;Allow room for the CLO bcs 130$ ;Must fit mov r1,-(sp) ;Save pointer to pattern end 20$: dec r1 ;Slide the closed cmp r1,r3 ;Pattern blos 30$ ;Along movb -1(r1),(r1) ;As the CLO opcode br 20$ ;Goes first 30$: movb #CLO,(r1) ;Insert closure opcode mov (sp)+,r1 ;Restore output pointer br 10$ ;Next 40$: mov r1,r3 ;Set pointer to last pattern cmpb r2,#'. ;Any bne 50$ ;No mov #ANY,r2 ;Yes, get opcode br 100$ ;Store 50$: cmpb r2,#'^ ;Beginning of line bne 60$ ;No mov #BOL,r2 ;Yes, get opcode br 100$ ;Store 60$: cmpb r2,#'$ ;End of line bne 70$ ;No mov #EOL,r2 ;Yes, get opcode br 100$ ;Store 70$: cmpb r2,#'[ ;Character class bne 80$ ;No call getcc ;Yes, get character class bcs 130$ ;Bad class br 10$ ;Go for more 80$: cmpb r2,#'\ ;Escape bne 90$ ;No tstb (r0) ;Yes, test for trailing escape beq 90$ ;Treat as literal backslash movb (r0)+,r2 ;Get pattern character 90$: mov r2,-(sp) ;Save character mov #CHAR,r2 ;Put out opcode call putpat ;For character mov (sp)+,r2 ;Get character back call maplow ;Make uniform case (caseless match) 100$: call putpat ;Save in pattern and br 10$ ;Go for more 110$: mov #END,r2 ;Finish off pattern call putpat br 130$ 120$: sec ;Errors enter here 130$: mov (sp)+,r3 ;Restore registers mov (sp)+,r2 mov (sp)+,r1 mov (sp)+,r0 return ;Return to caller ;+ ; ** getcc -- compile a character class specifier. ; ; Inputs: ; r0=pointer to first character in the class block ; r1=pointer into output buffer for pattern ; ; Outputs: ; r0=advanced ; r1=advanced ; r2=junk ;- getcc: mov r3,-(sp) ;Save from our nasty hands mov #CCL,r2 ;Opcode cmpb (r0),#'\ ;Escape at the start beq 10$ ;Yes, cannot be negated class cmpb (r0),#'^ ;Negated class bne 10$ ;No inc r0 ;Skip over the negation mov #NCCL,r2 ;New opcode 10$: call putpat ;Save in the pattern mov r1,r3 ;Address of count clr r2 ;Put out byte count call putpat ;Initially as zero bcs 60$ ;Must fit 20$: movb (r0)+,r2 ;Character from the pattern beq 40$ ;Oops cmpb r2,#'] ;End of pattern? beq 50$ ;Yes cmpb r2,#'\ ;Escape bne 30$ ;No movb (r0)+,r2 ;Get actual character beq 40$ ;Oops 30$: call putpat ;Store character in pattern incb (r3) ;Fixup character count bcc 20$ ;Br if <255 characters 40$: sec ;Error return br 60$ 50$: clc ;Good return 60$: mov (sp)+,r3 ;Restore r3 return ;Done ;+ ; ** putpat -- store a byte into the pattern (if it fits) ; ; Inputs: ; r1=pointer into pbuf ; r2=the byte to store ; ; Outputs: ; r1=updated ; c bit set if no fit; clear if all ok ;- putpat: cmp r1,#pbuf+PBSIZE ;Does it fit bhis 10$ ;No movb r2,(r1)+ ;Yes, store it clc ;Return with carry clear return 10$: sec ;No fit return ;+ ; ** maplow -- map character in r2 to lower case ;- maplow: cmpb r2,#'A ;Range check blo 10$ ;Too low cmpb r2,#'Z bhi 10$ ;Too high add #'a-'A,r2 ;Make lower 10$: return ;Done ;+ ; ** amatch -- look for a match anywhere in the line ; ; Inputs: ; pbuf=pattern ; rbuf=text line (asciz) ; ; Outputs: ; r0=junk ; r1=junk ; r2=junk ; c bit clear if a match; set otherwise ;- amatch: mov #pbuf,r1 ;Get pattern mov #rbuf,r2 ;Text pointer 10$: tstb (r2) ;End of the line? beq 20$ ;Br if yes mov r2,r0 ;Try for a call match ;Match bcc 30$ ;Yes inc r2 ;No, advance pointer and br 10$ ;Try again 20$: sec ;C bit set if no match 30$: return ;Done ;+ ; ** match -- pattern matcher ; ; Inputs: ; r0=pointer to asciz text string ; r1=pointer to pattern ; ; Outputs: ; r0=pointer past the matched text (success only) ; c bit clear if match; set otherwise ;- match: mov r1,-(sp) ;Save registers mov r2,-(sp) mov r3,-(sp) 10$: movb (r1)+,r2 ;Pattern opcode jmp @20$(r2) ;Go to executor 20$: .word 150$ ;END .word 30$ ;CHAR .word 40$ ;BOL .word 50$ ;EOL .word 60$ ;ANY .word 70$ ;CCL .word 80$ ;NCCL .word 90$ ;CLO 30$: movb (r0)+,r2 ;Char from the text call maplow ;Insure case insensitivity cmpb r2,(r1)+ ;Check for match beq 10$ ;Yes br 160$ ;No 40$: cmp r0,#rbuf ;At BOL beq 10$ ;Br if yes br 160$ ;No 50$: tstb (r0) ;At EOL beq 10$ ;Yes br 160$ ;No 60$: tstb (r0)+ ;Trying to match EOL bne 10$ ;No, ok br 160$ ;EOL fails 70$: call cmatch ;Match character class bcc 10$ ;If match, ok br 160$ ;Otherwise fail 80$: call cmatch ;Match character class bcs 10$ ;If no match, ok br 160$ ;Otherwise, fail 90$: mov r0,-(sp) ;Save where we are mov r0,-(sp) ;And how far I got 100$: call match ;Match as many times as you can bcs 110$ mov r0,(sp) ;Remembering how far you got br 100$ 110$: mov (sp)+,r0 ;Get pointer to character that failed 120$: cmpb (r1)+,#END ;No nested closures will work bne 120$ 130$: mov r0,-(sp) ;Save pointer call match ;Try to match the rest bcc 140$ ;Yes, entire pattern matches mov (sp)+,r0 ;Backup 1 byte dec r0 cmp r0,(sp) ;Until null match bhis 130$ tst (sp)+ ;Cleanup br 160$ ;Report failure 140$: cmp (sp)+,(sp)+ ;Cleanup 150$: clc ;Success br 170$ 160$: sec ;Failure 170$: mov (sp)+,r3 ;Restore registers mov (sp)+,r2 mov (sp)+,r1 return ;To caller ;+ ; ** cmatch -- match a character class ; ; Inputs: ; r0=pointer to text ; r1=pointer to count of class ; ; Outputs: ; r0=pointer to next character ; r1=pointer past the pattern ; c bit set if no match; clear if ok match ;- cmatch: movb (r0)+,r2 ;Get text byte call maplow clr r3 ;Get number of characters in class bisb (r1)+,r3 beq 30$ ;Null, success 10$: cmpb r2,(r1)+ ;Match class character beq 20$ ;Yes sob r3,10$ ;Try them all sec ;Failure br 40$ 20$: add r3,r1 ;Fixup R1 dec r1 30$: clc ;Success 40$: return ;To caller .end