; FL.Z80 ; ; A ZCPR3 utility to create file lists on disk. ; Vers equ 10 SubVers equ ' ' ; ; USAGE: ; ; FL {{dir:}{afn} {...}} {~afn} {/options} ; ; If no DU or DIR is given, the current default is assumed. If no filename ; is given, all files ("*.*") is assumed. Files matching the negative ; filespec (preceded by a tilde) are excluded. Although multiple filespecs ; may be given, directory specs after the first are ignored. ; ; OPTIONS: A leading slash is required. ; ; O Omit output of matching filenames to file FILELIST. ; File output is the default. ; ; D Include DU specs with filenames in FILELIST. ; ; S Include system (hidden) files. Normally system files ; are ignored. ; ; P Do not page screen display. Normally FL waits for a ; key to be pressed after each screenful of filenames. ; ; Q Quiet mode suppresses console output. ; ; HISTORY: ; ; Version 1.0 -- January 16, 1992 -- Gene Pizzetta ; Basic code from CPD 1.4, which made the job a lot easier. ; Multiple ambiguous filespecs allowed, but only one negative ; filespec. That shouldn't be too difficult to expand on in ; future enhancements. Also might be nice to be able to select ; files by attributes or combinations of attributes. ; ; Please report bugs and make suggestions to: ; ; Gene Pizzetta ; 481 Revere St. ; Revere, MA 02151 ; ; Voice: (617) 284-0891 ; Newton Centre Z-Node: (617) 965-7259 ; Ladera Z-Node Central: (213) 670-9465 ; ; System addresses ; CpmFcb equ 005Ch ; default file control block CpmDma equ 0080h ; default DMA buffer ; ; ASCII ; CtrlC equ 03h ; ^C LF equ 0Ah ; line feed FF equ 0Ch ; form feed CR equ 0Dh ; carriage return CtrlS equ 13h ; ^S ; .request zflib,zslib,z3lib,syslib ; extrn flwopen,flwclose,flput ; ZFLIB extrn eatspc,eatnspc,gcomnam,comnam,revcas,hvtinit ; ZSLIB extrn hvdinit,hvon,hvoff extrn z3init,zsyschk,dundr,getcrt,puter2,inverror ; Z3LIB extrn zfname extrn eprint,epstr,cout,crlf,condin,cin,pafdc,phldc ; SYSLIB extrn dirq,@afncmp,retud,logud,codend ; jp Start db 'Z3ENV',1 Z3EAdr: dw 0 ; ; Configuration . . . ; dw 0 ; filler db 'FL' ; for ZCNFG db Vers/10+'0',Vers mod 10+'0',' ' SysFlg: db 0 ; FFh=include system files FOFlg: db 0FFh ; FFh=file output PagFlg: db 0 ; FFh=no screen paging QtFlag: db 0 ; FFh=quiet mode FLDu: db 0 ; FFh=include DU's in file output FLDir: db 0FFh ; FFh=file output to target DU, 0=default DU CasFlg: db 0FFh ; FFh=reverse case of filenames AbtFlg: db 0 ; FFh=invoke error handler on abort ConDiv: db '|' ; character printed between screen columns OutFcb: db 0,'FILELIST ' ; FILELIST abbreviated FCB ; ; Start of program . . . ; Start: ld hl,(Z3EAdr) ; environment address call zsyschk ; check for ZCPR3 ret nz ; (no, quit) ld (Stack),sp ; save stack pointer ld sp,Stack ; ..and set up new stack call z3init ; initialize environment ld hl,DftNam ; point to default program name call gcomnam ; get real program name ; call codend ; get start of buffer ld (DirBuf),hl call hvtinit ; initialize terminal ld hl,CpmDma+1 ; point to command tail call eatspc ; anything there? jp z,Start1 ; (nope) cp '/' ; help request? jr nz,Start1 ; (nope) inc hl ; a second slash? cp (hl) jp z,Usage ; (yep, help request) ; Start1: call retud ; get default DU ld (DftDU),bc ; ..and store it call GetOpt ; check for options call GetNeg ; check for negative filespec ld hl,CpmDma+1 ; move tail to storage ld de,CLBuf push de ld bc,128 ldir pop hl ; parse first filename call eatspc ld (CLPtr),hl jr z,NoFn cp '/' jr z,NoFn cp '~' jr z,UseNeg ld de,CpmFcb call zfname ld (CLPtr),hl jr ChkFn ; UseNeg: ld hl,NegFcb ld de,CpmFcb ld bc,16 ldir NoFn: ld a,' ' ld (CpmFcb+1),a ; ChkFn: call ChkAmb ld ix,CpmFcb ; point to file control block call GetDU ; ..and get drive and user ld (DirDU),bc ; store user and drive call PrtHdr ; print header ld a,(ScrCol) ; set initial screen column count ld (ColCnt),a ld a,(OpOFlg) or a call nz,OpnFil ; open file list ; ; Re-entry point for each filespec ; Main: ld bc,(DirDU) ; log into main directory call logud xor a ; null-out the drive spec ld de,CpmFcb ld (de),a ld hl,(DirBuf) ; point to free buffer space ld a,(OpSFlg) ; non-system and maybe system files call dirq ; load drive 1 directory jp z,TPAOvr ; (DIRQ ran out of memory) ld (DirPtr),hl ; store drive 1 file list pointer ld (DirCnt),bc ; ..and file count ld a,b ; any matching files? or c jp z,DoNext ; (nope) ; MLoop: call condin ; check for key call nz,Abort ; (key pressed, check for ^C) ld hl,(DirPtr) ; HL = address of next entry inc hl ; HL = address of filename call CmpFn ; compare it to negative filename jr z,SkpFil ; (match, skip it) ; push hl call CkScrn pop hl call PrtFn ; no match, print filename ld hl,(MatCnt) inc hl ld (MatCnt),hl call ChkCnt ; check file counter jr z,DoNext ; (none left) jr SkpF2 ; ..and continue ; SkpFil: call ChkCnt ; check file counter jr z,DoNext ; (none left) SkpF2: ld hl,(DirPtr) ; advance file list pointer ld de,16 add hl,de ld (DirPtr),hl jp MLoop ; DoNext: ld hl,(CLPtr) call eatspc jr z,Finish cp '~' jr z,Finish cp '/' jr z,Finish ld de,CpmFcb call zfname ld (CLPtr),hl jr Main ; Finish: ld a,(OpQFlg) or a jr nz,ChkMat ld hl,(MatCnt) ; number of matching file found ld a,h or l call nz,crlf call hvon ; turn highlighting on call phldc ; print file count call eprint db ' matching file',0 call PrtEss ; print an "s" if appropriate call eprint db ' found on ',0 ld bc,(DirDU) ; print DU call PrtDU call hvoff ChkMat: ld hl,(MatCnt) ld a,h or l ld a,0FFh ; error code for no matches jr z,ErExit ; Exit: xor a ; no errors ErExit push af ; save error code ld a,(OpOFlg) or a call nz,ClsFil ; (yes, close it) call hvdinit ; de-initialize terminal pop af ; recover error code call puter2 ld b,a ; error code to B or a jr z,Exit2 ; (no error) inc a call nz,inverror ; invoke error handler unless code is FFh Exit2: ld sp,(Stack) ; restore stack ret ; return to Z ; ; Various problems end up here . . . ; Abort: cp CtrlS ; ^S jr z,Pause cp CtrlC ; ^C? ret nz ; (no, continue) ld a,(OpQFlg) or a call z,crlf Abort1: call eprint db CR,'Aborted',0 ld a,(AbtFlg) ; check abort flag and 4 ; set error code accordingly jr ErExit ; Pause: call cin ret ; InvDir: call eprint db 'Invalid directory.',0 ld a,2 ; error code jr ErExit ; InvOpt: call eprint db 'Invalid option.',0 ld a,19 ; error code jr ErExit ; TPAOvr: call eprint db 'Out of memory.',0 ld a,12 ; error code jp ErExit ; ; CmpFn -- Compare filename pointed by HL with filename in the ; negative file control block. Return zero flag set (Z) if match, ; zero flag reset (NZ) if no match. Preserves HL. ; CmpFn: push hl ex de,hl ld hl,NegFcb+1 ld b,11 ; compare 11 characters call @afncmp pop hl ret ; ; ChkCnt -- decrements file list count. Returns zero if no more. ; ChkCnt: ld hl,(DirCnt) ; decrement file list count dec hl ld (DirCnt),hl ld a,l or h ret ; ; GetNeg -- Look for negative filespec on command line. If found, ; set NegFlg and parse it to NegFcb. ; GetNeg: xor a ld (NegFlg),a ld hl,CpmDma+1 GetNLp: call eatnspc call eatspc ret z ; (end of command line) cp '~' jr nz,GetNLp inc hl ; move past tilde ld de,NegFcb xor a ; DIR before DU call zfname ld a,0FFh ; set negative flag ld (NegFlg),a ret ; ; GetOpt -- initialize defaults and get command line options ; GetOpt: ld hl,0 ; initialize to nulls ld (MatCnt),hl ; ..matching files counter ld hl,FOFlg ; move defaults for FOFlag, ld de,OpOFlg ; ..PagFlg, QtFlag, and FLdu ld bc,4 ; ..to options O, P, Q, and D ldir ld a,(SysFlg) or a ld a,10000000b ; default to excluding system files jr z,GetOp1 or 01000000b ; no, include them also GetOp1: ld (OpSFlg),a call getcrt ld a,(hl) ; get screen columns cp 122 ld a,8 jr nc,SetCol ld a,5 SetCol: ld (ScrCol),a inc hl ld a,(hl) ; get screen lines dec a ; make it one less ld (ScrLns),a ; ..store it dec a ; one less on first screen ld (LinCnt),a ld hl,CpmDma+1 ; point to command tail SkpLp: call eatnspc call eatspc ret z ; (end of tail) cp '/' ; options? jr nz,SkpLp ; (yes, get them) OptLp: inc hl ; point to next ld a,(hl) ; get option or a ret z cp 'S' ; system files jr z,OptS cp 'P' ; screen paging jr z,OptP cp 'O' ; disk file output jr z,OptO cp 'Q' ; quiet mode jr z,OptQ cp 'D' ; include DU in output jr z,OptD cp ' ' ; space is okay jp nz,InvOpt ; must be a bad option jr OptLp ; OptS: ld a,(OpSFlg) ; get flag xor 01000000b ; ..flip system file bit ld (OpSFlg),a ; ..and store it jr OptLp ; OptO: ld a,(OpOFlg) ; get flag cpl ; ..flip it ld (OpOFlg),a ; ..and store it jr OptLp ; OptP: ld a,(OpPFlg) ; get flag cpl ; ..flip it ld (OpPFlg),a ; ..and store it jr OptLp ; OptQ: ld (OpQFlg),a ; set flag cpl ld (OpQFlg),a jr OptLp ; OptD: ld a,(OpDFlg) ; include DU in file output cpl ld (OpDFlg),a jp OptLp ; ; PrtHdr -- prints directory header ; PrtHdr: ld a,(OpQFlg) or a ret nz call hvon ; turn on highlighting call eprint db 'File list from ',0 ld bc,(DirDU) call PrtDU ; first DU call hvoff ; turn highlighting off jp crlf ; jump and return ; ; GetDU -- get drive and user for file control block addressed by IX. ; Returns drive in B (A=0), user in C. Uses AF, BC. ; GetDU: ld a,(ix+15) ; check for valid directory or a jp nz,InvDir ; (nope) ld a,(ix+0) ; get drive (A=1) or a ; is there one? call z,GetDft ; (no, get default) dec a ; make A=0 ld b,a ; drive into B ld a,(ix+13) ; get user ld c,a ; put user in C ret ; ; GetDft -- gets default drive and user. Increments drive to A=1. ; GetDft: call retud ; C=user, B=drive (A=0) ld a,b ; A=drive (A=0) inc a ; A=drive (A=1) ret ; ; PrtDU -- Prints DU and directory name. Expects drive in B, user in C. ; PrtDU: ld a,b add a,'A' call cout ; print drive ld a,c call pafdc ; print user ld a,':' call cout inc b ; make drive A=1 call dundr ; get directory name ret z ; (none) inc hl inc hl ld b,8 PNdrLp: ld a,(hl) cp ' ' ret z call cout inc hl djnz PNdrLp ret ; ; PrtFn -- Print filename pointed to by HL. Printing in normal or reverse ; case are in separate loops for speed. ; PrtFn: ld bc,(DirDU) ; load DU ld a,(OpOFlg) or a ld a,(OpDFlg) ; load DU option flag call nz,flput ; file output first ld a,(OpQFlg) or a ret nz ld b,8 ; first print the filename ld a,(CasFlg) ; do we reverse the case? or a jr nz,PrtFn1 ; (yes) call PFnLp ; no, use it as is jr PrtFn2 ; PrtFn1: call RFnLp ; PrtFn2: ld a,'.' ; now print the filetype call cout ld b,3 ld a,(CasFlg) ; do we reverse the case? or a jr nz,RFnLp ; (yep) ; PFnLp: ld a,(hl) ; get character and 7Fh ; reset high bit call cout inc hl ; increment pointer djnz PFnLp ; loop for length of filename ret ; RFnLp: ld a,(hl) ; get character call revcas ; reset high bit and reverse case call cout inc hl ; increment pointer djnz RFnLp ; loop for length of filename ret ; ; OpnFil -- Open disk output file. ; OpnFil: ld bc,(DirDU) ; assume logging target directory ld a,(FLDir) ; default or target directory? or a jr nz,OpnFl1 ; (target directory) ld bc,(DftDU) ; log into default directory OpnFl1: call logud ld de,OutFcb call flwopen ; open file jr nz,FilErr ; (error) ret ; ; ClsFil -- Close disk output file. ; ClsFil: call flwclose jr nz,FilErr ret ; FilErr: call eprint db CR,LF,'Output file error.',0 ld a,11 ; error code jp ErExit ; ; ChkAmb -- Checks for filename. If it is blank, make it ambiguous. ; ChkAmb: ld a,(CpmFcb+1) ; see if filename is given cp ' ' ret nz ; (yes) ld hl,CpmFcb+1 ; no, make it all "?"'s ld a,'?' ld b,11 AmbLp: ld (hl),a inc hl djnz AmbLp ret ; ; CkScrn -- Check screen columns and screen lines used. ; CkScrn: ld a,(OpQFlg) or a ret nz ; (quiet mode, do nothing) ld hl,(MatCnt) ld a,h or l ret z ; (no files printed yet, do nothing) ld hl,ColCnt dec (hl) ; is line full? jr z,LnFull ; (yes) ld a,' ' call cout ld a,(ConDiv) call cout ld a,' ' call cout ret ; ..and get next filename ; LnFull: call crlf ; start a new line ld a,(ScrCol) ; reset column count ld (ColCnt),a ld a,(OpPFlg) ; do we page screen? or a ret nz ; (no, skip this) ld hl,LinCnt ; screen filled? dec (hl) ret nz ; (no, continue) call eprint db '[more]',0 call cin ; wait for key cp CtrlC ; abort? jp z,Abort1 ; (yes) call eprint db CR,' ',CR,0 ld a,(ScrLns) ; reset line count ld (LinCnt),a ret ; ..and continue ; ; PrtEss -- checks if HL is exactly 1. If not, prints an "s". ; Uses: AF, HL ; PrtEss: dec hl ; 1 becomes 0 ld a,h or l ld a,'s' call nz,cout ; (if it's not 1, print "s") ret ; ; Usage -- displays brief usage summary, checking configuration flags ; for defaults. ; Usage: call eprint DftNam: db 'FL Version ' db Vers/10+'0','.',Vers mod 10+'0',SubVers,CR,LF db 'Usage:',CR,LF db ' ',0 ld hl,comnam ; print invocation name call epstr call eprint db ' {{dir:}{afn} {...}} {~afn} {/options}',CR,LF db 'Matches files that don''t match negative (~) filespec.',CR,LF db 'DIR specs after the first are ignored.',CR,LF db 'Options:',CR,LF db ' O ',0 ld a,(FOFlg) or a jr z,Usage2 call eprint db 'omit ',0 Usage2: call eprint db 'output to file FILELIST',CR,LF db ' D ',0 ld a,(FLDu) or a call nz,PrtDnt call eprint db 'include DU''s in FILELIST',CR,LF db ' S ',0 ld a,(SysFlg) or a call nz,PrtDnt call eprint db 'include system files',CR,LF db ' P ',0 ld a,(PagFlg) or a call z,PrtDnt call eprint db 'page display',CR,LF db ' Q quiet mode'0 ld a,(QtFlag) or a jp z,Exit call eprint db ' off',0 jp Exit ; ..and quit ; PrtDnt: call eprint db 'don''t ',0 ret ; ; Uninitialized data . . . ; DSEG ; NegFcb: ds 36 ; filename not to match ScrCol: ds 1 ; number of display columns (5 or 8) ScrLns: ds 1 ; number of screen lines MatCnt: ds 2 ; matching files counter NegFlg: ds 1 ; FFh=negative filename active OpOFlg: ds 1 ; FFh=display if in both DU's OpPFlg: ds 1 ; FFh=no screen paging OpQFlg: ds 1 ; non=zero=quiet mode OpDFlg: ds 1 ; FFh=include DU in output file OpSFlg: ds 1 ; FFh=include system files DftDU: ds 2 ; default user and drive numbers DirDU: ds 2 ; directory user and drive numbers DirBuf: ds 2 ; directory buffer address DirCnt: ds 2 ; directory file list counter DirPtr: ds 2 ; directory file list pointer ColCnt: ds 1 ; files per line counter LinCnt: ds 1 ; screen lines counter CLPtr: ds 2 ; current command line pointer CLBuf: ds 128 ; command line storage ds 100 ; stack Stack: ds 2 ; stack pointer storage ; end