;======================================================================= ; ; ; Z F I N D ; ; A ZCPR3 String Find Utility ; ; by ; Terry Hazen ; 21460 Bear Creek Road ; Los Gatos CA 95030 ; ; Voice.......... (408) 354-7188 ; Zee-Machine.... (408) 245-1420 ; Ladera Znode... (213) 670-9465 ; ;======================================================================= ; ; Revision history ; ---------------- ; ; 08/10/91 All options are now properly initialized for use with GO. ; v1.4 The old 'P' option letter wasn't changed to 'D' in ; parsopt. That's been corrected. Added 'O' option to ; control overwriting an existing output file. The default ; is now to ask first. Can be configured to automatically ; overwrite the file. The keyboard is now checked for a ; user abort before each match is displayed. ; - Terry Hazen ; ; 08/01/91 Output file is now opened only after an initial string ; v1.3 find, fixing a problem reported by Richard Holway that ; caused the output file to consume all the available disk ; space when no string was found. Made 'U' option ; configurable. ; - Terry Hazen ; ; 07/18/91 Fixed problems in bufrd and linbak that, working ; v1.2 together, could cause the system to overwritten when ; the character following the end of the file buffer was ; a null. Added 'C' (context) option that displays the ; line in which the match was found along with the ; previous and next lines in a delimited context block. ; Changed the 'P' option to 'D' and the 'D' option to ; either 'B' (block) or 'L' (line), depending on default ; display setting, for better consistency with some other ; utilities. ; - Terry Hazen ; ; 07/02/91 Added 'U' option to search all user areas on a drive. ; v1.1 Added searched filename display 'A' option, toggling ; existing default flag. Expanded command line syntax ; flexibility and improved command line parsing. Fixed ; string parsing to properly allow a one-character search ; string. Changed end-of-buffer character from 00h to FFh ; to avoid problems in text files containing nulls (often ; found in MAST.CAT files, it seems). Fixed pointer ; problem at end of displayed find line, where it started ; the next search after instead of at the line feed. ; Several other small tuneups. ; - Terry Hazen ; ; 06/14/91 Initial release. ; v1.0 - Terry Hazen ; ;----------------------------------------------------------------------- ; ; ZFIND is a ZCPR3 utility designed to very quickly find one or more ; ASCII strings in a group of wildcard-related ASCII text files. ZFIND ; was designed to work in a similar way to Irv Hoff's FIND.COM, but is ; MUCH faster and can do more. String searchs are performed in a 16k ; text buffer, using my own version of the Boyer-Moore string search ; algorithm. When a matching string is located in the buffer, the line ; in which the string was found is displayed on the screen in either ; the line or the delimited block in which it was found. The output can ; be written or appended to a file. ; ; Searches are normally performed ignoring case. If you desire an ; exact match, don't specify a string on the command line and one will ; be requested, allowing you to enter the string in the exact case ; desired. ; ; Several special characters may be used during string entry to help you ; enter special characters into the search string. A question mark ; ("?") in the string will match any character in that position. A "_" ; character in the string will be changed to a TAB. A "\" (backslash) ; character will be changed to a line feed, allowing you to search for ; strings starting at the beginning of a line. ; ; Searches are not performed line-by line. The entire buffer is first ; searched for string1 and the finds displayed, then the buffer is ; searched for string2 and those finds displayed, etc. This means that ; if a line contains both string1 and string2, it is displayed once for ; each match. Because the search process ignores lines except to ; display the found string, line number output is not provided. ; ; When the buffer has been searched for all strings, the next buffer- ; full is read in and the process repeated. ; ; The same process is repeated for each file that matches the specified ; ambiguous filename. ; ;======================================================================= ; ; Equates ; vers equ 14 ; Version int equ 06 ; Internal revision month equ 08 ; Revision date day equ 06 year equ 91 ; on equ 0ffh off equ 0 ; bell equ 7 bs equ 8 ; Backspace tab equ 9 ; Tab character lf equ 10 ; Line feed cr equ 13 ; Carriage return eof equ 1ah ; End of file marker ; strlen equ 45 ; Maximum string length buflen equ 4000h ; Length of file read buffer blklen equ 1000h ; Maximum delimited block length ; ; System equates ; fcb equ 5ch fcb2 equ 6ch cmdbuf equ 80h ; ; BDOS equates ; bdos equ 5 srchf equ 17 ; Search first srchn equ 18 ; Search next getdrv equ 25 ; Get logged drive getdpb equ 31 ; Get DPB gsusr equ 32 ; Get/set user ; ; Request library routines ; .request z3lib ext z3init,zprsfn,z3log .request syslib ext codend,setdma,bline ext f$exist,f$open,f$mopen,f$appl ext f$close,f$read,f$write ext condin,cin,cout,eprint ;======================================================================= ; jp start db 'Z3ENV' db 1 ; Type 1 utility z3eadr: dw 0 ; Z3ENV address ;----------------------------------------------------------------------- ; ; Configuration area ; ; ; Default display of found string as line or delimited block ; dspdef: db off ; Set to OFF to display line ; Set to ON to display delimited block ; ; Block delimiter character. Always follows LF. ; delim: db '-' ; Matches CR,LF,'-' delimiter in XFOR ; Note: (LF),CR would match double spaced ; line ; ; Default ZCNFG CFG filename ; db 'ZFIND',vers/10+'0',vers mod 10+'0',' ' db 0 ; Termination ; ; Display all filenames being searched ; allf: db off ; Set to OFF to only display filenames ; in which matches occur ; Set to ON to display all filenames searched ; ; Page display default ; pgdef: db on ; Set to ON to page display ; ; Display found string in context (line display mode only) ; ctdef: db off ; Set to ON to display found string +\- ; one line (line mode only) ; ; Wildcard user ; wcudef: db off ; Set to ON to search files on all user ; areas ; ; Overwrite existing output file ; owdef: db off ; Overwrite existing file without asking ;======================================================================= ; ; Help screen ; help: call eprint db 'Syntax:',cr,lf,' ',0 ld hl,(fnam) ld b,8 call dispfn call eprint db ' [dir:]afn [string(s)] [>outfile] [/options]',cr,lf db ' ' db 'If no string is included, one will be requested,',cr,lf,0 call leader1 db 'permitting a search for lower-case characters.',cr,lf db ' ' db 'Special string characters:',cr,lf,0 call leader db '"|" separates multiple search strings',cr,lf,0 call leader db '"?" matches any character',cr,lf,0 call leader db '"_" matches a tab character',cr,lf,0 call leader db '"\" matches the beginning of a line' db cr,lf db 'Options:',cr,lf db ' U - Search files on ',0 ; ld a,(wcudef) ; Check wildcard user flag or a jr z,aua ; call eprint db 'single user area',0 jr daf0 ; aua: call eprint db 'all user areas',0 ; daf0: call eprint db cr,lf db ' A - Display ',0 ; ld a,(allf) or a jr z,daf1 ; call eprint ; Default is all files db 'only files with matches',0 jr df0 ; daf1: call eprint ; Default is matches db 'all files searched',0 ; df0: call eprint db cr,lf db ' C - ',0 ; ld a,(ctdef) ; Check for context or a call nz,dont call dfs ; Display found string line db 'in context (3 lines)' db cr,lf,' ',0 ; ld a,(dspdef) ; Check default display or a jr z,hblk ; call eprint db 'L - ',0 call dfs ; Display found string line db 'line' db cr,lf,0 jr dfo ; hblk: call eprint db 'B - ',0 call dfs db 'in a delimited (LF,',0 ; ld a,(delim) cp cr jr nz,de0 ; Not CR, must be displayable ; call eprint db 'CR',0 jr del1 ; de0: push af ; Display printable delimiter character ld a,'"' call cout pop af call cout ld a,'"' call cout ; del1: call eprint db ') block',cr,lf,0 ; dfo: call eprint db ' D - ',0 ; ld a,(pgdef) ; Get paging flag or a call nz,dont ; Default is no call eprint db 'Page screen display',cr,lf db ' O - ',0 ; ld a,(owdef) ; Get overwrite flag or a call nz,dont ; Default is no call eprint db 'Overwrite existing output file',cr,lf db ' >[dir:]outfile - Output to file',cr,lf db ' >>[dir:]outfile - Append to file',cr,lf,0 ret ; dfs: call eprint db 'Display found string '0 jp eprint ; dont: push af ; Save flag call eprint ; Default is yes db 'Don''t ',0 pop af ; Restore flag ret ;======================================================================= ; start: ld ix,(z3eadr) ld a,(ix+3) cp 'Z' ; Quick Z-system check jr z,start0 ; Ok ; call eprint db bell,'Requires ZCPR3!',0 ret ; start0: ld hl,data ; Initialize the data area ld bc,datalen call fillz ; ld hl,(z3eadr) call z3init ; ld l,(ix+24h) ; HL=efcb address ld h,(ix+25h) inc hl ; Skip to filename ld a,(hl) or a ; Anything there? jr z,getdl ; ld (fnam),hl ; Yes, save it ; getdl: ld a,(ix+33h) ; Get number of display lines dec a ; Allow for end of display dec a ld (lines),a ; Save max display lines ; bit 7,(ix+8) ; Check for extended environment jr z,getccp ; No, infer CCP address ; ld l,(ix+3fh) ; Get CCP from environment ld h,(ix+40h) jr gettop ; getccp: ld hl,(1) ; HL=warmboot address ld de,-1603h ; Offset to CCP entry point add hl,de ; HL=CCPaddr ; gettop: ld de,(6) ; Get BDOS or protected RSX address call comphd jr c,savmem ; If no carry, HL (CCPSaddr) is smaller ; ex de,hl ; Else, use DE (BDOSaddr), which is less ; savmem: dec h ; Reserve a little elbow room... ld l,0 ld (mem),hl ; Save top of available memory ; banner: call eprint name: db 'ZFIND String Find Utility vers ', db vers/10+'0','.',vers mod 10+'0' db cr,lf,0 ; ld hl,fcb+1 ; Parse for help request ld a,(hl) cp ' ' ; Empty? jp z,help ; Yes, display help ; cp '/' ; Help request? jr nz,st1 ; No ; inc hl ; Possible help request ld a,(hl) ; Check next character cp '/' ; Help jp z,help st1: ld a,(fcb2+13) ; Get current user ld (outfcb+13),a ; Save it ; ld (stack),sp ; Save stack ld sp,stack ; Use our own stack ; ld iy,fskip ; Set jump destination ; call codend ; Get end of code ld (fnbuf),hl ; Save as start of filename buffer ld (rdbuf),hl ; And and as start of read buffer ; ld hl,dspdef ; Move default option settings ld de,dspflg ; to data area ld bc,16 ldir ; call ddrive ; Get default drive ld (cdrive),a ; Save it ; call parsopt ; Parse command line for options ld a,(dspflg) ; Check display mode or a jr z,ptcmdb ; Line mode ; xor a ld (ctxtflg),a ; Block mode, so turn off context flag ; ptcmdb: ld hl,cmdbuf+2 ; Point to start of commands ; sscan: ld a,(hl) ; Get character inc hl or a ; Termination? jr z,getstr ; No string entered, get it from user ; cp ' ' ; Skip past filename jr nz,sscan ; Not yet ; ld de,string ; Move string to buffer ld c,strlen ld b,0 ; mvlp: ldi ld a,(hl) or a jr nz,mvlp ; setcnt: ld l,strlen ld h,0 sbc hl,bc ld a,l ; Set count ld (ccnt),a jr ckcase ; ; Accept string from console. ; getstr: ld b,strlen+eslen ; Space out to end of string marker call dispsp call eprint db '<',cr ; Display end marker ess: db 'Enter Exact Case String -->'; And string message eslen equ $-ess db 0 ; ld a,strlen ld (size),a ; Save buffer size xor a ; Don't capitalize input ld hl,size ; Point to buffer call bline ; ld a,(ccnt) ; Check for any input or a jp z,exit ; None, so quit ; ld (case),a ; Set for exact match call newline ; Bump display line ; ; Test for desired case matching, patch code accordingly ; ckcase: ld a,(case) ; Get case flag or a ld a,28h ; JR_Z jr z,ckcs0 ; Ignore case ; ld a,20h ; JR_NZ for exact match ; ckcs0: ld (case1),a ; Patch code to set case ld (case2),a ; ; Replace special characters in search string ; ld hl,string ; Point to string ; strlp: or a jr z,savfn ; Quit at termination ; inc b ; Bump counter ld a,(hl) cp '\' ; If backslash, replace with lf jr nz,tabck ; ld (hl),lf jr nsch ; tabck: cp '_' ; If underline, replace with tab jr nz,nsch ; ld (hl),tab ; nsch: inc hl jr strlp ; savfn: ld hl,fcb ld de,reqfn ; Save original requested dir:afn ld bc,16 ldir ; call mfname ; Look for files, fill buffer jp c,nfexit ; No file ; ld hl,(rdbuf) ; Get buffer pointer ld de,buflen+blklen; Offset to orphan text buffer add hl,de ld (orptxt),hl ; Save orphan text buffer pointer ; ld de,blklen ; Length of orphan text buffer add hl,de inc h ; Page boundary ld l,0 ld (bufadr),hl ; Save pointer to output file buffer ld (bytenxt),hl ; Initialize byte pointer ; ld hl,(fnbuf) ; Get address of filename buffer ; ; Open each file, read it into buffer, do search ; readf: ld de,fcb+1 ; Point to filename ld bc,13 ldir ; Move it to fcb ; ld a,(fcb+13) ; Set user for input file call setusr ld a,(reqfn) ; Get requested drive ld (fcb),a ; Replace it ; ld hl,fcb+12 ; Point to extent ld bc,24 ; Zero out rest of fcb call fillz ld (orplen),a ; Reset orphan text length ; ld de,fcb ; Open file call f$open ld (dfnflg),a ; Reset match flag jp nz,oexit ; Can''t open file ; ld a,(allflg) ; Display all filenames? or a call nz,showfil ; Yes, display filename being searched ; nxtbuf: ld hl,string ; Reset string pointer to start ld (strend),hl call setstr ; Set end of string and length jp z,exit ; Quit if no more OR's ; call bufrd ; Read up to 16k to buffer call chkabt ; Check for user abort ; ; Set up to start search ; initrd: ld hl,(rdbuf) ; Point to buffer ; fndstr: call search ; Search buffer for string jr c,nxtor ; No match in buffer, try next OR ; push hl ; Save pointer call ofile ; Check for file output pop hl ; Restore pointer ; ld a,(allflg) ; Display all filenames? or a jr nz,disp0 ; Yes ; ld a,(dfnflg) ; Check filename display flag or a jr nz,disp0 ; Already displayed filename ; dec a ; We've found at least one match ld (dfnflg),a ; ; Display filename only at first match ; push hl ; Save buffer pointer call showfil ; Display file pop hl ; Restore buffer pointer ; disp0: ld a,(dspflg) ; Get display flag or a jr z,displn ; Display found line ; ; Display delimited block containing match ; call delbak ; Back up to last delimiter call dispblk ; Display delimited block call chkabt ; Check for user abort ld a,(pageflg) ; Do we want paging between blocks? or a call nz,pause ; Yes, pause at the end of each block jr fndstr ; And continue search ; ; Display line containing match ; displn: ld a,(ctxtflg) ; Check context flag or a jr z,displ0 ; call linbak ; Back up one more line dec hl ; Skip back over LF dec hl ; displ0: call linbak ; Back up to start of line ; ld a,(ctxtflg) ; Check context flag or a jr z,displ1 ; push hl call linchk ; Increment line count, check for pause call dspdel ; Display context delimiter call linchk ; Increment line count, check for pause pop hl call dlnlp ; Display previous line jr c,nxtor jr nz,fndstr ; inc hl call linchk ; Increment line count, check for pause call dlnlp ; Display matched line jr c,nxtor jr nz,fndstr inc hl ; displ1: call linchk ; Increment line count, check for pause call dlnlp ; Display line jr nc,fndstr ; ; End of file, do next OR ; nxtor: call setstr ; Initialize search string to next OR jr nz,initrd ; And try that one ; ld a,(eofflg) ; No more OR's inc a jp z,nxtbuf ; End of buffer, read in next buffer ; ; End of file, so bump pointers for next file ; ld hl,(fncnt) ; Decrement filename count dec hl ld (fncnt),hl ld a,h or l jp z,exit ; Done ; incfn: ld hl,(fnbuf) ; Get current filename pointer ld de,16 ; Increment to next filename add hl,de ld (fnbuf),hl ; Save next filename pointer jp readf ; Read in new file ; ; Display found string line ; dlnlp: ld a,(hl) ; Get character cp on ; Check for end of buffer jr nz,dlnlp0 ; No ; call crlf ; Quit at eob, provide new line and on ; Set NZ,NC for EOB return ret ; dlnlp0: and 07fh ; Filter character call fout ; Display it cp lf ret z ; Quit at first LF inc hl ; Point to next character cp eof jr nz,dlnlp ; Loop till end of line or EOF and on ; Set NZ,CARRY for EOF return scf ret ; End of file ; ; Error exits ; weexit: call eprint ; File write error db bell,'File write error!',0 jr exit0 ; wcexit: call eprint ; File output error db bell,'No wildcards in filename!',0 jr exit ; oexit: call eprint ; File open error db bell,'Can''t open ',0 ld de,fcb jr dspfil0 ; nfexit: call eprint ; No file error db bell,'No file ',0 ; ; Display filename on exit ; dspfil: ld de,reqfn ; dspfil0:xor a ld (output),a ; Turn off file output call zprdfn call crlf ; ; Normal exit point ; exit: ld a,(output) ; Check for file output or a call nz,fx$clo ; Close output file if so ; exit0: ld sp,(stack) ret ;======================================================================= ; ; SUBROUTINES ; ;======================================================================= ; ; Search for files matching fcb spec and add them to filename buffer ; ; Exit: CARRY SET if no matching filename ; ZERO SET if match ; (fncnt) has number of matched filenames ; mfname: ld hl,cmdbuf ; Set dma to command buffer call setdma ; ld de,fcb ; Log into specified drive call z3log ; ld c,getdpb ; Get dpb address call bdos ld de,4 add hl,de ld a,(hl) ; Get exm cpl ; Complement it ld (exm+1),a ; Save it ; ld hl,fcb ; Point to fcb ld (hl),'?' ; Set wildcard user ld de,12 add hl,de ; Point to extent ld (hl),'?' ; Any extent inc hl ; Skip user inc hl ld bc,22 call fillz ; Zero out end of fcb ; ld de,fcb ld c,srchf ; Search for first file match call bdos ; ld hl,cmdbuf ; Initialize dirbuf pointer ; ; Try to match each directory entry against target filename ; dirlp: ld (diradr),hl ; Save dirbuf pointer ld a,(hl) ; Get user number cp 0e5h ; Erased entry? jr z,mfnlp ; Yes, skip to next entry ; ld de,12 add hl,de ; Point to extent exm: ld a,0 ; Check for first extent and (hl) jr nz,mfnlp ; No, skip to next entry ; ld hl,(diradr) ; Point to dirbuf entry ld a,(wcuflg) ; Wildcard user? or a jr nz,chkfn ; Yes, match any user ; ld a,(reqfn+13) ; Get requested user number and 07fh ; Filter hi bit cp (hl) ; Match dirbuf user? jr nz,mfnlp ; No, skip to next entry ; chkfn: inc hl ; Point to filenames ld de,fcb+1 call fncmp ; Compare filenames jr nz,mfnlp ; No, skip to next entry ; ; Move matched filename to filename buffer ; movfn: ld hl,(diradr) ; Point to dirbuf entry ld a,(hl) ; Save user number inc hl ; Point to filename ld de,(rdbuf) ; Get filename buffer pointer ld bc,12 ; Move filename thru extent ldir ld (de),a ; Save user number ex de,hl ; Buffer pointer in HL inc hl ld a,lf ; Pad rest of line with LF's ld bc,3 call fillbc ld (rdbuf),hl ; Save new buffer pointer ; ld hl,(fncnt) ; Get filename count inc hl ; Bump it ld (fncnt),hl ; Save new count ; mfnlp: ld c,srchn ; Look for more matches call bdos add a,1 dec a ; Set flags jr c,mfndun ; No more matches ; rrca ; *32 rrca rrca add a,cmdbuf ld l,a ; HL=cmdbuf filename pointer ld h,0 jr dirlp ; mfndun: ld a,(cdrive) ; Get drive ld (fcb),a ; Restore it in fcb ; ld hl,(fncnt) ; Check count ld a,h or l ret nz ; We have some matches scf ; No matches ret ; fncmp: ld b,11 ; Match filenames ; strcmp: ld a,(hl) ; Get character and 07fh ; Filter it ld c,a ; Save it ld a,(de) ; Get character and 07fh ; Filter it cp '?' ; Wildcard? jr z,strnc0 ; Yes cp c ; Match? ret nz ; No, quit ; strnc0: inc de ; Bump pointers inc hl djnz strcmp ret ; ; Get string pointers. Looks for '|' OR marker, treats as termination. ; ; Entry:Start with last pointer to end of string in (strend) ; Exit: SLOOP-2=pointer to end of string ; FSKIP+1(et al)=length of string ; Z if end of string ; NZ if terminated with OR ; setstr: ld hl,(strend) ; Last string end pointer ld b,0 ; Initialize character count ld a,(hl) or a ; Termination? jr z,ssdun0 ; Yes cp '|' ; Leading OR? jr z,sslp ; inc b ; No, bump count ; sslp: inc hl ; Point to next character inc b ; Bump count ld a,(hl) or a ; End of string? jr z,ssdun ; cp '|' ; OR? jr nz,sslp ; No, loop ; ssdun: ld (strend),hl ; Save string end pointer dec hl ; Back up to last non-term character dec b ; Back up count ; ssdun0: ld (sloop-2),hl ; Save string end in search code ld a,b ; String length in A ld (fskip+1),a ; Save string length in search code ld (skip0+1),a ld (lmatch+1),a or a ; Set Z if no remaining string ret ; And we're done ; ; Read records into 16k (128record) read buffer. Incorporate any ; orphan text from last buffer read. ; ; Exit: ORPTXT points to any orphan text, ORPLEN is length of text ; EOFFLG=NZ if EOF, Z if not EOF ; Read buffer is terminated with STRLEN+1 markers ; bufrd: ld de,(rdbuf) ; Point to start of read buffer ld bc,(orplen) ; Check for orphan text from last read ld a,b ; Any orphan text? or c jr z,bufrd0 ; No ; ld hl,(orptxt) ; Move orphan text to head of read buffer ldir ; bufrd0: ex de,hl ; HL=read buffer pointer ld b,128 ; Number records in read buffer ; bufrd1: call setdma ; Set DMA address ld de,fcb ; Point to FCB call f$read ; Read one record jr nz,seteof ; EOF ; ld de,80h ; Bump DMA address by one record add hl,de djnz bufrd1 ; Decrement record count and loop ; ld a,(dspflg) ; Check type of display or a jr z,lineb ; Back up one line ; call delbak ; Backup one block jr mvorp ; lineb: call linbak ; Back up to start of last line ; mvorp: push hl ; Save EOB pointer dec hl ; Back up to LF dec bc ld (orplen),bc ; Save length ld a,b ; Any orphan text? or c jr z,mvorp0 ; No ; ld de,(orptxt) ; Point to orphan text buffer ldir ; Move orphan text segment out of the way ; mvorp0: pop hl ; Restore EOB pointer ld a,on ; Load EOB marker ; ; Enter with A=marker ; padbuf: ld (eofflg),a ; Set EOF flag as EOF or EOB ld bc,30h ; Must be strlen (plus...) long jp fillbc ; Pad end of buffer with markers ; ; Add EOF characters ; seteof: ld a,eof ; Load EOF marker jr padbuf ; Pad end of buffer ; ; Search for string in buffer using the Boyer-Moore algorithm. ; ; Entry:HL=Start of buffer to be searched. ; Buffer terminated with EOF (1ah) or EOB marker (ffh). ; ; The proper relative jump for the desired match case requirement is ; patched into the inline code at case1, case2 before calling search. ; ; CASEx=jr_z,xxx: Ignore case ; CASEx=jr_nz,xxx: Exact matches only ; ; The value for string length is patched into the inline code ; at fskip+1, skip0+1 and lmatch+1 before calling search. ; ; The pointer to the last string byte is patched into the inline code ; at sloop-2 before calling search. ; ; Exit: String found: ZERO SET (Z), HL=pointer to string in buffer ; String not found: CARRY SET (C) ; ;----------------------------------------------------------------------- ; search: dec hl ; Make sure we can match string at start ; fskip: ld a,0 ; Get string length ; askip: add a,l ; Skip in buffer by value in a ld l,a jr nc,skip inc h ; skip: ld a,(hl) ; Get character inc a ; Check for end of buffer jr z,nofnd ; Yes, we're done dec a ; Restore character and 07fh ; Filter it cp eof ; End of file? jr z,nofnd ; Yes, quit ; ; Check for first occurrence of buffer byte in find string ; Always nz: patch to jr_nz,skip0 to match exact case ; case1: jr z,skip0 ; Nop's for ignore case ld c,a ; Save character sub 'a' ; If character is lower case cp 'z'-'a'+1 ; set C, else NC sbc a,a ; Carry into A and 'a'-'A' ; Difference between upper and lower case xor c ; Convert to upper case ; skip0: ld b,0 ; Get string length ld c,a ; Save character ld de,0 ; Point to end of search string ; sloop: ld a,(de) ; Get string character dec e ; Back up one cp '?' ; Wild card? jr z,lmatch ; Yes cp c ; Match? jr z,lmatch ; Yes djnz sloop jp (iy) ; No match at all, do full skip ; lmatch: ld a,0 ; Get string length ld c,a ; Save it sub b ; Calculate skip jp nz,askip ; Not last position, do short skip ; ; Match in last position, try to match rest of find string ; ld b,c ; Get string length in B dec b ; One match already ret z ; Done ; push hl ; Save starting buffer pointer ; strmlp: dec hl ; Back up one ld a,(hl) ; Get buffer byte and 07fh ; Filter hi bit ; ; Always nz: patch to jr_nz,strm0 to match exact case ; case2: jr z,strm0 ; Nop for ignore case ld c,a ; Save character sub 'a' ; C if character is lower case cp 'z'-'a'+1 ; else NC sbc a,a ; Carry into A and 'a'-'A' ; Difference between upper and lower case xor c ; Convert to upper case ; strm0: ld c,a ; Save character ld a,(de) ; Get a search string byte cp '?' ; Wildcard? jr z,strm1 ; Yes cp c ; Compare bytes jr nz,nomtch ; No match, we're done ; strm1: dec e ; Back up one djnz strmlp ; Keep trying till end of string ; ; Match found ; pop bc ; Discard buffer pointer xor a ; Set good return ret ; ; EOB - no match ; nofnd: scf ; Set no-match flag ret ; And quit ; ; No match, skip one character and try again ; nomtch: pop hl ; Restore buffer pointer inc hl ; Skip 1 character jp skip ; ; Backup from buffer position in HL to start of line ; ; Entry:HL=buffer pointer ; Exit: HL=first character in line ; BC=number of characters between lf and starting hl ; linbak: ld bc,0 ; Initialize counter ; lblp: inc bc ; Increment count ld a,(hl) ; Get buffer character and 7fh ; Filter it cp lf ; LF? jr nz,lblp0 ; No ; inc hl ; Point to first character in line ret ; lblp0: dec hl jr lblp ; Keep backing up ; ; Backup from buffer position in HL to start of delimeter string ; ; Entry:HL=buffer pointer ; Exit: HL=first LF in block ; BC=length of block ; delbak: ld bc,0 ; Initialize counter ld a,(delim) ; Get delimiter character ld d,a ; dellp: ld a,(hl) ; Get character and 7fh ; Filter it dec hl ; Point to previous inc bc ; Bump counter cp d ; Delimiter? jr z,dellf ; Yes ; push de ld de,(rdbuf) ; Get top of buffer pointer dec de or a ; Clear CARRY sbc hl,de ; Treat top of buffer as delimiter add hl,de pop de ret z ; Top of buffer ; push hl ld hl,blklen ; Maximum delimited block length or a ; Clear CARRY sbc hl,bc ; Make sure we quit at end of one add hl,bc ; max block pop hl jr nc,dellp ; Ok, back up one more ; call eprint ; No delimiter before top of buffer db bell,'Block too long!',lf,0 jp exit ; Quit ; ; We've matched the delimiter, check for line feed ; dellf: ld a,(hl) and 07fh cp lf jr nz,dellp ; No, continue looking ret ; Yes, we're done ; ; Display delimited block ; dispblk:inc hl ld a,(hl) ; Skip initial cr,lf,delim characters and 07fh ; Filter character cp cr jr z,dispblk cp lf jr z,dispblk cp d jr z,dispblk ; dblp: cp eof ; Quit at end of file ret z call fout ; Display character inc hl ; Point to next cp lf ; Line feed? ld a,(hl) ; Get possible delimiter jr nz,dblp0 ; No, continue ; and 07fh ; Filter it cp d ; Delimiter? jr nz,dblp ; No, continue ; ; Display/output new line ; crlf: push af ld a,cr call fout ld a,lf call fout pop af ret ; dblp0: and 07fh ; Filter character jr dblp ; ; Zero out buffer pointed to by HL for BC bytes ; fillz: xor a ; And fall thru ; ; Fill with character in A from HL for BC bytes ; fillbc: ld d,h ; DE=HL+1 ld e,l inc de ld (hl),a ; Load first byte ldir ; Pad buffer ret ; ; Display filename being searched ; showfil:call newline ld a,'-' call fout ; Display/output leader ld a,'>' call fout ld de,fcb call zprdfn ; Display/output filename and fall thru ; ; Display new line, bump line counter ; newline:call crlf ; ; Check for a screen full, pause if necessary ; linchk: ld a,(pageflg) ; Do we want paging? or a jr z,chkabt ; No, check for user abort ; exx ld hl,dlines ; Point to dlines inc (hl) ld a,(lines) cp (hl) exx jr z,pause jr c,pause ret ; ; Pause for input when a full screen of displayed lines ; pause: call eprint db '[More]',0 call cin ; Wait for input cp 3 ; ^C? jr z,abort ; Yes, quit with message ; call eprint db cr,' ',cr,0; Delete message xor a ; Reset counter ld (dlines),a ret ; And continue ; ; Check for keyboard input and user abort request ; chkabt: call condin ; Check for user abort ret z ; Nothing entered ; cp 3 ; User abort with ^C? ret nz ; No ; abort: call eprint db cr,lf,'User abort!',cr,lf,0 jp exit ; ; Parse command line for options ; parsopt:ld hl,cmdbuf+1 ; prsopt: inc hl ; poplp: ld a,(hl) ; Get character or a ; Termination? ret z ; Done cp '/' ; Option? jr z,opts ; Yes cp '>' ; File output? jr nz,prsopt ; No, loop ; dec hl ; Back up ld (hl),0 ; Poke termination inc hl ; And resume ; foopt0: inc hl ; Point to next character ld a,(hl) ; Get it or a ; Termination? ret z ; Done cp ' ' ; Blank filename? jr z,poplp ; Yes, skip it cp '>' ; Append? jr nz,foopt1 ; No ; ld (append),a ; Set append flag jr foopt0 ; Check next character ; foopt1: ld de,outfcb ; Point to fcb call zprsfn ; Parse filename to fcb jp nz,wcexit ; Wildcards ; ld a,(de) ; Default drive? or a jr nz,poplp ; No, continue ; ld a,(cdrive) ; Yes, replace it with actual drive ld (de),a jr poplp ; And continue ; opts: dec hl ; Back up ld (hl),0 ; Poke termination inc hl ; And resume ; optlp: inc hl ; Point to possible option ld a,(hl) ; Get it or a jr z,poplp ; Quit at termination cp ' ' jr z,poplp ; Quit at first space cp 'A' ; Display all files? jr z,aopt ; Yes cp 'B' ; Block display option? jr z,bopt ; Yes cp 'L' ; Line display option? jr z,lopt ; Yes cp 'D' ; Paging option? jr z,popt ; Yes cp 'C' ; Context option? jr z,copt cp 'U' ; Wildcard user option? jr z,uopt cp 'O' ; Overwrite option? jr nz,optlp ; Bad option, continue search ; oopt: ld a,(owflg) ; Get overwrite flag cpl ; Toggle it ld (owflg),a ; Save new flag jr optlp ; uopt: ld a,(wcuflg) ; Get wildcard user flag cpl ; Toggle it ld (wcuflg),a ; Save new flag jr optlp ; copt: ld a,(ctxtflg) ; Get context default cpl ; Toggle it ld (ctxtflg),a ; Save new flag jr optlp ; lopt: xor a ; Set line display mode ; bopt: ld (dspflg),a ; Save new flag jr optlp ; popt: ld a,(pageflg) ; Get paging flag cpl ; Toggle it ld (pageflg),a ; Save new flag jr optlp ; aopt: ld a,(allflg) ; Get all files flag cpl ; Toggle it ld (allflg),a ; Save new flag jr optlp ; ; Set user number in A ; setusr: and 7fh ; Filter it ld e,a ld c,gsusr ; Set user area jp bdos ; ddrive: push de ; Save fcb pointer ld c,getdrv ; Get current drive call bdos pop de ; Restore fcb pointer inc a ; Make drive A=1 ret ; ; Display/output filename ; zprdfn: ld a,(de) ; Get drive or a ; Default? jr nz,dispdu ; No ; ld a,(cdrive) ; Yes, replace it with actual drive ; dispdu: add '@' ; Make it ASCII call fout ; Display it ; ld hl,13 ; Point to user number add hl,de ld a,(hl) ; Get user number and 7fh ; Filter it ; ld b,'0'-1 ; Preset counter cp 10 ; Single digit? jr c,putcln ; Yes, display single digit ; prndu1: inc b ; Count tens digit in B sub 10 ; Keep subtracting 10 until CARRY is set jr nc,prndu1 ; add a,10 ; Get remainder (units digit) back ld c,a ; Save it in C ld a,b ; Get tens digit call fout ld a,c ; Get units digit ; putcln: add a,'0' ; Convert to ASCII call fout ; ld a,':' ; Add colon call fout ; zprfn: push de pop hl ; Pointer in DE,HL inc hl ; Point to filename ld b,8 call dispfn ; zprft: ld hl,9 add hl,de ; Point to file type ld a,(hl) cp ' ' ret z ; No file type, we're done, else fall thru ; dispft: ld a,'.' ; Display separator call fout ; ld b,3 ; Fall thru to display filetype ; dispfn: ld a,(hl) ; Get character and 7fh ; Filter it inc hl or a ; End? ret z ; Yes cp ' ' ; End? ret z ; call fout djnz dispfn ret ; ; Display context delimiter string ; dspdel: ld hl,ctxdel jr dispfn ; leader1:ld b,5 jr lead0 ; leader: ld b,7 ; lead0: call dispsp jp eprint ; dispsp: ld a,' ' ; dsp: call cout djnz dsp ret ; ; Display character ; dcout: call cout ; Display character cp lf ; Line feed? jr z,rescnt ; exx ld hl,column ; Increment column count inc (hl) exx ret ; rescnt: xor a ; Reset column count ld (column),a ret ; ; Execute these control characters ; cntrl: db cr,lf,bell,bs ctllen equ $-cntrl ; ; Display byte in A and send to output file if requested ; fout: ld (byte),a ; Save character cp ' ' ; Check for control character jr nc,fout0 ; Not control character ; exx ld hl,cntrl ; Execute these control characters ld bc,ctllen cpir exx jr z,fout0 ; cp tab ret nz ; Skip other control characters ; ; Expand tab ; push hl ; Save buffer pointer push bc ; Save line count ld a,08h ; Fix count for tabs ld hl,column ; Point to character count add a,(hl) ; Increment to next 8 count and 0f8h ; Make into multiple of 8 sub (hl) ; Get number of spaces to go ld b,a ; Save it in B ld a,' ' ; Load space ; tab1: call dcout ; Display it djnz tab1 ; More spaces to go ; pop bc ; Restore line count pop hl ; Restore buffer pointer jr fout1 ; fout0: call dcout ; fout1: ld a,(output) or a ld a,(byte) ; Restore character ret z ; No file output ; push hl ; Save registers push de ld hl,(bytenxt) ; Get pointer to next byte ld de,(mem) ; See if byte will fit in buffer call comphd call nc,flushb ; Flush buffer to file if byte won't fit ld a,(byte) ; Get next byte ld (hl),a ; put it in buffer inc hl ; Point to next byte ld (bytenxt),hl ; Update pointer pop de ; Restore registers pop hl ret ; ; Close output file, fill last record with EOF characters ; Exit: NZ if error in closing file ; fx$clo: ld hl,(bytenxt) ; Get buffer pointer ld a,l ; Done if on page boundary and 7fh jr z,fxc0 ; ld (hl),eof ; Poke EOF char inc hl ld (bytenxt),hl ; Update pointer jr fx$clo ; Loop until last record is full ; fxc0: call flushb ; Flush buffers to disk ld de,outfcb ; Get fcb address jp f$close ; Close file and quit ; ; Flush buffer to disk and initialize for next write ; flushb: push bc ; Save registers or a ; Clear CARRY ld hl,(bytenxt) ; Get next byte ld de,(bufadr) ; Get start of buffer push de sbc hl,de ; Get length of buffer ; ld bc,127 ; Calculate number of records add hl,bc add hl,hl ld c,h ; Number of records in BC ; nrecs: pop hl ; Restore buffer pointer ; flblp: call setdma ; Set dma ld de,outfcb ; Point to fcb call f$write ; Write next record jp nz,weexit ; Quit if write error ; ld de,128 ; Get record length add hl,de ; Point to next record dec bc ; Count down ld a,b or c jr nz,flblp ; Loop to write all records ; pop bc ; Restore BC and fall thru ld hl,(bufadr) ; Point to start of buffer ; pinit: ld (bytenxt),hl ; Save as next byte pointer ret ; ; Scan last record of append file for EOF, set buffer pointer ; setptrs:ld b,128 ; Only look at first record ; splp: ld a,(hl) ; Get byte or a ; Check for empty record jr z,pinit ; Save new pointer cp eof ; Check for EOF jr z,pinit ; Save new pointer inc hl djnz splp jr pinit ; Save new pointer ; ; Open output file if requested ; ofile: ld a,(output) ; Check file output flag or a ret nz ; Yes, already opened ; ld a,(outfcb+1) ; Check for output file or a ret z ; None specified ; ; Initialize buffer pointers for file output ; ld (output),a ; Set output flag ; ld a,(outfcb+13) ; Set default user for output file call setusr ; ld de,outfcb ; Point to output FCB ld a,(append) ; Check for append or a jr nz,aopen ; Yes, open for append ; call f$exist ; Check for existing file jr z,mopen ; None, continue ; ld a,(owflg) ; Check overwrite flag or a jr nz,mopen ; Yes, continue ; call eprint db ' File exists - Overwrite? ',0 call cin push af ; Save flag call cout call eprint db cr,lf,0 pop af ; Restore flag cp 'y' jr z,mopen ; Yes, continue cp 'Y' jr z,mopen ; Yes, continue jp exit0 ; No, quit now ; aopen: ld hl,(bufadr) ; Get buffer address call setdma ; Set dma call f$appl ; Open file for appending ld hl,(bufadr) jp z,setptrs ; Set buffer pointers ; mopen: call f$mopen ; Open and/or create file jp nz,oexit ; Error - no directory space ; ; Compare HL with DE, set flags ; ; Exit: DE>HL C,NZ ; DE=HL NC,Z ; DE