; CPD.Z80 ; ; A ZCPR3 utility to compare two directories. ; Vers equ 10 SubVers equ ' ' ; ; USAGE: ; ; CPD {dir:}{afn} {dir:} {{/}options} ; ; If no DU or DIR is given, the current default is assumed. If no filename ; is given, all files ("*.*") is assumed. If no option is given, all ; matching files in the first directory are displayed with files marked ; which also exist on the second directory. ; ; OPTIONS: A slash is not required if the option list is the third ; parameter on the command line. ; ; B Display only files that exist in both directories. This ; option cancels option M. ; ; M Display only files in the first directory that are missing ; from the second directory. This option cancels option B. ; ; A Set the archive attribute of files in the first directory ; that also exist in the second directory. This allows ; copying the files with a copy program that recognizes ; archived files. CPD does not remove archive attributes, ; so they should be reset with a program such as DA or FA ; before using this mode of CPD. ; ; S Include system (hidden) files. Normally CPD ignores ; system files. ; ; P Do not page screen display. Normally CPD waits for a ; key to be pressed after each screenful of filenames. ; ; L Echo display to the printer. This option also suppresses ; screen paging. ; ; F Send a final form feed to the printer, if option L is ; also active. ; ; HISTORY: ; ; Version 1.0 -- August 10, 1991 -- Gene Pizzetta ; The idea (but not the code) came from CDIR 2.0 by Robert Wilcox ; and Richard Brewster. This program could be a lot faster if ; both directories were loaded into memory first, but it does the ; job. Next time . . . ; ; 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 ; Bdos equ 0005h ; BDOS entry CpmFcb equ 005Ch ; default file control block AltFcb equ 006Ch ; alternate file control block CpmDma equ 0080h ; default DMA buffer SetAtt equ 30 ; set file attributes (BDOS function) ; ; ASCII ; CtrlC equ 03h ; ^C LF equ 0Ah ; line feed FF equ 0Ch ; form feed CR equ 0Dh ; carriage return CtrlS equ 13h ; ^S ; ScCols: equ 5 ; number of filenames per line ; .request zslib,vlib,z3lib,syslib ; extrn eatspc,eatnspc,gcomnam,comnam ; ZSLIB 3.0 extrn z3vinit,stndout,stndend ; VLIB extrn zsyschk,dundr,getcrt ; Z3LIB extrn sctlfl,sfn1,sprint,spstr,sout,scrlf,lcrlf ; SYSLIB extrn lout,condin,cin,safdc,shldc,f$exist,dirq extrn retud,logud,codend ; jp Start db 'Z3ENV',1 Z3EAdr: dw 0 ; ; Configuration . . . (Bytes must be either 0 or FFh) ; dw 0 ; filler db 'CPD' ; for ZCNFG db Vers/10+'0',Vers mod 10+'0',' ' SysFlg: db 0 ; FFh=include system files BthFlg: db 0 ; FFh=only files in both DU's MisFlg: db 0 ; FFh=only files missing from second DU ArcFlg: db 0 ; FFh=set archive attribute if in both DU's PagFlg: db 0 ; FFh=no screen paging FFFlag: db 0 ; FFh=send final form feed if L option ; ; 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 z3vinit ; initialize environment ld hl,DftNam ; point to default program name call gcomnam ; get real program name ; call codend ; get start of buffer ld (Buffer),hl ld hl,CpmDma+1 ; point to command tail call eatspc ; anything there? jp z,Usage ; (nope) cp '/' ; help request? jr nz,Start1 ; (nope) inc hl ; a second slash? cp (hl) jp z,Usage ; (yep, help request) ; Start1: call GetOpt ; check for options ld a,(CpmFcb+1) ; see if file spec given cp ' ' ; a filename? jp nz,Start2 ; (yes) ld hl,CpmFcb+1 ; no, make it all "?"'s ld a,'?' ld b,11 AmbLp: ld (hl),a inc hl djnz AmbLp ; Start2: ld a,(CpmFcb+15) ; check for valid directory or a jp nz,InvDir ; (nope) ld a,(CpmFcb) ; 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,(CpmFcb+13) ; get user ld c,a ; put user in C ld (InUsr),bc ; store user and drive ld d,b ; save in DE ld e,c ; ld a,(AltFcb+15) ; check alternate for valid directory or a jp nz,InvDir ; (nope) ld a,(AltFcb) ; 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,(AltFcb+13) ; get user ld c,a ; put it in C ld (CmpUsr),bc ; store user and drive ld a,d ; make sure both DU's aren't the same cp b jr nz,Start3 ld a,e cp c jr nz,Start3 call sprint db 'Duplicate directories.',0 jp Exit ; Start3: call PrtHdr ; print header ld bc,(InUsr) ; log into main directory call logud xor a ld de,CpmFcb ld (de),a ld hl,(Buffer) ; point to free buffer space ld a,(OpSFlg) ; non-system and maybe system files call dirq jp z,0 ld (FilPtr),hl ; store file list pointer ld (FilCnt),bc ld a,b ; any matching files? or c jp z,Finish ; (nope) ld a,ScCols ld (ColCnt),a ld bc,(CmpUsr) ; log into compared directory call logud ; MLoop: call condin ; check for key call nz,Abort ; (key pressed, check for ^C) ld hl,(FilPtr) ; HL = ptr to next name ld de,CpmFcb ; DE = destination address ld bc,16 ; # bytes to move ldir ld (FilPtr),hl ; update file list pointer xor a ld de,CpmFcb ; search for file ld (de),a call f$exist ld de,CpmFcb+1 ; point to filename jr nz,BLoop ; MLoop1: ld a,(OpBFlg) or a jp nz,SkpFil call sfn1 ; print filename ld a,' ' call sout ; print a space ld hl,(MatCnt) inc hl ld (MatCnt),hl ; MLoop2: call ChkCnt ; check file counter jp z,Finish ; (none left) ld hl,ColCnt dec (hl) ; is line full? jp z,MLoop4 ; (yes) call sprint db '| ',0 jp MLoop ; ..and get next filename ; MLoop4: call scrlf ; start a new line ld a,ScCols ; reset column count ld (ColCnt),a ld a,(OpPFlg) ; do we page screen? or a jp nz,MLoop ; (no, skip this) ld hl,LinCnt ; screen filled? dec (hl) jp nz,MLoop ; (no, continue) call sprint db '[more]',0 call cin ; wait for key cp CtrlC ; abort? jp z,Abort1 ; (yes) call sprint db CR,' ',CR,0 ld a,(ScrLns) ; reset line count ld (LinCnt),a jp MLoop ; ..and continue ; BLoop: ld a,(OpMFlg) or a jp nz,SkpFil call stndout ; match, use standout call sfn1 ; print filename call stndend ; end standout ld a,'*' ; print asterisk call sout ld hl,(MatCnt) ; increment counter inc hl ld (MatCnt),hl ld a,(OpAFlg) ; set archive attribute? or a jr z,MLoop2 ; (no) ld bc,(InUsr) ; log into first directory call logud dec de ; DE points to file control block ld hl,11 add hl,de ; HL points to T3 set 7,(hl) ld c,SetAtt call Bdos ld bc,(CmpUsr) ; log back to second directory call logud jp MLoop2 ; SkpFil: call ChkCnt ; check file counter jp z,Finish ; (none left) jp MLoop ; Finish: call scrlf ld hl,(MatCnt) ; number of matching files found call shldc call sprint db ' matching files found on ',0 ld bc,(InUsr) ; print DU call PrtDU ; Exit: ld a,(sctlfl) ; printer output? and 80h jr z,Exit1 call lcrlf ld a,(OpFFlg) or a ld a,FF call nz,lout Exit1: 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) call scrlf Abort1: call sprint db CR,'Aborted',0 jr Exit ; Pause: call cin ret ; InvDir: call sprint db 'Invalid directory.',0 jr Exit ; InvOpt: call sprint db 'Invalid option.',0 jr Exit ; ; ChkCnt -- decrements file list count. Returns zero if no more. ; ChkCnt: ld hl,(FilCnt) ; decrement file list count dec hl ld (FilCnt),hl ld a,l or h ret ; ; GetOpt -- initialize defaults and get command line options ; GetOpt: ld hl,0 ; zero matching files counter ld (MatCnt),hl ld hl,BthFlg ; initialize options from defaults ld de,OpBFlg ld bc,5 ldir ld a,(SysFlg) or a ld a,10000000b jr z,GetOp1 or 01000000b GetOp1: ld (OpSFlg),a ld a,00000001b ld (sctlfl),a call getcrt ; get screen lines inc hl ld a,(hl) 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 call eatspc ; skip first token call eatnspc ret z ; end of tail call eatspc ret z cp '/' ; options? jr z,OptLp ; (yes, get them) call eatnspc ; skip second token call eatspc ret z cp '/' ; slash? jr nz,OptLp1 ; (no, get options) OptLp: inc hl ; point to next OptLp1: ld a,(hl) ; get option or a ret z cp 'S' ; system files jr z,OptS cp 'B' ; both directories jr z,OptB cp 'M' ; missing from second jr z,OptM cp 'A' ; set archive bit jr z,OptA cp 'P' ; screen paging jr z,OptP cp 'L' ; printer output jr z,OptL cp 'F' ; form feed jr z,OptF 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 jp OptLp ; OptB: ld a,(OpBFlg) ; get flag OptB1: cpl ; ..flip it ld (OpBFlg),a ; ..and store it OptB2: or a ; if we're setting it jr nz,OptM1 ; ..reset option M jp OptLp ; OptM: ld a,(OpMFlg) ; get flag OptM1: cpl ; ..flip it ld (OpMFlg),a ; ..and store it or a ; if we're setting it jr nz,OptB1 ; ..reset option B jp OptLp ; OptA: ld a,(OpAFlg) ; get flag cpl ; ..flip it ld (OpAFlg),a ; ..and store it jr OptB2 ; if we're setting it, reset option M ; OptP: ld a,(OpPFlg) ; get flag cpl ; ..flip it ld (OpPFlg),a ; ..and store it jp OptLp ; OptL: ld a,81h ; set switch control flag for LST echo ld (sctlfl),a ld (OpLFlg),a xor a ; ..and reset paging flag ld (OpPFlg),a jp OptLp ; OptF: ld a,(OpFFlg) ; get flag cpl ; ..flip it ld (OpFFlg),a ; ..and store it jp OptLp ; ; PrtHdr -- prints directory header ; PrtHdr: call sprint db 'Directory of ',0 ld bc,(InUsr) call PrtDU ; first DU ld a,(OpBFlg) or a ld hl,MsgHd3 ; option B header jr nz,PrtHd1 ld a,(OpMFlg) or a ld hl,MsgHd2 ; option M header jr nz,PrtHd1 ld hl,MsgHd1 ; default header PrtHd1: call spstr ld bc,(CmpUsr) call PrtDU ; second DU call scrlf ret ; MsgHd1: db ' -- Marked files also on ',0 MsgHd2: db ' -- Only files not on ',0 MsgHd3: db ' -- Only files also on ',0 ; ; 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 sout ; print drive ld a,c call safdc ; print user ld a,':' call sout 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 sout inc hl djnz PNdrLp ret ; ; Usage -- displays brief usage summary, checking configuration flags ; for defaults. ; Usage: call sprint DftNam: db 'CPD 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 spstr call sprint db ' {dir:}{afn} {dir:} {{/}options}',CR,LF db 'Displays files in first directory, marking files',CR,LF db 'that are duplicated in second directory.',CR,LF db 'Options:',CR,LF db ' B ',0 ld a,(BthFlg) or a call nz,PrtDnt call sprint db 'display only files in both directories',CR,LF db ' M ',0 ld a,(MisFlg) or a call nz,PrtDnt call sprint db 'display only files missing from second directory',CR,LF db ' A ',0 ld a,(ArcFlg) or a call nz,PrtDnt call sprint db 'set archive attribute if in both directories',CR,LF db ' S ',0 ld a,(SysFlg) or a call nz,PrtDnt call sprint db 'include system files',CR,LF db ' P ',0 ld a,(PagFlg) or a call z,PrtDnt call sprint db 'page display',CR,LF db ' L echo to printer',CR,LF db ' F ',0 ld a,(FFFlag) or a call nz,PrtDnt call sprint db 'send final form feed',CR,LF db 'Options B and M are mutually exclusive.',0 jp Exit ; ..and quit ; PrtDnt: call sprint db 'don''t ',0 ret ; DSEG ; ScrLns: ds 1 ; number of screen lines OpSFlg: ds 1 ; FFh=include system files OpBFlg: ds 1 ; FFh=display if in both DU's OpMFlg: ds 1 ; FFh=display if not in second DU OpAFlg: ds 1 ; FFh=set archive bit if in both DU's OpPFlg: ds 1 ; FFh=no screen paging OpFFlg: ds 1 ; FFh=final form feed to printer OpLFlg: ds 1 ; non-zero=echo to printer InUsr: ds 1 ; first user InDrv: ds 1 ; first drive CmpUsr: ds 1 ; second user CmpDrv: ds 1 ; second drive Buffer: ds 2 ; buffer address MatCnt: ds 2 ; matching files counter FilCnt: ds 2 ; file list counter FilPtr: ds 2 ; file list pointer ColCnt: ds 1 ; files per line counter LinCnt: ds 1 ; screen lines counter ds 64 ; stack Stack: ds 2 ; stack pointer storage ; end