; FOR.Z80 ; ; Takes a list of (ambiguous) file specifications as parameters and stores ; them in a file (FORFILES.SYS), optionally expanding the ambiguous names. ; Explicit directory specifications are retained in front of the names. ; The output file FORFILES.SYS is set to System status. This is an ordinary ; text file, with one filename (or other list element) on each line. ; ; Alternatively, arbitrary strings, all named directories, an ascending ; sequence of integers, or all current shell variable names may be written ; to the file. ; ; Syntax: ; ; FOR <[du:|dir:]fn.ft> [<[du:|dir:]fn.ft>] [/X] ; ; where the 'X' option indicates that all ambiguous filenames are to be ; expanded. If an ambiguous filename is prefaced with a DU: or DIR: ; specification, its expansions will all also contain the directory ; specification. ; ; -or- ; ; FOR 'one string' "another string" \a third string\ /S ; ; where the 'S' option is REQUIRED to indicate that the list elements ; are delimited strings. Any non-blank character except the virgule ; (/) and comma may be used as a delimiter. ; ; -or- ; ; FOR /o ; ; where 'o' can be: ; D -- list all named directories ; Rn -- list all integers up to that contained in register 'n', ; one per line. The list is zero-based ; Nn -- list all integers up to 'n', one per line (zero-based). ; V -- list all currently defined shell variable names. ; ; If the 'Nn' or 'Rn' forms are used and a 1-based list is needed, ; the first element can be removed with 'NEXT' and not used. Any ; parameters preceding one of these option specifications will be ; ignored. ; ; In all circumstances, only one option is appropriate. As presently written, ; program control is transferred directly based upon the option, rather than ; using an array of option flags. ; ; Delimiters BETWEEN list elements (whether filenames or strings) may be ; either spaces or commas. ; ; ; Author: Dreas Nielsen ; History -- ; Date Version Comments ; ---- ------- -------- ; 7/15/86 0.1 First code ; 1/10/87 0.2 Fixed ambiguous filenames ; 1/11/87 1.0 First complete version ; 2/19/89 1.1 Added shell variable name expansion. ; 3/19/89 1.2 Removed unreferenced externals, ; labels, etc., made code compatible ; with non-ZAS assemblers, removed/ ; corrected anamolous code, established ; DSEG to simplify buffer allocation, ; generally tuned up and tightened code, ; linked to a new standalone VARLOAD.REL ; and the new Z3LIB/SYSLIB, resulting in ; a 30-record COMfile vs. 51 records for ; v1.1. ; Bruce Morgen ; ; ;================[ Equates and External Routines ]================ ; VERS EQU 12 ; FALSE EQU 0 TRUE EQU NOT FALSE ; DEBUG EQU FALSE ; CR EQU 0DH LF EQU 0AH BELL EQU 7 TAB EQU 9 SPACE EQU 20H CTRLZ EQU 1AH EOF EQU 1AH BDOS EQU 5 INFERR EQU 1 ;error code for "can't open input file" OFERR EQU 2 ;error code for "can't open output file" RDERR EQU 3 ;error code for "can't read input file" WRTERR EQU 4 ;error code for "can't write output file" CLZERR EQU 5 ;error code for "can't close output file" PARMFL EQU '/' XPARM EQU 'X' LINLEN EQU 300 ;max # of chars in output line CMDLIN EQU 080H SYSFCB EQU 05CH BUFSIZ EQU 8 ;8 128-byte sectors (1k) for I/O buffers FCBLEN EQU 36 BSZOFF EQU 0 ;offset of buffer size indicator from I/O ctl block BADOFF EQU 6 ;offset of buffer addr indic. from I/O ctl block start FCBOFF EQU 8 ;offset of fcb from I/O ctl block start ; PUBLIC $MEMRY EXT Z3INIT,GETQUIET,SKSP,EPRINT,PUTUD,GETUD,LOGUD EXT PUTER2,FXO$OPEN,FX$PUT,FXO$CLOSE EXT DIRQ,MFN2,FILLB EXT DNSCAN,FNAME,GETNDR,GETREG,EVAL10,MHLFDC EXT VARLOAD,Z3VARS ; ;================[ Beginning of Program ]================ ; DB 'Z3ENV' DB 1 Z3ENV: DW 00 DB VERS ; START: LD HL,(Z3ENV) ; ; Save stack and set a new one LD (SAVESP),SP LD SP,STK ; CALL Z3INIT ; ; Reset error flag XOR A CALL PUTER2 CALL GETQUIET JR NZ,QBEGIN ; Print signon message CALL EPRINT DB 'FOR v. ',[VERS / 10] + '0','.',[VERS MOD 10] + '0',CR,LF,0 ; ; Save currently logged DU QBEGIN: CALL PUTUD ; ; Check for no parameters (help) LD A,(SYSFCB+1) CP ' ' JP Z,HELP ; ; Store null at end of cmdline (it's already there, isn't it?....) LD HL,CMDLIN LD C,(HL) INC C LD B,0 PUSH HL ADD HL,BC LD (HL),B POP HL INC HL ; ; Move command line LD DE,PARAMS LDIR ; ; Allocate output buffer and initialize it LD HL,($MEMRY) LD B,BUFSIZ PUSH HL CALL FBINIT ;allocate I/O ctl buffer for output LD (FREE),HL POP HL LD DE,OFNAM CALL INITNAM ; ; Examine command line for option specification. If no options are specified, ; the list is presumed to be of filenames which are not to be expanded or ; undelimited strings (without embedded spaces or commas). This routine finds ; the LAST parameter flag on the line, as there may be some embedded in ; delimited strings. GETOPT: LD HL,00 ;zero out parameter address LD (PFADR),HL LD HL,PARAMS GETO2: LD A,(HL) INC HL OR A ;end of list? JR Z,GETO3 CP PARMFL JR NZ,GETO2 LD (PFADR),HL JR GETO2 ; GETO3: LD HL,(PFADR) LD A,H OR L ;if this is null... JR Z,RAWFILES ;...no parameter flag was found ; ; Examine char after '/' and proceed accordingly CALL SKSP LD A,(HL) CP 'X' JR Z,AMBFILS ;ambiguous filenames CP 'S' JP Z,STRINGS ;delimited strings CP 'D' JP Z,DIRNAMES ;directory names CP 'R' JP Z,REGS ;register value CP 'N' JP Z,NUM ;integer value CP 'V' JP Z,VARS ; shell variable names JP HELP ;because it is an unrecognized option ; ; Parameter list is unambigous filenames or undelimited strings. RAWFILES: CALL OPENOUT LD HL,PARAMS RAW1: LD A,(HL) ;skip to first non-delimiter INC HL CALL LISTDEL JR Z,RAW1 OR A JR Z,RAW6 DEC HL ;point back 1 to fetch 1st char again ; RAW2: LD DE,NMDEST ;transfer token RAW3: LD A,(HL) INC HL OR A ;end of list? JR Z,RAW4 CALL LISTDEL ;space or comma? JR Z,RAW4 LD (DE),A INC DE JR RAW3 ;get next char of current token ; RAW4: ;write current token and look for next CALL WRTTOK JR NZ,RAW2 ;next token found -- get and write it ; RAW6: ;end of list found CALL CLOSOUT JP DONE ; ;---------------- ; Parameter list is a list of ambiguous filenames. AMBFILS: CALL OPENOUT LD HL,PARAMS ;save prefix (DU: or DIR:) if it exists AMB0: LD A,(HL) ;skip to first non-delimiter INC HL CALL LISTDEL ;is (A) a list delimiter? JR Z,AMB0 ;if so, get next character OR A JP Z,AMB3 ;if end of list, quit DEC HL ;point back 1 to 1st char of token PUSH HL ;save starting point CALL NXTDLM ;see if next delimiter is a colon POP HL ;get starting addr. back LD DE,DIRNAM ;destination buffer; HL points to source CP ':' JR NZ,NOMOV ; ;move DU:/DIR: spec into buffer MOVDU: LD A,(HL) LD (DE),A INC HL INC DE CP ':' JR NZ,MOVDU ; NOMOV: XOR A ;null-terminate DU:/DIR: spec buffer LD (DE),A ; ; Parse the filename pointed to by HL into FCB format PUSH HL ;save ptr to filename LD HL,DIRNAM LD A,(HL) OR A JR Z,P2 ;don't scan if no DU/DIR XOR A ;DU before DIR CALL DNSCAN JR Z,P2 ;if invalid, stay here CALL LOGUD P2: POP HL ;get filename LD DE,SYSFCB CALL FNAME ;parse filename into FCB JR Z,AMB2 LD (CLPTR),HL ;save pointer to tokens LD HL,(FREE) ;buffer area for directory load LD A,10100000B ;non-system files sorted by name CALL DIRQ ;load directory CALL GETUD ;return home to write file ; INC HL ;point to first name EX DE,HL ;...with DE WRNAMS: LD A,B ;for all filenames in buffer... OR C JR Z,WRNAM3 PUSH DE ;(save ptr to name) LD DE,DIRNAM ;...move directory name to write buffer LD HL,NMDEST WRNAM1: LD A,(DE) INC DE OR A JR Z,WRNAM2 LD (HL),A INC HL JR WRNAM1 WRNAM2: POP DE ;HL = mem buffer, DE = name ; PUSH BC ;write 13 nulls, so name will be null-term. LD B,13 XOR A CALL FILLB POP BC ; CALL MFN2 ;convert fname to packed string in wrt buffer PUSH DE ; CALL WRTSTR ;write name to file POP DE LD HL,16 ;set DE to point to next name ADD HL,DE EX DE,HL DEC BC ;count down number of names written JR WRNAMS ; WRNAM3: ;done with this token, look for next LD HL,(CLPTR) AMB2: LD A,(HL) INC HL OR A JR Z,AMB3 CP PARMFL JR Z,AMB3 CALL LISTDEL JR Z,AMB2 DEC HL ;if another token found, point to 1st char JP AMB0 ;go back and process it ; AMB3: CALL CLOSOUT JP DONE ; ; ;---------------- ; Chop parameter list into delimited strings. ; STRINGS: CALL OPENOUT LD HL,PARAMS STR1: LD A,(HL) ;skip to first non-comma, non-space INC HL CALL LISTDEL JR Z,STR1 OR A JR Z,STR5 DEC HL ; STR2: LD A,(HL) LD B,A ;save delimiter for comparison INC HL LD DE,NMDEST STR3: LD A,(HL) INC HL OR A JR Z,STR4 CP B ;the current string delimiter is used here JR Z,STR4 LD (DE),A INC DE JR STR3 ; STR4: OR A ;if not null, bump HL by 1 more to account... JR Z,STR5 ;...for DEC HL in WRTTOK INC HL STR5: CALL WRTTOK JR NZ,STR2 ; STR6: CALL CLOSOUT JP DONE ; ; ; List all directory names. ; DIRNAMES: CALL GETNDR JP Z,DONE LD A,(HL) OR A JP Z,DONE ; There is a buffer and there is at least one entry in it CALL OPENOUT DIRN1: LD A,(HL) OR A JR Z,DIRNZ ; end of list reached INC HL INC HL ; now pointing to name PUSH HL ; save this pointer LD DE,NMDEST ; transfer name to output buffer LD B,8 DIRN2: LD A,(HL) CP ' ' JR Z,DIRN3 LD (DE),A INC HL INC DE DJNZ DIRN2 ; DIRN3: LD A,':' LD (DE),A INC DE XOR A LD (DE),A CALL WRTSTR ; POP HL ; advance to next directory name LD DE,16 ADD HL,DE JR DIRN1 ; DIRNZ: CALL CLOSOUT ; all done with directory names JP DONE ; ; ; Write all numbers up to the value contained in the register specified by ; the character at (HL+1). The list will be zero-based and will run up to ; the register value - 1. ; REGS: INC HL LD A,(HL) OR A JR Z,REGERR ; premature eol SUB '0' JR C,REGERR ; illegal value JR Z,REGERR CP 10 JR NC,REGERR ; A register value is specified and it is legal LD B,A CALL GETREG LD E,A LD D,0 JR WRTNUMS ; REGERR: CALL EPRINT DB 'Improper register value.',CR,LF,0 XOR A DEC A CALL PUTER2 JP DONE ; ; ; Write a zero-based list of numbers, up to (but not including) the value ; indicated by the string at (HL+1). ; NUM: INC HL LD A,(HL) OR A JR Z,NUMERR CALL EVAL10 JR WRTNUMS ; NUMERR: CALL EPRINT DB 'Unspecified numeric argument.',CR,LF,0 XOR A DEC A CALL PUTER2 JP DONE ; ; Write out numbers up to the value in DE if DE > 0 WRTNUMS: LD A,E OR D JP Z,DONE ; don't write anything if arg is 0 PUSH DE CALL OPENOUT POP DE LD HL,00 ; HL counts up to limit in DE WRTNUM1: PUSH DE ; save limit LD DE,NMDEST ; format & write number CALL MHLFDC XOR A LD (DE),A PUSH HL CALL WRTSTR POP HL POP DE ; get limit back INC HL ; compute next number to write PUSH HL ; save it while comparing to limit OR A ; clear carry flag SBC HL,DE ; at limit yet? POP HL ; get current value back ; if at limit, go quit to WRTNUMZ JR NZ,WRTNUM1 ; else go back and write the next number ; WRTNUMZ: CALL CLOSOUT JP DONE ; ; ; Write a list of all currently defined shell variable names. VARS: LD HL,(FREE) CALL VARLOAD JP NZ,DONE LD (FREE),HL LD HL,(Z3VARS) ; origin of list--same as (FREE) passed. LD A,(HL) CP 1AH ; eof? JP Z,DONE CALL OPENOUT ; ; Transfer each variable name to the name buffer, null terminate, and ; write out. LD HL,(Z3VARS) ; ; While not eof (^Z at HL) VARS1: LD A,(HL) CP 1AH JR Z,VARS5 ; Transfer up to 8 characters to destination buffer. LD DE,NMDEST LD B,8 VARS2: LD A,(HL) CP ' ' JR Z,VARS3 ; Exit loop if end of name found LD (DE),A INC HL INC DE DJNZ VARS2 VARS3: ; Terminate name XOR A LD (DE),A ; Write out name. PUSH HL CALL WRTSTR POP HL ; Scan to end of this definition, skip to next name. XOR A LD BC,0FFFFH CPIR ; End while JR VARS1 ; VARS5: CALL CLOSOUT JP DONE ; ; ; ; Print help message and fall through to exit. HELP: CALL EPRINT DB 'Syntax is:',CR,LF DB ' FOR <[du:|dir:]fn.ft> [<[du:|dir:]fn.ft>] [/x]',CR,LF DB 'or',CR,LF DB ' FOR ''one string'' "another string" \a third string\ /s',CR,LF DB 'or',CR,LF DB ' FOR /o',CR,LF DB 'Options:',CR,LF DB ' x -- Expand ambiguous filenames',CR,LF DB ' s -- List elements are delimited strings',CR,LF DB ' o -- May be:',CR,LF DB TAB,' D -- list all named directories',CR,LF DB TAB,' Rn -- list all integers up to that contained in register ''n'',',CR,LF DB TAB,' one per line. The list is zero-based.',CR,LF DB TAB,' Nn -- list all integers up to ''n'', one per line (zero-based).',CR,LF DB TAB,' V -- list all current shell variable names.',CR,LF DB 0 ; ; ; Clean up and exit program. DONE: CALL GETUD LD SP,(SAVESP) RET ; ; ;================[ Subroutines ]================ ; ; Initialize file I/O control buffers. ; Enter with HL = first free address in memory ; B = number of 128-byte sectors for the file buffer ; Return: HL = first free address after buffer ; FBINIT: PUSH DE PUSH BC LD (HL),B LD DE,BADOFF ;loc of buf addr in I/O ctl block ADD HL,DE PUSH HL LD DE,FCBLEN+FCBOFF-BADOFF ADD HL,DE EX DE,HL POP HL LD (HL),E INC HL LD (HL),D EX DE,HL ;get buf start addr in HL LD DE,128 ;incr HL by buf len in bytes FBINI1: ADD HL,DE DJNZ FBINI1 POP BC POP DE RET ; ;---------------- ; Move filename into fcb of I/O ctl block. Enter with HL = I/O ctl blk addr, ; DE = addr of string to move. Drive is set to current. INITNAM: PUSH BC PUSH DE ;save while adding fcb offset LD DE,FCBOFF ADD HL,DE ;point to input fcb XOR A ;set current drive LD (HL),A INC HL ;point to name field of fcb POP DE ;get source addr EX DE,HL ;put dest addr in DE, source in HL LD BC,11 LDIR POP BC ;restore original contents RET ; ;---------------- ; Open output file OPENOUT: LD DE,($MEMRY) JP FXO$OPEN ; ;---------------- ; Close output file CLOSOUT: LD DE,($MEMRY) JP FXO$CLOSE ; ;---------------- ; Find next delimiter in string pointed to by HL. Return with delim. in A, ; HL pointing past char. Delimiters are: ; / , : NXTDLM: LD A,(HL) INC HL CP ':' RET Z CP ',' RET Z CP ' ' RET Z OR A RET Z CP '/' RET Z JR NXTDLM ; ;---------------- ; Is (A) a list-element delimiter (a comma or space?). Return Z if true. LISTDEL: CP SPACE RET Z CP ',' RET ; ;---------------- ; Write the string at NMDEST to the output file. String will be ; terminated with a CR/LF. WRTSTR: LD DE,($MEMRY) LD HL,NMDEST WRT2: LD A,(HL) INC HL OR A JR Z,WRT3 CALL FX$PUT JR WRT2 WRT3: LD A,CR CALL FX$PUT LD A,LF JP FX$PUT ; ;---------------- ; Null-terminate the current token and write it out, then search for the next. ; Return Z if the end of the input has been reached, NZ otherwise. On return, ; HL points to the next non-blank, non-comma character. WRTTOK: XOR A ;store null at end of token LD (DE),A PUSH HL CALL WRTSTR POP HL DEC HL ;point to delim again in case it's a null WRTT: LD A,(HL) ;skip over multiple delimiters INC HL OR A ;No blank lines can be generated by multiple RET Z ;delimiters. CP PARMFL RET Z CALL LISTDEL JR Z,WRTT DEC HL ;we'll fetch the non-delimiter again RET ; $MEMRY: DS 2 ; OFNAM: DB 'FORFILESS','Y'+80H,'S' ; ;================[ Storage ]================ ; DSEG SAVESP: DS 2 PARAMS: ;Buffer for list of files (command line) DS 127 DIRNAM: DS 24 ;Buffer for directory name NMDEST: DS LINLEN ;Buffer for destn for packed name string PFADR: DS 2 ;address following last parameter flag in command line CLPTR: DS 2 ;temp. storage for pointer to cmd line FREE: DS 2 ;addr of beginning of free memory DS 48 STK: DS 2 ; ; END START