; ---------------------------------------------------------- ; Reassembly of FIND.COM, which was originally extracted ; from the CBBS(R) package available from Ward Christensen ; and Randy Suess. However, considerable rearrangement has ; taken place, most notably the following: ; - results in a screen of information ; - the search pattern may be a regular expression ; - instance count reported, both per file and globally ; To achieve compatibility with MicroShell the vertical bar ; was replaced by exclamation point; all syntactic elements ; are defined by equ's and may be redefined. PATTERN!SKELETON ; is checked for balanced parentheses and non-null arguments ; to forestall the most common failure modes. Had MicroShell ; not been available, an option to direct the output to some ; disk file would probably have been included. STUTE.ASN was ; derived from STUTE.ASM by applying Sorcim's TRANS86, then ; cleaning up the resulting code by hand. The code can be ; assembled by Sorcim's ACT86, which uses a different style ; than the Intel assembler. STUTE differs from FIND in that ; the patterns found are replaced by a constant rather than ; just being locatse, and that a new disk file is created ; to hold the modified text. The extension of each original ; file is changed to .BAK. ; ; STUTE.ASN Copyright (C) 1983 ; Universidad Autonoma de Puebla ; ; [Harold V. McIntosh, 16 November 1983] ; ---------------------------------------------------------- HT equ 09H ;horizontal tab LF equ 0AH ;line feed CR equ 0DH ;carriage return KZ equ 1AH ;^Z ; Delimiters for the command line LSQ equ '[' ;begin alternative list RSQ equ ']' ;end alternative list LBR equ '{' ;begin iterated expression RBR equ '}' ;end iterated expression ORR equ '!' ;separate alternatives ; Representatives of characters or classes. TAB equ '_' ;substitute for tab QUE equ '?' ;represent any byte ALF equ '@' ;represent any alphanumeric ; CPM locations and parameters BDOS: equ 224 ;CP/M interrupt to BDOS TFCB equ 005CH ;CP/M's file control block CBLK equ 007CH ;CP/M's block counter TSIZ equ 0080H ;CP/M's record size TBUF equ 0080H ;CP/M's record buffer KSIZ equ 26 ;sector capacity of IN buffer ISIZ equ KSIZ*128 OSIZ equ 128 ; ------------- org 0100H ; ------------- BEGN: cmpb TFCB+1,#' ' ;file name jnz X0143 ld bx,#TUTO ;tutorial message jmp GBYE X0143: ld bx,#LOGO ;identification line call MSSG ld ah,#12 ld dx,#TFCB ;CP/M's FCB ld bx,#FILE ;generic filename call MIUC ld al,FILE ;generic filename sto al,KFIL ;backup file (.BAK) sto al,IFIL ;input file (.EXT) sto al,OFIL ;output file (.$$$) sto al,UFIL ;active output (.$$$) stob #0,ENTH ld bx,#TBUF ;CP/M's record buffer ld dl,[bx] mov cl,dl ld dh,#0 xchg bx,dx inc dx add bx,dx stob #0,[bx] ;mark end of console line xchg bx,dx ;take up beginning of line X0152: inc bx dec cl cmpb [bx],#0 jnz GPAT jmp BADP ;message: 'Bad Pattern' GPAT: cmpb [bx],#' ' jnz X0152 ;pass over file name inc bx call BALA ;check balance of [], {}. call NULA ;check for null alternatives ld dx,#PATT ;command line pattern call MUVE xchg bx,dx dec bx sto bx,SKND ;skeleton end ld ah,#4 ld dx,#LZER ;counter initializer ld bx,#DTOT ;'Disk Total' call MIUC ld bx,#PATT ;command line pattern sto bx,PAPO ;pattern pointer call NEXT sto bx,SKPO ;skeleton pointer ; Scan the directory for file names. A list is made of files ; which will be scanned, both because of a quirk in CP/M's ; "search again" and to avoid a future encounter with a file ; which has already been processed and given the old name. ; Likewise, .BAK and .$$$ files are excluded from analysis. PREP: sto #1,DCTR ;directory counter sto #DIRY,DPTR ;#directory extract,directory pointer ld cl,#26 ;(1A) set DMA address ld dx,#TBUF ;CP/M's record buffer int BDOS ; - B D O S - ld cl,#17 ;(11) search once ld dx,#FILE ;generic filename int BDOS ; - B D O S - ; Find the Nth file which fits the file description. FNTH: cmp al,#0FFH ;FF=search failed jz SCAN ;all relevant files located and al,#03 ;isolate quad ld ah,#32 mulb ah ld bx,#0081H add bx,ax cmpb [bx+8],#'B' jnz NBAK cmpb [bx+9],#'A' jnz NBAK cmpb [bx+10],#'K' jz OMIT NBAK: cmpb [bx+8],#'$' jnz NDOL cmpb [bx+9],#'$' jnz NDOL cmpb [bx+10],#'$' jz OMIT NDOL: ld ah,#12 mov dx,bx ld bx,DPTR ;directory pointer call MIUC sto bx,DPTR ;directory pointer incb DCTR ;directory counter OMIT: ld cl,#18 ;(12) search again ld dx,#FILE ;generic filename int BDOS ; - B D O S - jmp FNTH ; We're all done. DONE: ld bx,#DTOT ;'Disk Total' GBYE: call MSSG EXIT: ld dl,#00 ld cl,#00 int BDOS ; Now we are ready to scan the prospective files. SCAN: sto #DIRY,DPTR SCAM: decb DCTR jz DONE push DPTR add DPTR,#12 ld ah,#8 pop dx push dx ld bx,#KFIL+1 ;backup file (.BAK) call MIUC ld ah,#11 pop dx push dx ld bx,#IFIL+1 ;input file (.EXT) call MIUC ld ah,#8 pop dx push dx ld bx,#OFIL+1 ;output file (.$$$) call MIUC ld ah,#8 pop dx push dx ld bx,#UFIL+1 ;active output (.$$$) call MIUC ld ah,#12 pop dx ld bx,#TFCB+1 ;CP/M's file control block call MIUC ld cl,#19 ;(13) delete file ld dx,#KFIL ;backup file (.BAK) int BDOS ; - B D O S - ld cl,#19 ;(13) delete file ld dx,#OFIL ;output file (.$$$) int BDOS ; - B D O S - ld cl,#22 ;(16) create file ld dx,#UFIL ;active output (.$$$) int BDOS ; - B D O S - stob #0,UREC ld cl,#15 ;(0F) open file ld dx,#TFCB ;CP/M's FCB int BDOS ; - B D O S - inc al jnz STAY jmp EXIT ;quit [without message] STAY: stob #0,CBLK ;block pointer ld ah,#4 ld dx,#LZER ;counter initializer ld bx,#LNUM ;'line number' call MIUC ld ah,#4 ld dx,#LZER ;counter initializer ld bx,#FTOT ;'file total' call MIUC ld ah,#8 ld dx,#TFCB+1 ;file name ld bx,#FNAM ;'file name' call MIUC ld ah,#3 ld dx,#0065H ;extension ld bx,#FEXT ;'file extension' call MIUC ld bx,#FHED ;"File" header call MSSG ;message to console X01C2: sto #0000,ICTR ;input file counter sto #TSIZ,OCTR sto #UBUF,OPTR LOOP: ld bx,#LNUM+3 ;increment l.c. call INCO ;increment line counter ld bx,#LBUF ;line buffer ld ch,#0FFH MORL: inc ch js CRLF ;force a line after 80H columns push cx push bx call INCH ;char from big bffr to line bffr pop bx pop cx sto al,[bx] inc bx cmp al,#KZ jnz X01E8 ld bx,#FTOT call MSSG ; Close out last, incomplete record. LARE: cmp OCTR,#0000 jz CLOF ld al,#KZ call OUCH jmp LARE CLOF: ld cl,#21 ;(15) write one record ld dx,#UFIL ;active output (.$$$) int BDOS ; - B D O S - ld cl,#16 ;(10) close file ld dx,#UFIL ;active output (.$$$) int BDOS ; - B D O S - ld cl,#23 ;(17) rename file ld dx,#IFIL ;input file (.EXT) int BDOS ; - B D O S - ld cl,#23 ;(17) rename file ld dx,#OFIL ;output file (.$$$) int BDOS ; - B D O S - jmp SCAM X01E8: cmp al,#LF jnz MORL ;line not finished jmp IRRQ CRLF: stob #CR,[bx] inc bx stob #LF,[bx] inc bx ; Check console for termination request. If one ; is present, clear it out before leaving. IRRQ: stob #00,[bx] ;guarantee right hand fence ld cl,#11 ;(0B) console status int BDOS ; - B D O S - or al,al jz CULI ld cl,#1 ;(01) read console int BDOS ; - B D O S - ld bx,#M4 ;"search terminated" jmp GBYE ; Scan the current line. CULI: ld bx,#LBUF ;line buffer sto bx,ALFA ;begin good segment sto bx,BETA ;end good segment NEXB: ld dx,PAPO ;pattern pointer ld bx,BETA ;end good segment cmpb [bx],#CR jz NULI call CHEK jnz FAIL push dx push bx ld bx,#LNUM ;'line number' call MSSG ld bx,#LBUF call MSSG ld bx,#FTOT+3 call INCO ld bx,#DTOT+3 ;'Disk Total' call INCO ld dx,BETA ;end good segment ld bx,ALFA ;begin good segment call OULI ld dx,SKND ;skeleton end ld bx,SKPO ;skeleton pointer call OULI pop bx sto bx,ALFA ;begin good segment dec bx sto bx,BETA ;end good segment pop dx FAIL: inc BETA ;end good segment jmp NEXB ;move scan up one byte NULI: ld dx,BETA ;end good segment inc dx inc dx ld bx,ALFA ;begin good segment call OULI jmp LOOP ;go for another line ; Increment ASCII counter at (HL-3). INCC: stob #'0',[bx] dec bx orb [bx],#30H INCO: incb [bx] cmpb [bx],#':' jz INCC ret ; Memory to console MSSG: ld dl,[bx] inc bx push bx ld cl,#2 ;(02) write console int BDOS ; - B D O S - pop bx cmpb [bx],#0 jnz MSSG ret ; Read a character from the input buffer; if and when ; the buffer is empty, more sectors are brought in from ; the disk until an end-of-file is reached. INCH: cmp ICTR,#0000 ;input file counter jnz INCI call INDI ;disk to IN area INCI: dec ICTR ;input file counter ld bx,IPTR ;input file pointer ld al,[bx] cmp al,#KZ jz INCR inc IPTR ;input file pointer INCR: ret ; Bring in reinforcements from the disk. INDI: ld ch,#KSIZ sto #ISIZ,ICTR ;input file counter ld bx,#IBUF ;input file buffer sto bx,IPTR ;input file pointer INDD: stob #KZ,[bx] push bx push cx xchg bx,dx ld cl,#26 ;(1A) set DMA address int BDOS ; - B D O S - ld dx,#TFCB ;CP/M's file control block ld cl,#20 ;(14) read one record int BDOS ; - B D O S - pop cx pop bx or al,al jnz INDR dec ch jz INDR add bx,#TSIZ ;CP/M's record size jmp INDD INDR: ret ; Send a "line" [(HL) to (DE)] to out file OULJ: ld al,[bx] call OUCH inc bx OULI: cmp bx,dx jnz OULJ ret ; Send one character to the output file, conserve BC, HL OUCH: push dx push bx push ax cmp OCTR,#0000 jnz OUCI call OUDI ;OUT area to disk OUCI: dec OCTR pop ax ld bx,OPTR sto al,[bx] inc OPTR pop bx pop dx ret ; Flush the buffer when it is full, make ready for next. OUDI: sto #OSIZ,OCTR sto #UBUF,OPTR ld cl,#26 ;(1A) set DMA address ld dx,#UBUF int BDOS ; - B D O S - ld cl,#21 ;(15) write one record ld dx,#UFIL ;active output (.$$$) int BDOS ; - B D O S - cmp al,#0 jz OUDR ld bx,#M5 jmp GBYE OUDR: ret ; Advance to next alternative NEXX: ld bx,[bx] NEXT: ld al,[bx] or al,al jz NEXR inc bx call ENDA jz NEXR call BEGB jz NEXX jmp NEXT NEXR: ret ; Move by Increment Until Count. MIUC: xchg bx,dx ld al,[bx] xchg bx,dx sto al,[bx] inc dx inc bx dec ah jnz MIUC ret ; Move and semi-compile the command line. MUVE: ld al,[bx] cmp al,#TAB jnz MUNT ld al,#HT MUNT: xchg bx,dx sto al,[bx] xchg bx,dx inc bx inc dx cmp al,#RBR jz MURB cmp al,#RSQ jz MURB cmp al,#LBR jz MULB cmp al,#LSQ jz MULB MUST: dec cl jnz MUVE ret MURB: mov ax,bx pop bx sto dx,[bx] mov bx,ax jmp MUST MULB: push dx inc dx inc dx jmp MUST ; Check balance of []'s and {}'s. BALA: push bx push cx ld cx,#0101H BALB: ld al,[bx] inc bx cmp al,#LSQ jnz BALC inc ch jmp BALB BALC: cmp al,#RSQ jnz BALD dec ch jz BADP jmp BALB BALD: cmp al,#LBR jnz BALE inc cl jmp BALB BALE: cmp al,#RBR jnz BALF dec cl jz BADP jmp BALB BALF: or al,al jnz BALB cmp cl,#01 jnz BADP cmp ch,#01 pop cx pop bx jnz BADP ret BADP: ld bx,#M3 ;"bad pattern" jmp GBYE ; Check for termination of alternative. ENDA: cmp al,#ORR jz ENDC cmp al,#RSQ jz ENDC cmp al,#RBR jz ENDC or al,al ENDC: ret ; Check for beginning of alternative. BEGA: cmp al,#ORR jz BEGC BEGB: cmp al,#LSQ jz BEGC cmp al,#LBR BEGC: ret ; Check for null alternative. NULA: push bx call NULC pop bx NULB: ret NULC: ld al,[bx] inc bx or al,al jz NULB call BEGA jnz NULC ld al,[bx] call ENDA jnz NULC jmp BADP ; Check for given expression. CHEK: ld ah,[bx] xchg bx,dx ld al,[bx] xchg bx,dx inc dx call ENDA jz CHNR cmp ah,#CR jz CHNO cmp al,#LBR jnz CHEL jmp CHLB CHEL: cmp al,#LSQ jz CHSQ ld cl,[bx] inc bx cmp al,#QUE jz CHEK cmp al,#ALF jz CHAL cmp al,ah jz CHEK cmp ah,#'a' jc CHNO cmp ah,#'{' jnc CHNO and ah,#05FH cmp al,ah jz CHEK CHNO: or al,#0FFH CHNR: ret ; Check alphanumeric. CHAL: mov al,cl cmp al,#'0' jc CHNO cmp al,#':' jc CHEK cmp al,#'A' jc CHNO cmp al,#'[' jc CHEK cmp al,#'a' jc CHNO cmp al,#'{' jc CHEK jmp CHNO ; Check list of alternatives. CHSQ: push SQXX push SQAA push SQZZ sto bx,SQXX xchg bx,dx ld dx,[bx] inc bx inc bx sto bx,SQAA sto dx,SQZZ CHAA: ld bx,SQXX call CHEK jz CHFF CHBB: ld bx,SQAA ;fail so find next alternative CHCC: call NEXT cmp al,#RSQ jz CHDD ;no more alternatives, so fail cmp al,#ORR jnz CHCC sto bx,SQAA xchg bx,dx jmp CHAA ;try next alternative CHDD: ld bx,SQXX or al,#0FFH CHEE: pop SQZZ pop SQAA pop SQXX ret CHFF: ld dx,SQZZ ;good alternative, try rest call CHEK jz CHEE jmp CHBB ; Check iterative pattern. CHLB: push TEXT push TEXX push REST push REPT push REPP sto bx,TEXT sto bx,TEXX xchg bx,dx ld dx,[bx] inc bx inc bx sto bx,REPT sto bx,REPP sto dx,REST CHLC: ld dx,REST ld bx,TEXT call CHEK ;check rest jz CHZZ CHII: ld dx,REPT ;rest failed ld bx,TEXT ;keep same text call CHEK ;try out the repeater jnz CHOO sto bx,TEXT ;repeater worked, record progress ld bx,REPP ;start alternatives over again sto bx,REPT jmp CHLC CHOO: ld bx,REPT ;repeater failed, try next CHXX: call NEXT cmp al,#RBR jz CHYY ;this was the last, quit cmp al,#ORR jnz CHXX sto bx,REPT jmp CHII CHYY: ld bx,TEXX or al,#00 ;emphasize the RBR CHZZ: pop REPP pop REPT pop REST pop TEXX pop TEXT ret TUTO: db 'The command line',CR,LF db ' STUTE D:FILE.EXT PATTERN!SKELETON',CR,LF db 'will search through all instances of FILE.EXT',CR,LF db '(which may be an ambiguous reference) on disk D',CR,LF db 'to find lines containing PATTERN. Such lines will',CR,LF db 'be shown on the console preceded by a line number,',CR,LF db 'classified by file. Whenever the regular expression',CR,LF db 'PATTERN is found, it will be replaced by the constant',CR,LF db 'SKELETON. The PATTERN may contain:',CR,LF db ' [p1!p2!...!pn] alternative strings',CR,LF db ' {p1!p2!...!pn} repeated alternatives',CR,LF db ' ? any single character',CR,LF db ' @ for any alphanumeric: a-z, A-Z, 0-9',CR,LF db ' _ in place of horizontal tab',CR,LF db 'A general PATTERN must be used with extreme caution',CR,LF db 'because the same constant will replace it, whatever',CR,LF db 'its form. The PATTERN may not involve more than one',CR,LF db 'single line, but more than one instance of the PATTERN',CR,LF db 'may occupy the same line.',CR,LF db 00 LOGO: db 'STUTE.CMD 16/NOV/83 ICUAP',CR,LF,00 M3: db '-- Bad Pattern --',00 M4: db CR,LF,'-- Substitution Terminated --',00 M5: db CR,LF,'-- Disk or Directory Full --',00 ; The following file control segments are arranged in just ; the form shown so that they will be properly paired for ; the renaming which has to be done after closing each file. ENTH: ds 1 FILE: db 'DFilenameEXT',00,00,00,00 ;generic filename ds 16 OFIL: db 'DFilename$$$',00,00,00,00 ;output file (.$$$) IFIL: db 'DFilenameEXT',00,00,00,00 ;input file (.EXT) KFIL: db 'DFilenameBAK',00,00,00,00 ;backup file (.BAK) UFIL: db 'Dfilename$$$',00,00,00,00 ;active output (.$$$) ds 16 UREC: ds 1 UBUF: ds 80H OPTR: ds 2 OCTR: ds 2 ; All the relevant files are located and noted before any ; substitutions are made, to avoid a newly created file ; being placed in the dictionary, being encountered again ; later on and then being processed a second time. The worst ; case would be to have to use all 64 files (minus one, for ; elbow room) in the directory, but that's not likely. DCTR: ds 1 ;directory counter DPTR: ds 2 ;directory pointer DIRY: ds 1024 ;directory of file names, if needed ; Temporary storage for the regular expression parser. SQXX: ds 2 SQAA: ds 2 SQZZ: ds 2 TEXT: ds 2 TEXX: ds 2 REST: ds 2 REPT: ds 2 REPP: ds 2 ; Scan pointers, buffers, headings and labels. ALFA: ds 2 ;begin good segment BETA: ds 2 ;end good segment PATT: ds 256 ;command line pattern PAPO: ds 2 ;pattern pointer SKPO: ds 2 ;skeleton pointer SKND: ds 2 ;skeleton end FHED: db '----> File ' ;"File" header FNAM: db 'xxxxxxxx.' ;filename FEXT: db 'xxx',CR,LF,00 ;file extension LNUM: db ' 0 ',00 ;'line number' LZER: db ' 0' ;counter initializer FTOT: db ' 0 substitutions made',CR,LF,00 DTOT: db ' 0 instances in the entire disk',CR,LF,00 db 00 ;fence for line buffer LBUF: ds 85H ;line buffer ICTR: ds 2 ;input file counter IPTR: ds 2 ;input file pointer IBUF: ds ISIZ ;input file buffer FINI: ds 0 end