title Differential File Compare Utility name ('DFC') maclib base80 ; Differential file compare program ; It bases upon a C program published in MC 11'87 (Franzis) ; Titled 'Differentieller Datei-Vergleich' ; by Harald Halm and Olaf Zurth ; ; CP/M 3.x Z80 assembler version by Werner Cirsovius, 11'94 ; ; Version 3.2 ; ; Call it: ; DFC {-L|S|Mn|B|I|T|W} {d{u}:}file1 {d{u}:}{file2} ; ; If unambiguous, either file1 or file2 may hold wildcards. ; Both filenames must be different or on different drives. ; ; With options: ; -L Select list output (default) ; -S Select editor output ; -Mn Select grade of synchronisation (default is 1) ; -B Ignore trailing blanks and treat all other strings ; of blanks as equivalent ; -I Ignore case of letters ; -T Expand tab characters in output lines ; -W Ignore all blanks in a line ; ; Utility compares text files and outputs result on files which ; are different in the following format: ; ; n1,n2d Delete lines n1 to n2 in file1 ; n1,n2cm1,m2 exChange lines n1 to n2 in file1 against ; lines m1 to m2 in file2 ; n1am1,m2 Append m1 to m2 lines from file2 after line ; n1 in file1 ; ; If n1=n2 or m1=m2, only one number will be printed entry $memry extrn getver,cmdarg,indexa,parse,getdu,wcard extrn sub32,dout32,decin extrn open,opnapp,delete,setdma,creatd,close extrn ftell,fseek,dskred,srcfrs,srcnxt,srfbuf extrn getlnf,usrget,usrset extrn strcn0,prfn,prdufn,crlf,conout,combrk,defio extrn strcmp,@asstr,skpblk,upplin extrn ugteof,uclose,udskpt,wrpblk,rdpblk ;@GERM equ TRUE ; Select German language @GERM equ FALSE ; Select English language _PRG macro db 'DFC' endm _VER macro db '3.2' endm argmax equ 9 argF equ 2 LISTING equ 0 SCRIPT equ 1 NO_DIFF equ 0 MAXLINE equ 256 _long equ 4 ; Length of 'long' integers _b equ 0 ; Status bits _i equ 1 _t equ 2 _w equ 3 ; ; FILESET structure ; _no equ 0 ; Line number _offset equ _no+_long ; File position _eof equ _offset+_long ; End of file marker _line equ _eof+1 ; Last lne read _buffer equ _line+2 ; Line buffer _cp equ _buffer+2 ; Start of chain _last equ _cp+2 ; End of chain _sync equ _last+2 ; Sync line _fp equ _sync+2 ; File pointer FILESET equ _fp+2 ; ; LIST structure ; _lno equ 0 ; Line number _loffs equ _lno+_long ; File position _key equ _loffs+_long ; String key _next equ _key+2 ; Pointer to next LIST equ _next+2 ; ; Save D-E-H-L into IX+OFFSET ; stix macro off ld (ix+off+0),d ld (ix+off+1),e ld (ix+off+2),h ld (ix+off+3),l endm ; ; Save D-E-H-L into IY+OFFSET ; stiy macro off ld (iy+off+0),d ld (iy+off+1),e ld (iy+off+2),h ld (iy+off+3),l endm ; ; Load D-E-H-L from IX+OFFSET ; ldix macro off ld d,(ix+off+0) ld e,(ix+off+1) ld h,(ix+off+2) ld l,(ix+off+3) endm ; ; Save 32 bit from IX to IY ; svxy macro off1,off2 ld a,(ix+off1+0) ld (iy+off2+0),a ld a,(ix+off1+1) ld (iy+off2+1),a ld a,(ix+off1+2) ld (iy+off2+2),a ld a,(ix+off1+3) ld (iy+off2+3),a endm dseg IF @GERM $ILL.CPU: db 'Ben|tigt Z80 CPU',eot $ILL.OS: db 'Ben|tigt CP/M 3.x',eot $HELP: db 'Differentielles Dateivergleichsprogramm ' _PRG db ' v' _VER db cr,lf,lf db tab,'Aufruf: ' _PRG db ' (-L|S|Mn|B|I|T|W) ' db '(d:(u))Datei1 (d:(u))(Datei2)' db cr,lf,lf db 'Falls unzweideutig, d}rfen beide Dateien ' db 'Jokerzeichen enthalten.',cr,lf db 'Die Dateien m}ssen unterschiedlich sein im' db ' Namen oder Laufwerk.',cr,lf,lf db 'Mit den Optionen:',cr,lf db '-L',tab,'Listausgabe w{hlen (Voreinstellung)' db cr,lf db '-S',tab,'Editorausgabe w{hlen',cr,lf db '-Mn',tab,'Synchronisation w{hlen ' db '(Voreinstellung ist 1)',cr,lf db '-B',tab,'F}hrende Leerzeichen ignorieren ' db 'und alle Zeichenketten',cr,lf db tab,'aus Leerzeichen gleich behandeln',cr,lf db '-I',tab,'Gro~/Kleinschreibung ignorieren' db cr,lf db '-T',tab,'Tabulatoren in den Ausgabezeilen ' db 'gegen entsprechende Anzahl',cr,lf db tab,'Leerzeichen austauschen',cr,lf db '-W',tab db 'Leerzeichen in einer Zeile ignorieren' db cr,lf,lf db 'Das Programm vergleicht Textdateien und',cr,lf db 'gibt das Resultat im folgenden Format aus:' db cr,lf,lf db 'n1,n2d',tab,tab,'L|schen (delete) der Zeilen' db ' n1 bis n2 aus Datei1',cr,lf db 'n1,n2cm1,m2',tab,'[ndern (Change) der Zeilen' db ' n1 bis n2 aus Datei1 in',cr,lf db tab,tab,'Zeilen m1 bis m2 aus Dateie2',cr,lf db 'n1am1,m2',tab,'Anh{ngen (Append) der Zeilen' db ' m1 bis m2 aus Datei2',cr,lf db tab,tab,'hinter Zeile n1 aus Datei1',cr,lf,lf db 'Wenn n1=n2 oder m1=m2 gilt, dann wird nur ' db 'eine Zahl ausgegeben',cr,lf,null $ILL.PARSE: db 'Fehler im Namen von ',null $INV.DU: db 'Falsches Laufwerk oder falsche Nutzernummer ' db null $ILL.OPEN: db 'Datei nicht gefunden ',null $ILL.WILD: db 'Joker falsch in Datei ',null $NO.MEMORY: db 'Speicher reicht nicht aus',null $BAD.SEEK: db 'Suchfehler in Datei',null $NOT.DIFF: db 'Dateien m}~en unterschiedlich sein',null ELSE $ILL.CPU: db 'Requires Z80 CPU',eot $ILL.OS: db 'Requires CP/M 3.x',eot $HELP: db 'Differential file compare program ' _PRG db ' v' _VER db cr,lf,lf db tab,'Usage: ' _PRG db ' {-L|S|Mn|B|I|T|W} ' db '{d:{u}}file1 {d:{u}}{file2}' db cr,lf,lf db 'If unambiguous, either file1 or file2 ' db 'may hold wildcards.',cr,lf db 'Both filenames must be different or on ' db 'different drives.',cr,lf,lf db 'With options:',cr,lf db '-L',tab,'Select list output (default)',cr,lf db '-S',tab,'Select editor output',cr,lf db '-Mn',tab,'Select grade of synchronisation ' db '(default is 1)',cr,lf db '-B',tab,'Ignore trailing blanks and treat ' db 'all other strings',cr,lf db tab,'of blanks as equivalent',cr,lf db '-I',tab,'Ignore case of letters',cr,lf db '-T',tab,'Expand tab characters in output ' db 'lines',cr,lf db '-W',tab,'Ignore all blanks in a line' db cr,lf,lf db 'Utility compares text files and outputs ' db 'result on files which',cr,lf db 'are different in the following format:' db cr,lf,lf db 'n1,n2d',tab,tab,'Delete lines n1 to n2 in ' db 'file1',cr,lf db 'n1,n2cm1,m2',tab,'exChange lines n1 to' db ' n2 in file1 against',cr,lf db tab,tab,'lines m1 to m2 in file2',cr,lf db 'n1am1,m2',tab,'Append m1 to m2 lines ' db 'from file2 after line',cr,lf db tab,tab,'n1 in file1',cr,lf,lf db 'If n1=n2 or m1=m2, only one number ' db 'will be printed',cr,lf,null $ILL.PARSE: db 'Invalid parsing of ',null $INV.DU: db 'Invalid drive or user ',null $ILL.OPEN: db 'Cannot open file ',null $ILL.WILD: db 'Invalid wildcard in file ',null $NO.MEMORY: db 'Not enough memory',null $BAD.SEEK: db 'Can''t seek',null $NOT.DIFF: db 'Files should be different',null ENDIF ;@GERM $AND: db ' + ',null $FCL: db ':',cr,lf,null $DELIM: db '---',cr,lf,null $CHANGE: db '%d,%dc%d,%d',null $APPEND: db '%da%d,%d',null $DELETE: db '%d,%dd',null ChgTab: dw FIB1.cp.no dw FIB1.sync.no dw FIB2.cp.no dw FIB2.sync.no AppTab: dw FIB1.cp.no dw FIB2.cp.no dw FIB2.sync.no DelTab: dw FIB1.cp.no dw FIB1.sync.no F1.arr: dw FIB1.sync.no dw FIB1.cp.no F2.arr: dw FIB2.sync.no dw FIB2.cp.no $MODE: db '? ',null ; < or > cntptr: ds 2 FIB1.sync.no: ds 4 FIB1.cp.no: ds 4 FIB2.sync.no: ds 4 FIB2.cp.no: ds 4 IF @GERM $NO: db 'Kein',null $ONE: db 'Ein',null $FILES: db '(Dateien: ',null ELSE $NO: db 'No',null $ONE: db 'One',null $FILES: db '[Files: ',null ENDIF ;@GERM $NUM.32: db '4294967295',null $RES.1: _PRG db ': ',null IF @GERM $RES.2: db ' Unterschied' $RES.2.e: db null,null ELSE $RES.2: db ' difference' $RES.2.e: db null,null ENDIF ;@GERM $OPT: db 'LSMBITW' OptLen equ $-$OPT OptTab: dw LIST.opt,EDIT.opt,SYNC.opt dw B.opt,I.opt,T.opt,W.opt style: db LISTING matches: dw 1 CnvMode: db 0000b _match: ds 2 one.32: db 0,0,0,1 ; ; File control structures for user area setting ; fp.F1 equ $ ; \ fp.DMA1: ; | dw $-$ ; | db reclng ; File 1 fp.FCB1: ; | dw $-$ ; | fp.USR1: ; | db -1 ; / ; fp.F2 equ $ ; \ fp.DMA2: ; | dw $-$ ; | db reclng ; File 2 fp.FCB2: ; | dw $-$ ; | fp.USR2: ; | db -1 ; / ; fp.RED equ $ ; \ R$DMA: ; | dw $-$ ; | R$BP: ; | db 0 ; | R$FCB: ; Redirection dw $-$ ; | fp.REDUSR: ; | db -1 ; | R$EOF: ; | db FALSE ; / ; _RED_: db ' ' _APP_: db ' ' curusr: ds 1 ARGV: ds 2*argmax ARGC: ds 1 $$FN: db 'Duu:12345678.123',null $PB: ds 2*2 R$PB: ds 2*2 dfcnt: ds 4 FIB1: ds FILESET,0 FIB2: ds FILESET,0 $memry: dw 0 Heap: ds 2 Root: ds 2 fsa: ds 2 fsb: ds 2 cptr: ds 2 F1cnt: ds 4 F2cnt: ds 4 @s.b: ds 2 @s.t: ds 2 res$$: ds 2 cseg ; ; Parse options ; ENTRY Accu holds option count ; GetOpt: ld iy,CnvMode ; Init status pointer ld b,a ; Set count ld c,0 ; Init index ..getopt: ld hl,ARGV ld a,c call indexa ; Fetch pointer push bc call parse.opt ; Parse option pop bc inc c djnz ..getopt ret ; ; Parse option ; ENTRY Reg DE points to option string ; parse.opt: ld a,(de) ; Verify option prefix cp '-' jp nz,Help ; .. invalid inc de parse.more: ld hl,$OPT+OptLen-1 ld bc,OptLen ld a,(de) cpdr ; Find option key jp nz,Help ; .. error inc de ; Skip option push de ld hl,OptTab ld a,c call indexa ; Fetch address ex de,hl pop de ld bc,parse.loop push bc ; Set return vector jp (hl) ; .. execute option parse.loop: ld a,(de) ; Test more in this item or a jr nz,parse.more ret ; ; Option '-L' - select list format ; LIST.opt: ld a,LISTING ld (style),a ; .. set it ret ; ; Option '-E' - select edit format ; EDIT.opt: ld a,SCRIPT ld (style),a ; .. set it ret ; ; Option '-Mn' - select grade of synchronization ; SYNC.opt: ld b,-1 call decin ; Get number jp c,Help ; .. error ld (matches),hl ; Save number ret ; ; Option '-B' - ignore trailing blanks ; B.opt: set _b,(iy) ; Set bit ret ; ; Option '-I' - ignore character case ; I.opt: set _i,(iy) ret ; ; Option '-T' - expand tabs in output line ; T.opt: set _t,(iy) ret ; ; Option '-W' - ignore all blanks ; W.opt: set _w,(iy) ret ; ; Convert FCB to file name ; ENTRY Reg DE points to FCB ; Reg HL points to memory ; CnvFtN: ld a,(defio) ; Save I/O state push af ld a,1000b ; Set memory bit ld (defio),a call prfn ; Store into memory ld (hl),null ; .. close pop af ld (defio),a ; Reset I/O ret ; ; Get unambiguous file name ; ENTRY Reg DE points to FCB ; EXIT Zero set if ambiguous ; UnAmbiguous?: call wcard ; Test wildcard ret nz ; .. nope ld hl,(Heap) ld bc,reclng add hl,bc ld (srfbuf),hl ; Set first search buffer call execDU dw srcfrs ; Find file jr c,Ambiguous ; .. not here push hl ; Save file pointer ld hl,(Heap) ld (srfbuf),hl ; Set next search buffer call srcnxt ; Find next file pop hl jr nc,Ambiguous ; .. any here push de inc de ld bc,.fname+.fext ldir ; Unpack new file name pop de push de ld hl,(Heap) ld ($PB),hl ; .. change string call CnvFtN pop de xor a inc a ret Ambiguous: xor a ; Set error ret ; ; Set user on request ; ENTRY Reg -1 holds user ; Next word following call will be jumped to ; execDU: push hl push af ld hl,curusr dec de ld a,(de) ; Get user cp (hl) ; .. test same ld (hl),a call nz,usrset inc de ; .. fix FCB pop af pop hl ex (sp),hl ld c,(hl) ; Get execution address inc hl ld b,(hl) inc hl ex (sp),hl push bc ; .. set it ret ; .. jump ; ; Open source file ; ENTRY Reg DE points to file name ; Reg IX points to FIB ; Fassign: ex de,hl ld ($PB),hl ; Save name for error call getdu ; Fetch drive and user ld de,$INV.DU jr c,ErrAss ; .. error ld ($PB),hl ; Save new pointer push bc ; Save DU ld l,(ix+3) ld h,(ix+3+1) ld ($PB+2),hl ; Save FCB ld de,$PB call parse ; Parse file ld de,$ILL.PARSE jr c,ErrAss ; .. error ld a,l or h ; Verify real end jr nz,ErrAss ; .. nope ld de,($PB+2) ; Get FCB push de pop iy pop bc ld (iy+0),b ; Set drive ld (iy-1),c ; .. and user ld (ix+5),c call UnAmbiguous? ; Get unambiguous file name ld de,$ILL.WILD ret nz ; .. got it ErrAss: call strcn0 ; Tell error ld de,($PB) ; Get file name call strcn0 ; .. print jp R.D.OS ; .. and stop ; ; Open source file ; ENTRY Reg DE points to FCB ; Fopen: call execDU dw open ; Open file ret nc ; .. got it push de ld de,$ILL.OPEN call strcn0 ; Tell error pop de ; Get FCB call prdufn ; .. print jp R.D.OS ; .. and stop ; ; Tell files involved on entry - In redirection mode only ; GiveFHint: ld de,$FILES ; .. indicate call R.STR GiveHFiles: ld de,(fp.FCB1) call R.PRDUFN ; Print file names ld de,$AND call R.STR ld de,(fp.FCB2) call R.PRDUFN IF @GERM ld a,')' ELSE ld a,']' ; .. close ENDIF ;@GERM call R.CO call R.NL ; .. close line ret ; ; Build name of file ; ENTRY Reg DE points to FCB ; TellFP: dec de ld a,(de) call usrset ; Set user inc de call prdufn ; .. print ret ; ; Print resulting differences ; ENTRY Reg HL points to count ; prRes: call crlf ld de,(fp.FCB1) call TellFP ; Tell file names ld de,$AND call strcn0 ld de,(fp.FCB2) call TellFP ld de,$FCL call strcn0 ld de,$NO call isZero? ; Test no difference jr z,..prRes call _dec ; .. test one difference ld de,$ONE jr z,..prRes call _.inc ; Fix number IF @GERM ld a,'e' ELSE ld a,'s' ENDIF ;@GERM ld ($RES.2.e),a ; .. change message a bit push hl call cnv32tA ; Convert to ASCII pop hl ..prRes: ld (res$$),de ; Save resulting string push de ld de,$RES.1 call strcn0 ; Give final message pop de call strcn0 ld de,$RES.2 call strcn0 call crlf ld a,(_RED_) ; Test redirection cp '>' call z,FprRes ; .. yeap ret ; ; Print resulting differences to redirection file ; ENTRY Reg HL points to count ; FprRes: call R.NL ; Give new line IF @GERM ld a,'(' ELSE ld a,'[' ; .. open ENDIF ;@GERM call R.CO call GiveHFiles ; Tell files ld de,$RES.1 call R.STR ; Give final message ld de,(res$$) ; Get result pointer call R.STR ; .. and tell whatever ld de,$RES.2 call R.STR call R.NL ret ; ; Convert 32 bit number to ASCII ; ENTRY Reg HL points to number ; EXIT Reg DE points to ASCII buffer ; cnv32tA: ld de,$NUM.32 push de ld b,null call dout32 ; Convert to ASCII pop de ret ; ; Allocate memory for a new listset ; EXIT Reg HL points to free memory ; GetLink: ld hl,(Heap) ; Get heap push hl ld de,LIST add hl,de ; Get top ld (Heap),hl ; .. save ex de,hl ld hl,0 add hl,sp ; Get stack dec h ; Less one page or a sbc hl,de ; Test room jr c,AllocErr ; .. nope pop hl ; Get back pointer push hl ld e,l ld d,h inc de ld bc,LIST-1 ld (hl),0 ldir ; Clear array pop hl ret AllocErr: ld de,$NO.MEMORY call strcn0 ; Give message jp R.OS ; ; Init dynamic memory chain ; ENTRY Reg IX points to fileset ; GetSet: call GetLink ; Get memory ld (ix+_last),l ; .. save ld (ix+_last+1),h ld (ix+_cp),l ld (ix+_cp+1),h ret ; ; Read line from file ; ENTRY Reg IX points to fileset ; Reg DE points to buffer ; EXIT Carry set on end of file ; ; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ; !!! NOTE: Library function 'getlnf' requires two !!! ; !!! additional bytes for length management !!! ; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ; Fgets: ld l,(ix+_fp) ; Get file block ld h,(ix+_fp+1) ld (rdpblk),hl ; .. into vector push ix dec de ; 'getlnf' specific dec de ld a,LOW (MAXLINE-1) ld (de),a ; Set length ld b,null call getlnf ; Read line pop ix push af call Abort? ; Test abort program pop af ; Get back state ret ; ; Process program abortion ; Abort?: call combrk ; Test character there ret nc ; .. nope cp CtrlC ; Test abort ret nz ; .. nope jp R.OS ; .. hard stop ; ; Set file position ; ENTRY Reg IX points to fileset ; Reg HL points to offset ; _fseek: push ix ld e,(ix+_fp) ; Get file pointer ld d,(ix+_fp+1) push de pop iy ld e,(iy+0) ; Fetch DMA buffer ld d,(iy+0+1) call setdma ; .. set it push hl pop ix ; Copy offset pointer ld a,(ix+3) ld (iy+2),a ; Set byte offset ld e,(iy+3) ; Fetch FCB ld d,(iy+3+1) ld a,(ix+0) ; Get position ld h,(ix+1) ld l,(ix+2) call execDU dw fseek ; Position file jr c,seek.err call dskred ; Advance buffer pop ix ret nc seek.err: ld de,$BAD.SEEK call strcn0 ; .. error seeking jp R.OS ; ; Get file position ; ENTRY Reg HL points to file pointer ; EXIT Regs D,E,H,L hold position ; (Reg L holds byte pointer) ; _ftell: inc hl ; Skip buffer pointer inc hl ld a,(hl) ; Get byte position push af inc hl ld e,(hl) ; Fetch FCB inc hl ld d,(hl) call execDU dw ftell ; Get it ld d,a ; Unpack ld e,h ld h,l pop af ld l,a ret ; ; Read line from file ; ENTRY Reg IX points to file set ; Readln: ld a,(ix+_eof) ; Test eof or a ret nz ; .. yeap, skip ld e,(ix+_last) ; Get list pointer ld d,(ix+_last+1) push de pop iy svxy _offset,_loffs ; Save offset ldix _no ; Get line number call _inc ; .. bump stix _no ; .. and save new value stiy _lno ld e,(ix+_line) ; Get address of line ld d,(ix+_line+1) call Fgets ; Read line sbc a,a ; Build eof ld (ix+_eof),a ld l,(ix+_fp) ; Get file pointer ld h,(ix+_fp+1) call _ftell ; Get file position stix _offset ; .. save it ret ; ; Decrement long integer pointed to by reg HL ; EXIT Zero set on result zero ; _dec: push hl ld c,l ; Copy pointer ld b,h ld de,one.32 call sub32 ; Decrement pop hl ; ; Test 32bit value EQ zero ; ENTRY Reg HL points to number ; EXIT Zero flag set if number is zero ; isZero?: push hl ld a,(hl) ; Get 1st inc hl or (hl) ; .. check remainder inc hl or (hl) inc hl or (hl) pop hl ret ; ; Increment long integer pointed to in reg HL ; _.inc: push ix push de push hl push hl pop ix ; Copy pointer ldix 0 ; Get number call _inc ; .. bump stix 0 ; .. and save new value pop hl pop de pop ix ret ; ; Increment long integer in = ; _inc: push bc ld bc,1 add hl,bc ; Add low part pop bc ret nc ; .. no carry inc de ; .. fix carry ret ; ; Convert string depending on selected option ; ENTRY Reg HL points to original line ; Reg DE points to temp line ; Reg IY points to option bits ; EXIT Reg HL points to copied line ; Reg DE points to end of copied line ; cnv.opt: ld (@s.b),de ; Save entry pointer call @asstr ; Unpack string ld (@s.t),de ; .. save top bit _b,(iy) ; .. process iptions call nz,no.trail bit _i,(iy) call nz,ignore.case ;; bit _t,(iy) ;; call nz,expand.tab bit _w,(iy) call nz,no.blanks bit _b,(iy) ; .. process iptions call nz,no.blk.str ld hl,(@s.b) ; Get back addresses ld de,(@s.t) ret ; ; Option '-B' - ignore trailing blanks >> trailing ; no.trail: ld de,(@s.b) ; Get start call skpblk ; .. skip ld (@s.b),de ; .. set new start ret ; ; Option '-B' - ignore trailing blanks >> other strings ; no.blk.str: bit _w,(iy) ret nz ; Ignore if no blank here ld hl,(@s.b) ; Get start ld e,l ld d,h ..no.blk: ld a,(hl) ; Test character a blank cp ' ' jr nz,fix.blnk inc hl ld a,' ' cp (hl) ; Test more than one dec hl jr nz,fix.blnk ; .. nope, save single blank wt.blnk: inc hl cp (hl) ; Wait for no blank jr z,wt.blnk dec hl ; .. fix fix.blnk: ld (de),a inc de inc hl or a jr nz,..no.blk ld (@s.t),de ; Change top ret ; ; Option '-I' - ignore character case ; ignore.case: ld de,(@s.b) ; Get start ld b,null call upplin ; .. convert to UPPER case ret ; ; Option '-W' - ignore all blanks ; no.blanks: ld hl,(@s.b) ; Get start ld e,l ld d,h ..no.bln: ld a,(hl) ; Get character cp ' ' ; Test blank jr z,ignore.bln ld (de),a ; Unpack inc de ignore.bln: inc hl or a ; Test end jr nz,..no.bln ld (@s.t),de ; Change top ret ; ; Compare strings at ^DE and ^HL, fix for options ; EXIT Zero set if same ; _strcmp: push iy ld iy,CnvMode ; Load mode bits push de ld de,(Heap) ; Init temp base call cnv.opt ; Set first string ex (sp),hl ; Bring back pointer call cnv.opt ; Set second string pop de pop iy call strcmp ; .. compare ret ; ; Get key of line ; ENTRY Reg HL points to line ; EXIT REg DE holds key ; MakeKey: ld de,0 ; Clear result ..MakeKey: ld a,(hl) ; Test end or a ret z ; .. yeap inc hl add a,e ; .. add ld e,a jr nc,..MakeKey inc d ; Remember carry jr ..MakeKey ; ; Set key of line ; ENTRY Reg IX points to fileset ; SetKey: ld l,(ix+_line) ; Get line ld h,(ix+_line+1) call MakeKey ; Get key of this line ld l,(ix+_last) ; Get listset ld h,(ix+_last+1) push hl pop iy ld (iy+_key),e ; Set key ld (iy+_key+1),d ret ; ; Add line to chain ; ENTRY Reg IX points to fileset ; Addln: ld a,(ix+_eof) ; Test eof or a ret nz ; .. yeap, skip call GetLink ; Get list item ld e,(ix+_last) ; Get set pointer ld d,(ix+_last+1) push de pop iy ; .. get it ld (iy+_next),l ; Set to next ld (iy+_next+1),h ld (ix+_last),l ld (ix+_last+1),h call Readln ; Get next line call SetKey ; Store key ret ; ; Report differences ; Report: ld hl,F1cnt ld ix,FIB1 ld iy,F1.arr call SetDiff ; Calculate differences ld hl,F2cnt ld ix,FIB2 ld iy,F2.arr call SetDiff ld hl,F1cnt call isZero? ; Test count1 > 0 jr z,.append ; .. it's append ld hl,F2cnt call isZero? ; Test count1 > 0, too jr z,.delet ; .. it's delete ; ; -->> Change lines in oldfile to those in newfile ; ld de,$CHANGE ; Load string ld hl,ChgTab ; .. and array jr ..report ; .. print ; ; -->> Append lines from newfile ; .append: ;; ld hl,FIB1.cp.no ;; call _dec ; Decrement ld de,$APPEND ld hl,AppTab jr ..report ; ; -->> Delete lines in oldfile ; .delet: ld de,$DELETE ld hl,DelTab ..report: call printf ; Print formatted output ld a,(style) cp LISTING ; Test list format jr nz,no_old ld hl,F1cnt call isZero? ; Test count1 > 0 jr z,no_old ld ix,FIB1 ld a,'<' call fp.report ld hl,F2cnt call isZero? ; Test count2 > 0 ret z ; .. nope, that's all ld de,$DELIM call R.STR ; Give delimiter no_old: ld hl,F2cnt call isZero? ; Test count2 > 0 ld ix,FIB2 ld a,'>' call nz,fp.report ; Give report for new lines ret ; ; Do line report ; ENTRY Reg IX points to file set ; Reg HL points to count ; Accu holds file indicator ; fp.report: ld ($MODE),a ; Set indicator ld (cntptr),hl ; Save count ld l,(ix+_cp) ; Get chain start ld h,(ix+_cp+1) ld de,_loffs add hl,de ; Get offset pointer call _fseek ; Position file ..fp.report: call _buff ; Read line push de ld de,$MODE call R.STR ; Give delimiter pop de call Filter.Tab ; Print line ld hl,(cntptr) call _dec jr nz,..fp.report ret ; ; Print result line - filtering tabs if option set ; ENTRY Reg DE points to string ; Filter.Tab: ld a,(CnvMode) ; Load mode bits bit _t,a ; Test request call nz,expand.tab ; .. yeap call R.STR ; Print line ret ; ; Option '-T' - expand tabs in output line ; ENTRY Reg DE points to line to be printed ; EXIT Reg DE points to string ; expand.tab: push bc push hl ld hl,(Heap) ; Get dynamic start push hl ld c,0 ; Clear column count ..expand: ld a,(de) ; Get character cp tab ; Test change jr nz,no.tab ; .. nope ld a,c ld b,00000111b and b ; Get column count xor b ld b,a call nz,swp.tab inc de jr ..expand no.tab: ld (hl),a ; Unpack inc hl inc de inc c or a jr nz,..expand pop de pop hl pop bc ret ; ; Fill buffer with blanks ; swp.tab: ld (hl),' ' ; Fill it inc hl djnz swp.tab ret ; ; Print formatted string ; ENTRY Reg DE points to formatted string ; Reg HL points to parameter array ; printf: ld a,(de) ; Get character inc de cp '%' ; Test format prefix jr z,format cp ',' ; Test previous number jr z,suppress? cp null ; Test end of line jr z,print.NL pr.numf: call R.CO ; Print character jr printf suppress?: call previous? ; Test previous same ld a,',' jr nz,pr.numf ; .. nope, print it inc de ; .. skip control %d inc de inc hl ; .. and number pointer inc hl jr printf format: inc de ; Skip next push de ld e,(hl) ; Fetch pointer to number inc hl ld d,(hl) inc hl push hl ex de,hl call cnv32tA ; Convert 32 bit number to ASCII call R.STR ; .. print pop hl pop de jr printf print.NL: call R.NL ; Close line ret ; ; Test curent parameter same as previous ; EXIT Zero set if so ; previous?: push de ; Save string pointer push hl ; .. and number pointer dec hl dec hl ld e,(hl) ; Fetch previous number pointer inc hl ld d,(hl) inc hl ld a,(hl) inc hl ld h,(hl) ld l,a ld b,4 ; .. get length call cmp.fix ; .. compare pop hl pop de ret ; ; Compare long integers at ^DE and ^HL ; EXIT Zero set if same numbers ; cmp.fix: ld a,(de) cp (hl) ; Compare ret nz ; .. not same inc de inc hl djnz cmp.fix ret ; ; Build count differences ; ENTRY Reg IX points to fileset ; Reg HL points to result ; Reg IY points to array where line numbers to be stored ; SetDiff: push hl ld l,(ix+_sync) ; Point to listset ld h,(ix+_sync+1) call get.no.ptr ; Get pointer push hl ld e,(iy+0) ; Fetch address ld d,(iy+1) push de ld bc,_long ldir ; Unpack pop hl ; Get back number pointer call _dec ; Decrement ld l,(ix+_cp) ; Point to first listset ld h,(ix+_cp+1) call get.no.ptr ; Get pointer push hl ld e,(iy+2) ; Fetch address ld d,(iy+3) ld bc,_long ldir ; Unpack pop de pop bc pop hl call sub32 ; .. get difference ret ; ; Get pointer to line number of a list set ; ENTRY Reg HL points to listset ; EXIT Reg HL points to line number ; get.no.ptr: ld de,_lno add hl,de ; Get pointer ret ; ; Compare chain till match ; Match: ld hl,FIB1 ; Init files ld (fsa),hl ld hl,FIB2 ld (fsb),hl call do.match ; Do it ret nz ; .. got match ld hl,FIB2 ; Try counterpart ld (fsa),hl ld hl,FIB1 ld (fsb),hl do.match: ld ix,(fsa) ld l,(ix+_cp) ; Fetch start of chain ld h,(ix+_cp+1) _mtch.cp: ld (cptr),hl ; Save pointer push hl pop iy ld ix,(fsb) ld l,(ix+_last) ; Get pointer to last ld h,(ix+_last+1) ld de,_key add hl,de ; Point to key ld a,(hl) cp (iy+_key) ; Test same keys jp nz,_mtch.nok inc hl ld a,(hl) cp (iy+_key+1) jp nz,_mtch.nok ld ix,(fsa) ld hl,(cptr) ; Get pointer ld de,_loffs add hl,de ; Get address of offset call _fseek ; Position file call _buff ; Read line ld ix,(fsb) ld l,(ix+_line) ld h,(ix+_line+1) call _strcmp ; Compare lines jp nz,_mtch.nok ; .. no match ld hl,(fsb) push hl pop ix ld de,_offset add hl,de call _fseek ; Position 2nd file ld hl,1 ; Init match count ld (_match),hl _mtch.mtch: ld ix,(fsa) call _buff ; Get buffers push de ld ix,(fsb) call _buff pop hl call _strcmp ; Test same jr nz,_mtch.brk ld hl,(_match) inc hl ; Bump match count ld (_match),hl dec hl ld de,(matches) or a sbc hl,de jr c,_mtch.mtch ; .. still more _mtch.brk: ld hl,(_match) ld de,(matches) or a sbc hl,de ; Test sync'ed jp c,_mtch.nok ; .. nope ld ix,(cptr) ld l,(ix+_next) ld h,(ix+_next+1) ld a,l or h ; Test last entry jr z,_mtch.last ld de,_loffs add hl,de ; Point to offset ex de,hl ld hl,(fsa) ld bc,_offset add hl,bc ; Point to file offset ex de,hl ld bc,_long ldir ; .. unpack offset ld ix,(fsa) ld (ix+_eof),FALSE ; .. clear end of file _mtch.last: ld hl,(fsa) push hl pop ix ld de,_no add hl,de ex de,hl ld hl,(cptr) push hl ld bc,_lno add hl,bc ld bc,_long ldir ; Set line number pop hl ld (ix+_sync),l ; Set list pointer ld (ix+_sync+1),h ld ix,(fsb) ld a,(ix+_last) ; Unpack for 2nd file ld (ix+_sync),a ld a,(ix+_last+1) ld (ix+_sync+1),a xor a inc a ; Indicate match ret _mtch.nok: ld ix,(cptr) ; Get chain pointer ld l,(ix+_next) ; Get next chain ld h,(ix+_next+1) ld a,l or h ; Test NIL jp nz,_mtch.cp xor a ; Set no match ret ; ; Read buffer from file ; ENTRY Reg IX points to file set ; EXIT Reg DE points to buffer ; _buff: ld e,(ix+_buffer) ; Get buffer address ld d,(ix+_buffer+1) push de call Fgets ; Read line pop de ret ; ; Synchronize to next lines ; Sync: call Match ; Test match push af ; Save result jr z,SyncEnd ; .. no match call Report ; Give report ld hl,(Root) ld (Heap),hl ; Release chains ld ix,FIB1 call GetSet ; Init base dynamic memory ld ix,FIB2 call GetSet SyncEnd: ld hl,FIB1+_offset ld ix,FIB1 call _fseek ; Position files ld hl,FIB2+_offset ld ix,FIB2 call _fseek pop af ret ; ; Difference detected, so proccess sync task ; ExecSync: ld hl,dfcnt call _.inc ; Bump counter ld ix,FIB1 call SetKey ; Get key of lines ld ix,FIB2 call SetKey ..ExecSync: ld ix,FIB1 call Addln ; Add lines ld ix,FIB2 call Addln call Sync ; Synchronize jr z,..ExecSync ; .. loop on if not sync'ed ret ; ; The comparision task ; EXIT Reg HL points to difference count ; doDFC: ld a,(_RED_) ; Test redirection in progress cp '>' call z,GiveFHint ; .. tell files ld ix,FIB1 call GetSet ; Init base dynamic memory ld ix,FIB2 call GetSet ld hl,fp.F1 ; Set file pointers ld (FIB1+_fp),hl ld hl,fp.F2 ld (FIB2+_fp),hl ld hl,NO_DIFF ld (dfcnt),hl ; Clear count ld (dfcnt+2),hl ..doDFC: ld ix,FIB1 call Readln ; Read line from 1st file ld ix,FIB2 call Readln ; .. read line from 2nd file ld hl,(FIB1+_line) ; Get line pointers ld de,(FIB2+_line) call _strcmp ; Compare call nz,ExecSync ; .. not same ld a,(FIB2+_eof) ; Test end of file ld c,a ld a,(FIB1+_eof) or c jr z,..doDFC ; .. loop on ld hl,dfcnt ; Get resulting pointer ret ; ; Test if file2 has only drive defined ; isDU.only?: ld hl,(fp.FCB2) ; Get pointer inc hl ld a,(hl) ; Test name cp ' ' ret nz ; .. yeap ld de,(fp.FCB1) dec hl dec hl dec de ld a,(de) cp (hl) ; Test same user inc de inc hl jr nz,diff.cpy ; .. nope, copy name ld a,(de) cp (hl) ; Test same drive jr z,not.diff ; .. should not be diff.cpy: inc de inc hl ex de,hl ld bc,.fname+.fext ldir ; Set name of file 1 ret ; ; Verify different files to be compared ; sameFiles?: ld de,(fp.FCB1) ld hl,(fp.FCB2) dec de ; Remember user dec hl ld b,1+.fdrv+.fname+.fext ..same?: ld a,(de) ; Get name cp (hl) ; Compare ret nz ; .. ok, not same inc de inc hl djnz ..same? not.diff: ld de,$NOT.DIFF call strcn0 ; Tell error jp R.D.OS ; ; Test >> in redirection ; REDIR.append?: ld a,(hl) ; Get previous character ld (_APP_),a ; .. save cp '>' ; Test append call nz,inc.r ; .. fix pointer if so ld (hl),null ; Clear character ex af,af' ld a,l sub CCPBUF+1 ld (CCPBUF),a ; Set new length ex af,af' call z,inc.r ; Skip 2nd > inc.r: inc hl ret ; ; Process redirection ; REDIR?: ld hl,CCPBUF ; Point to input ld a,(hl) ; At least we need .. cp 3 ; .. blank.>.c ret c ; Ignore empty line ld c,a ld b,0 add hl,bc ld a,'>' cpdr ; Find redirection symbol ret nz ; .. nope call REDIR.append? ; Test >> call getdu ; Get disk/user ret c ; .. error push bc ld (R$PB),hl ; Set into block ld hl,(R$FCB) ld (R$PB+2),hl ld de,R$PB call parse ; Parse file pop bc ret c ld a,c ld (fp.REDUSR),a ; Set user ld hl,(R$FCB) dec hl ld (hl),c ; Set user inc hl ld (hl),b ; .. and drive ex de,hl call wcard ; Verify no wildcard ret z ld a,(_APP_) cp '>' ; Test append jr z,REDIR.file? REDIR.crec: call execDU dw creatd ; Create file ret c ; .. bad jr REDIR.vec REDIR.file?: call execDU dw open ; Test file on board jr c,REDIR.crec ; .. nope, create it ld hl,(R$DMA) ex de,hl call setdma ; Set disk buffer ex de,hl call opnapp ; Open for append ret c ; .. oops, something wrong ld (R$BP),a ; Save offset REDIR.vec: ld a,'>' ld (_RED_),a ; Indicate redirection ld hl,fp.RED ld (wrpblk),hl ; Init write block ld de,R.OS ld hl,.R.OS ld bc,R.len ldir ; Unpack vectors ret ; ; Redirection vectors ; R.OS: jp OS R.D.OS: jp OS R.STR: jp strcn0 R.PRDUFN: jp prdufn R.NL: jp crlf R.CO: jp conout R.len equ $-R.OS ; ; Vectors in case of redirection ; .R.OS: jp R.close jp R.delete jp R.str0 jp R.prf jp R.crlf jp R.con ; ; End of program -> Close file ; R.close: ld a,(R$EOF) ; Test write in progress or a jp nz,OS ; .. nope call uclose ; .. close file jp OS ; ; End of program in case of error -> Delete file ; R.delete: ld de,(R$FCB) call execDU dw close ; Close file call delete ; .. and delete it jp OS ; ; Write string to file ; R.str0: ld a,(de) ; Get character or a ret z call R.con ; .. put to disk inc de jr R.str0 ; ; Print file name to file ; R.prf: ld a,(defio) ; Save I/O state push af ld a,1000b ; Set memory bit ld (defio),a push hl ld hl,$$FN push hl call TellFP ; Store into memory ld (hl),null ; .. close pop de call R.str0 ; .. put to file pop hl pop af ld (defio),a ; Reset I/O ret ; ; Give new line to file ; R.crlf: ld a,cr call R.con ; Give new line ld a,lf call R.con ret ; ; Print character to file ; R.con: push af ld a,(R$EOF) ; Test write in progress or a jr nz,skp.fout ; .. nope pop af push af call udskpt ; Write to disk sbc a,a ld (R$EOF),a ; Set eof state skp.fout: pop af ret ; ; Initialize file buffers ; ini$mem: ld hl,($memry) ; Get start of data ; ; Step 1 : Allocate record buffers ; Each buffer requires 128 bytes ; ld de,reclng ld (R$DMA),hl ; Redirection buffer add hl,de ld (fp.DMA1),hl ; .. file1 add hl,de ld (fp.DMA2),hl ; .. file2 add hl,de ; ; Step 2 : Allocate file control blocks ; Each FCB requires 36 bytes plus one extra byte for user ; ld de,FCBlen inc hl ld (R$FCB),hl ; Redirection FCB add hl,de inc hl ld (fp.FCB1),hl ; .. file1 add hl,de inc hl ld (fp.FCB2),hl ; .. file2 add hl,de ; ; Step 3 : Allocate line buffers ; Each line requres MAXLINE bytes plus two for management ; ld de,MAXLINE inc hl ; 'getlnf' specific inc hl ld (FIB1+_line),hl add hl,de inc hl inc hl ld (FIB1+_buffer),hl add hl,de inc hl inc hl ld (FIB2+_line),hl add hl,de inc hl inc hl ld (FIB2+_buffer),hl add hl,de ; ; Final setting ; ld (Heap),hl ; .. set start of heap ld (Root),hl ex de,hl call setdma ; Change from CCP buffer ld hl,ugteof ld (getlnf-2),hl ; Init 'fgetc' vector ret ; ; -->> Simple error routine on cold start ; Error: ld c,.string call BDOS ; Give message jp OS ; .. abort ; ; %%%%%%%%%%%%%%%%% ; %%% ENTER DFC %%% ; %%%%%%%%%%%%%%%%% ; MAIN: sub a ; Verify right machine ld de,$ILL.CPU jp pe,Error call getver ; Get OS version ld de,$ILL.OS jp c,Error ld sp,(TPAtop) ; Get stack call ini$mem ; Initialize memory buffers call usrget ; Get current user ld (curusr),a ; .. save push af call REDIR? ; Process redirection pop af ld (curusr),a ; Save user call usrset ; .. and log again ld hl,ARGV ld de,CCPBUF ld b,argmax call cmdarg ; Get arguments jr c,Help ; .. invalid sub argF ; Delete file offset jr c,Help ; .. invalid ld (ARGC),a ; Save argument index call nz,GetOpt ; .. get options ld a,(ARGC) ; Get file index push af ld hl,ARGV call indexa ; Get pointer ld ix,fp.F1 call Fassign ; Assign file1 pop af inc a ld hl,ARGV call indexa ; Get pointer ld ix,fp.F2 call Fassign ; Assign file2 call isDU.only? ; Process file2 drive only call sameFiles? ; Verify different files ld de,(fp.FCB1) call Fopen ; Open file1 ld de,(fp.FCB2) call Fopen ; Open file2 call doDFC ; Do the main task call prRes ; Give result jp R.OS ; .. that's all Help: ld de,$HELP call strcn0 ; Give help jp R.D.OS end MAIN