; ERASE.Z80 ; ; ERASE erases one or more files specified on the command line. It ; supports an Inspect mode that allows the user to confirm each ; deletion before it is done. ; Vers equ 56 SubVers equ ' ' ; ; USAGE: ; ; ERASE {dir:}afn1{,{dir:}afn2{,...}} {{/}options} ; ; A DU or DIR specification is optional; if one is not given, the current ; directory is assumed. ; ; OPTIONS: ; ; I Inspect and approve each erasure before it is done. ; ; S Include system files. Normally only directory files ; are found. ; ; R Erase read-only files without prompting. Without this ; option, ERASE prompts "Read-only, Erase?". ; ; A Erase all files (*.*) without prompting. If an all- ; files specification is given without this option, ; ERASE prompts "Erase all files?". ; ; Q Toggle current default quiet mode. Defaults to quiet ; flag, if it is on. Otherwise, defaults to configured ; quiet mode. ; ; HISTORY: ; ; Version 5.6 -- November 4, 1990 -- Gene Pizzetta ; Only 24 hours after releasing 5.5 I discovered the usage screen ; was not reflecting the current effect of the S option. It does ; now. ; ; Version 5.5 -- November 3, 1990 -- Gene Pizzetta ; Added Q option to toggle default quiet mode, which is controlled ; by the ZCPR3 quiet flag or a new quiet configuration byte. In ; fact, all command line options, except I, can now be configured ; as the default mode with ZCNFG. Now displays DU specification ; with each filename, so you can be sure which directory ERASE is ; working on. If run under ZCPR33+, now reports invalid directory ; instead of trying to erase files on default drive. What a ; disaster that could be! ; ; Version 5.4 -- July 1, 1990 -- Gene Pizzetta ; Eliminated internal environment assembly option. Added ; configuration byte after ASCII string 'SYSFILES>' to chose ; whether to include system files by default (non-zero) or ; exclude them by default (0). S option now toggles the ; default meaning (it's current action is shown by the usage ; screen. Now filters high bit of filenames for terminals that ; display special characters that way. At the suggestion of ; Howard Goldstein, ERASE no longer signs on with its load ; address and version number; these are displayed only on the ; usage screen. Restored Q (quit) as a valid response to ; an erase query, but ^C still works. Display has been made ; even more compact than last version. Many of these changes ; are to make the ERASE interface very similar to the RCP ; version. Program error flag is now set to 10 if no files ; are erased, unless the program is aborted with a ^C or with ; a "Q" response to a prompt. If an invalid option is given ; the program error code is set to 19 and error handler is ; invoked. Added disk free space display. Now obeys ZCPR3 ; quiet flag, except when in inspect mode or when a prompt is ; necessary (when a read/only file is found, for instance). ; Removed Wheel byte protection for S and R options. ; ; Version 5.3 -- March 25, 1990 -- Gene Pizzetta ; Added "A" option to suppress "Erase all files?" question ; when "*.*" is given as a filespec. This option is primarily ; for use in alias scripts and batch files, where you might ; want to clear a directory without ERASE stopping to ask if ; you know what you're doing. Rick Charnes added some nice ; features in v5.2, but succeeded in badly messing up the display ; for all prompted erasures. That has been corrected and the ; Q (quit) option has been eliminated. Instead, ^C now aborts ; ERASE at any prompt. ; ; Version 5.2 -- January 24, 1989 -- Rick Charnes ; Three changes: ; (1) Now turns off ZEX input while program is running. ; This is important when ERASE.COM (a) is used in an alias inside ; a ZEX file under certain circumstances and (b) ERASE.COM requires ; user input, such as if its I)nspect option is used or if R/O files ; are encountered. Otherwise the ZEX script itself would be sent ; willy-nilly into the prompt rather than allowing the user to do so. ; (2) The ZCPR3 error flag is set if program did not erase ; any files. This can be useful in aliases when the correct ; execution of the remainder of an alias is dependent upon a ; successful erasure. The error flag is cleared if any files ; were successfully erased. ; (3) ZCPR3 register "0" is set to the number of files ; successfully erased. This can also be nice for aliases. I always ; liked this concept, for which my inspiration was Jay Sage's doing ; the same thing with FF.COM, which sets ZCPR3 register 0 to the ; number of files successfully found. ; ; Version 5.1A -- December 30, 1987 -- Bruce Morgen ; Fix option parser as per RENAME32, strip high bit ; when printing program's actual name for help screen, wheel byte ; checked before granting "S" or "R" option or help text on those ; options. ; ; Version 5.1 -- December 21, 1987 -- Bruce Morgen ; Z80 opcodes, Zilog mnemonics and ZCPR33 Type 3 format. ; Usage message gives correct COMfile name of program. Following ; Howard Goldstein's (LX17 & 18) lead, the ZCPR 3.3 parser is used ; if it's available. ; ; Version 5.0 -- May 18, 1984 -- Richard Conn ; Version 4.0 -- January 16, 1983 -- Richard Conn ; Version 3.3 -- January 6, 1983 -- Richard Conn ; Version 3.2 -- December 7, 1982 -- Richard Conn ; Version 3.1 -- November 9, 1982 -- Richard Conn ; Version 3.0 -- October 18, 1982 -- Richard Conn ; Version 2.0 -- November 18, 1981 -- Richard Conn ; Version 1.4 -- October 26, 1981 -- Richard Conn ; Version 1.3 -- October 25, 1981 -- Richard Conn ; Version 1.2 -- April 12, 1981 -- Richard Conn ; Version 1.1 -- October 19, 1980 -- Richard Con ; Version 1.0 -- June 14, 1980 -- Richard Conn ; .request z3lib,syslib ; ext z3init,z33chk,z33fname,zfname,z3log,stopzex,putreg ext puter2,prtname,inverror,dparams,dfree,getquiet ext dirq,cin,caps,cout,eprint,epstr,crlf,retud,getud ext putud,sfa,initfcb,f$delete,phlfdc,phl4hc,codend ; ; Equates ; Bdos equ 5 ; BDOS entry SetAtt equ 30 ; BDOS set attributes function CpmFcb equ 5Ch ; file control block CpmDma equ 80h ; input line buffer ESize equ 16 ; size of dir entry (from SYSLIB DIRQ routine) Z3Env defl 0FE00h ; environment address CtrlC equ 03h ; ^C BEL equ 07h ; bell BS equ 08h ; backspace TAB equ 09h ; tab CR equ 0Dh ; carriage return LF equ 0Ah ; linefeed ; ; Type 3 Header -- Code modified as suggested by Charles Irvine to function ; correctly with interrupts enabled. Program will abort with an error message ; when not loaded to the correct address (attempt to run it under CP/M or Z30). ; Entry: jr Start0 ; must use relative jump nop ; filler db 'Z3ENV',3 ; type-3 environment Z3EAdr: dw Z3Env ; filled in by Z33 dw Entry ; intended load address ; ; Configuration area . . . ; db 'ERASE' ; for ZCNFG db Vers/10+'0',Vers mod 10+'0',' ' QtFlag: db 0 ; FFh=default to quiet mode SysFlg: db 80h ; 80h=exclude system files, C0h=include ROFlag: db 0 ; FFh=erase R/O files without prompting AskFlg: db 0 ; FFh=erase *.* without prompting ; Start0: ld hl,0 ; point to warmboot entry ld a,(hl) ; save the byte there di ; protect against interrupts ld (hl),0C9h ; replace warmboot with a return opcode rst 0 ; call address 0, pushing RETADDR ; onto stack RetAddr: ld (hl),a ; restore byte at 0 dec sp ; get stack pointer to point dec sp ; to the value of RETADDR pop hl ; get it into HL and restore stack ei ; we can allow interrupts again ld de,RetAddr ; this is where we should be xor a ; clear carry flag push hl ; save address again sbc hl,de ; subtract -- we should have 0 now pop hl ; restore value of RETADDR jr z,Start ; if addresses matched, begin real code ; ld de,NotZ33Msg-RetAddr ; offset to message add hl,de ex de,hl ; switch pointer to message into DE ld c,9 jp 5 ; return via BDOS print string function NotZ33Msg: db 'Not Z33+$' ; abort message if not Z33-compatible ; ; Start of program . . . ; Start: ld hl,(Z3EAdr) ; point to ZCPR3 environment call z3init ; initialize the ZCPR3 env and the VLIB env ld (Stack),sp ; get stack pointer & save it ; ; Compute buffer locations ; call codend ; determine free space ld (CmdLne),hl ; set pointer to command line ld de,100h ; buffer size add hl,de ; command line ld (EraFcb),hl ; FCB for erase add hl,de ld (DirBuf),hl ; set pointer to directory buffer ld sp,hl ; set stack pointer ; ; Save our location and stop ZEX input ; call putud call stopzex ; Initialize option flags ld hl,0 ; zero file count ld (FilCnt),hl ld de,OpIFlg ; point to inspect flag xor a ; no inspect mode ld (de),a inc de ; point to quiet flag call getquiet ; is quiet flag set? rra ; make it FFh or 0 sbc a,a jr nz,SetQt ; (yes) ld a,(QtFlag) ; get configuration byte SetQt: ld (de),a ; ..and store it inc de ; point to remaining flags ld hl,SysFlg ; ..and move defaults ld bc,3 ldir ; ; Check for help request ; ld a,(CpmFcb+1) ; get first char of filename cp ' ' ; no file spec? jr z,Usage cp '/' ; option caught? jp nz,ECont ld a,(CpmFcb+2) cp '/' ; help? jp nz,ECont ; (no) ; ; Print usage information ; Usage: call eprint db 'ERASE Version ' db Vers/10+'0','.',Vers MOD 10+'0',SubVers db ' (loaded at ',0 ld hl,Entry call phl4hc call eprint db 'h)',CR,LF db 'Usage:',CR,LF,' ',0 call prtname call eprint db ' {dir:}afn.aft{,...} {{/}options}',CR,LF db 'Options:',CR,LF db ' I Inspect mode (confirm each erasure).',CR,LF db ' A ',0 ld a,(AskFlg) or a call nz,PrtNot call eprint db 'Erase all (*.*)',0 call PrtFWA call eprint db ' R ',0 ld a,(ROFlag) or a call nz,PrtNot call eprint db 'Erase read/only'0 call PrtFWA call eprint db ' S ',0 ld a,(SysFlg) bit 6,a jr nz,Usage4 call eprint db 'In',0 jr Usage5 Usage4: call eprint db 'Ex',0 Usage5: call eprint db 'clude system files.',CR,LF db ' Q Toggle quiet mode o',0 ld a,(OpQFlg) or a jr nz,Usage6 call eprint db 'n.',0 jr Exit Usage6: call eprint db 'ff.',0 ; ; Return to operating system ; Exit: xor a ErExit: call puter2 ; set program error flag ld b,a ; move error code to B cp 19 ; invalid option error? jr z,ErExt1 cp 2 ErExt1: call z,inverror ; (yes, invoke error handler) ld sp,(Stack) ; get old stack & set it ret ; PrtNot: call eprint db 'Don''t ',0 ret PrtFWA: call eprint db ' files without asking.',CR,LF,0 ret ; ; Copy buffer into temp buffer ; ECont: ld de,(CmdLne) ; point to command line ld hl,CpmDma+1 ; point to buffer ld bc,80h ; buffer size (max) ldir ; copy into command line buffer ld hl,(CmdLne) ; point to buffer call EatSpc ; skip over spaces or a ; null? jp z,DSpec ; (done) SkpFn: inc hl ; skip over file list ld a,(hl) or a ; null? jr z,DSpec ; (yes, done) cp ' ' ; space? jr z,SkpFn1 cp TAB ; tab? jr z,SkpFn1 jr SkpFn ; keep skipping SkpFn1: call EatSpc ; skip following white space or a jr z,DSpec ; (done) cp '/' ; slash? jr nz,GetOpt ; (no, get options) inc hl ; get past slash ; ; Process list of options ; GetOpt: ld a,(hl) ; get byte or a ; done? jp z,DSpec inc hl ; point to next char cp ' ' ; skip over spaces jr z,GetOpt cp '/' ; if option letter, obvious error, so help jp z,Usage cp 'I' ; inspect? jr z,OptI cp 'R' ; read/only? jr z,OptR cp 'Q' ; quiet? jr z,OptQ cp 'A' ; don't ask? jr z,OptA cp 'S' ; system files? jp nz,InvOpt ; ; Set system file selection ; ld a,(OpSFlg) ; check default xor 40h ; 80h or C0h ld (OpSFlg),a jr GetOpt ; ; We have an invalid option ; InvOpt: call eprint db ' Invalid option.',0 ld a,19 jp ErExit ; ; Set inspect option ; OptI: ld a,0FFh ; inspect ld (OpIFlg),a jr GetOpt ; ; Set don't ask option ; OptA: ld a,(OpAFlg) ; don't ask cpl ld (OpAFlg),a jr GetOpt ; ; Set r/o option ; OptR: ld a,(OpRFlg) ; set r/o cpl ld (OpRFlg),a jr GetOpt ; ; Toggle quiet option ; OptQ: ld a,(OpQFlg) ; toggle whatever it is cpl ld (OpQFlg),a jr GetOpt ; ; Extract disk, user, and filename information ; DSpec: ld hl,(CmdLne) ; point to first byte of command tail call EatSpc ; skip over spaces or a ; done? jp z,Usage ; (yep, we had nothing) jr DSpec2 ; ; Major re-entry point when file specs are separated by commas. ; HL points to first byte of next file spec ; DSpec1: ld a,(OpQFlg) or a jr nz,DSpec2 call crlf DSpec2: call getud ; return home ld de,CpmFcb ; DE=CpmFcb adr, HL=first filename char adr call ParsIt ; move filename into CpmFcb, get disk and user ld a,(CpmFcb+15) ; check for valid directory or a jr z,DSpec3 call eprint db ' Invalid directory.',0 ld a,2 jp ErExit ; DSpec3: ld (NxtChr),hl ; save pointer to delimiter which ended scan ld hl,CpmFcb+1 ; see if filename is all wild ld b,11 ; 11 bytes WTest: ld a,(hl) ; get byte inc hl ; point to next cp '?' ; wild? jr nz,NoWild djnz WTest ; count down ld a,(OpIFlg) ; inspect? or a ; 0=no jr nz,NoWild ld a,(OpAFlg) ; check don't ask flag or a ; 0=yes, ask jr nz,NoWild call eprint db 'Erase all files on ',0 ld a,(CpmFcb) or a jr nz,PrtDU call retud ld a,b inc a PrtDU: add '@' call cout ld a,(CpmFcb+13) ld l,a ld h,0 call phlfdc ld hl,MsgAll call AskIt jp nz,CkDone ; NoWild: call z,crlf ld de,CpmFcb ; point to CpmFcb call z3log ; log into directory ; ; Load directory and erase files ; ld hl,(DirBuf) ; point to dir buffer ld a,(OpSFlg) ; get sys/non-sys flags ld de,CpmFcb ; point to CpmFcb call dirq ; load dir, select files, pack, and alphabetize ; ; Erase files. HL points to first file, BC=file count ; call EraseF ; ; Check for next file spec ; CkDone: ld hl,(NxtChr) ; get pointer ld a,(hl) ; get delim inc hl cp ',' ; another file? jp z,DSpec1 ; continue processing ; ; Erase complete: print count and exit ; EraDon: call PrtCnt ; print file count ld hl,(FilCnt) ; get count ld a,l ; check for none or h jp nz,Exit ld a,10 jp ErExit ; ; Erase selected files ; EraseF: ld a,b ; check for any files loaded or c ret z jr EraF1 ; ; Print filename ; EraFLp: ld a,(OpIFlg) or a jr nz,EraF0 ld a,(OpQFlg) or a jr nz,EraF1 EraF0: call crlf EraF1: push bc ; save entry count ld a,(OpIFlg) ; inspect mode? or a jr nz,SkipQt ; (yes, skip quiet mode) ld a,(OpQFlg) or a jr nz,NoFNam SkipQt: call PrFn ; print filename (saves pointer) ; ; Check for inspection and inspect if set ; NoFNam: ld a,(OpIFlg) ; get flag or a ; 0=no jp z,EraIt ; ; Prompt user for erase ; push hl ld hl,MsgEra call AskIt pop hl jp z,EraIt ; ; Don't erase file ; EraNo: jp EraTst ; ; AskIt -- prompts user. Pointer to prompt string in HL. ; AskIt: call epstr call cin ; get response call caps cp 'Y' jr z,AskIt2 cp 'Q' ; quit? jp z,Quit cp CtrlC ret nz ld a,'Q' Quit: or a AskIt2: call cout ret z jp EraDon ; MsgEra: db ' Erase (Y/N/Q)? N',BS,0 MsgRO: db BEL,' Read-Only, Erase? N',BS,0 MsgAll: db ': ? N',BS,0 ; ; Print filename ; PrFn: push hl ld a,' ' call cout call retud ld a,b add a,'A' call cout ex de,hl ; save pointer in DE ld l,c ld h,0 call phlfdc ex de,hl ld a,':' call cout inc hl ; point to filename ld b,8 ; print name call PrtIt ld a,'.' ; decimal call cout ld b,3 ; print type call PrtIt pop hl ret ; ; Erase file ; EraIt: push hl ld de,9 ; point to r/o attribute add hl,de ld a,(hl) ; get r/o attribute pop hl ; restore pointer and 80h ; r/o? jr z,EraIt1 ; r/w, proceed ld a,(OpRFlg) ; get r/o erase flag or a ; 0=query jr nz,EraIt0 ; erase without question if flag set ld a,(OpQFlg) ; check quiet flag or a jr z,SkipFn ; (filename already printed) call crlf call PrFn ; PrFn saves pointer in HL SkipFn: push hl ; notify user and prompt ld hl,MsgRO call AskIt pop hl jp nz,EraNo ; do not erase if not yes ; ; Erase R/O file ; EraIt0: push hl ; save pointer to file entry ld de,9 ; point to r/o attribute add hl,de ld a,(hl) ; get attribute and 7Fh ; make r/w ld (hl),a pop hl ; get pointer to CpmFcb push hl ; save pointer again ex de,hl ; DE points to CpmFcb xor a ; make sure current disk is selected ld (de),a ld c,SetAtt ; set file attributes call Bdos pop hl ; ; Erase R/W file ; EraIt1: push hl ; save pointer to filename to erase inc hl ; point to first byte of name push hl ; save HL ld hl,(EraFcb) ; set up FCB ex de,hl ; ..in DE pop hl ; get HL push de ; save pointer xor a ; A=0 ld (de),a ; current disk inc de ; point to first char ld bc,11 ; copy 11 bytes ldir ; copy HL to DE for 11 bytes pop de ; get pointer call initfcb call f$delete ld hl,(FilCnt) ; increment count inc hl ld (FilCnt),hl pop hl ; get pointer to directory entry ; ; Point to next entry ; EraTst: ld de,ESize ; point to next entry add hl,de pop bc ; get count dec bc ; count down ld a,b ; check for zero or c jp nz,EraFLp ret ; ; EatSpc -- gobbles up spaces and tabs ; EatSpc: ld a,(hl) or a ret z inc hl cp ' ' jr z,EatSpc cp TAB jr z,EatSpc dec hl ret ; ; Print chars pointed to by HL for B bytes ; PrtIt: ld a,(hl) ; get character and 07Fh call cout inc hl ; point to next djnz PrtIt ; count down ret ; ; Print count of number of files erased ; PrtCnt: ld hl,(FilCnt) ; get count ld b,0 ; we'll be setting ZCPR3 register "0" ld a,l ; check for none (and prepare for PUTREG) call putreg ld a,(OpQFlg) ; (even for inspect mode) or a ret nz call crlf ld a,h or l jr nz,PrtNbr ; call eprint db 'No',0 ; PrtNbr: call nz,phlfdc call eprint db ' File',0 ld a,h or a jr nz,PrtS ld a,l ; low one? cp 1 PrtS: ld a,'s' ; add 's' if more than one erased call nz,cout call eprint db ' Erased, ',0 call dparams ; prints free space on last drive used call dfree ex de,hl call phlfdc call eprint db 'k Free',0 ret ; ; Parse filename ; ParsIt: call z33chk jp z,z33fname xor a jp zfname ; DSEG ; ; Uninitialized data . . . ; OpIFlg: ds 1 ; inspect flag (0=no, 0FFh=yes) OpQFlg: ds 1 ; quiet flag (non-zero=quiet) OpSFlg: ds 1 ; system flag (80h=exclude, 0C0h=include) OpRFlg: ds 1 ; read/only flag (0=query for r/o, 0FFh=don't) OpAFlg: ds 1 ; don't ask flag (0=query *.*, 0FFh=don't) NxtChr: ds 2 ; pointer to next char in multifile command line FilCnt: ds 2 ; count of number of files erased EraFcb: ds 2 ; pointer to FCB for erase CmdLne: ds 2 ; pointer to command line DirBuf: ds 2 ; pointer to directory buffer Stack: ds 2 ; old stack pointer ; end