title Tail Print Utility name ('TAIL') maclib base80 ; Program executes the UNIX-like program tail - display last ; few lines of specified files or start from offset ; Call it: ; TAIL [-n] {du:}files ; ; Display the last n lines of each filename on the console. ; The default value of n is 10 lines. ; ; TAIL +n {du:}files ; ; Display all lines started from line n of each filename ; on the console. ; ; If more than one file is specified, the start of each file ; looks like: ; ; ==>filename<== ; ; Copyright (C) Werner Cirsovius ; Hohe Weide 44 ; D-2000 Hamburg 20 ; Voice +49 40 4223247 _VERS macro ;; 03-dec-92 db '1.0' endm _PRG macro db 'TAIL' endm ext strcn0,fidrus,parse,wcard,string,getver ext cmdarg,srcfrst,srcnxt,open,fillin,rdbfp ext combrk,getdu,usrset,decin,filsiz ext dskred,rndred _FCB equ 16 ; ; Get number from string ; ENTRY Reg HL points to number ; EXIT Reg HL holds number ; Carry set on conversion error or result zero ; Numb: ex de,hl ld b,nul call decin ; .. get number ret c ; .. error ld a,l ; Verify non-zero or h ret nz scf ret ; ; Get number from command line ; EXIT Carry set on invalid number ; GetNum: ld hl,(ARGV) ; Get 1st argument ld a,(hl) cp '-' ; Test prefix jr z,GotMin ; .. yeap cp '+' ; .. maybe offset scf ccf ret nz ; .. no GotMin: ld (TailType),a ; Save type inc hl call Numb ; Get number ret c ; .. invalid ld (HdLine),hl ; Store ld hl,(ARGV+2) ld (ARGV),hl ; .. shift ret ; ; Parse a file name ; ENTRY Reg DE holds FCB ; Reg HL holds string ; EXIT Carry set on invalid parse ; ParseFile: push de call getdu ; .. find optional drive, user pop de ret c ; .. error ld (DU),bc ; .. save ld (PB),hl ld (PB+2),de ld de,PB call parse ; Do the parse ret c ; .. error ld a,(DU) call usrset ; Set user ld a,(DU+1) ; Get drive ld de,(PB+2) ld (de),a ; .. set drive call wcard ; Check wild card scf ccf ret nz ; .. nope ld a,TRUE ld (Wild),a ; Set flag or a ret ; ; Sample files ; ENTRY Reg DE holds file mask ; Reg HL points to data area ; EXIT Reg BC holds file count ; Carry set on no file found ; GetFiles: ld bc,0 ; Clear count push hl call srcfrst ; Search for file pop de jr c,EndSrc ; .. that's all GetNxt: push bc ld bc,_FCB-1 ldir ; Unpack file name pop bc inc bc ; .. bump call srcnxt jr nc,GetNxt EndSrc: ld a,c or b ; Test any ret nz scf ret ; ; Give header ; TellFile: ld de,$LEFT call strcn0 ; Give signs ld de,FCB call fidrus ; Print drive,user and file ld de,$RIGHT call strcn0 ret ; ; Decrement random record if not zero ; ENTRY Reg IX points to record ; EXIT Zero set if zero record ; RRN.0: ld a,(ix+0) or (ix+1) ; .. find it out or (ix+2) push af call nz,decRRN ; Decrement if not zero pop af ret ; ; Decrement random record ; ENTRY Reg IX points to record ; decRRN: ld a,(ix+0) dec (ix+0) ; .. count down ret p bit 7,a ; Verify prvious < 0 ret nz ; .. yeap ld a,(ix+1) dec (ix+1) ret p bit 7,a ret nz dec (ix+2) ret ; ; Print file from last lines ; PrintLast: ld de,FCB call filsiz ; Get size of file ret c ; .. file not found jr nz,NotEmpty ld a,l ; Test empty file or h ret z ; .. yeap NotEmpty: ld ix,FCB+.frrn ; Point to random record call decRRN ; Fix random record ld de,FCB call rndred ; .. read last record ld hl,DMA ld bc,reclng ld a,eof cpir ; .. find end of file dec hl ; .. fix a bit ld a,l sub DMA-1 ld b,a ex de,hl ld hl,(HdLine) ; Get count inc hl FindLF: ld a,(de) cp lf ; Find new line jr nz,NoLF dec hl ld a,l or h ; Test found jr z,GotLine NoLF: dec de djnz FindLF call RRN.0 ; Test 1st record found jr z,GotLine0 ; .. yeap ld de,FCB call rndred ; .. read previous record call IsBRK ; .. allow break ld de,DMA+RecLng-1 ld b,RecLng jr FindLF GotLine: dec b GotLine0: ld a,b ld (rdbfp),a ; Set pointer ld de,FCB call dskred ; .. no get record jr PrLoop ; .. then print ; ; Print file from offset ; PrintOff: ld a,RecLng ld (rdbfp),a ; Force read ld hl,(HdLine) ; Get count OffLoop: dec hl ld a,l or h jr z,PrLoop call RdLine ; Read line ret c ; .. eof jr OffLoop PrLoop: call RdLine ; Read line ret c ; .. eof call strcn0 ; .. print line jr PrLoop ; ; Read line from file - test break ; EXIT Carry set on error ; Reg DE points to buffer ; RdLine: call IsBRK ; Test break ld b,0 ld de,Line push de call fillin ; Read line pop de inc de ; .. skip buffer ret ; ; Test BREAK ; IsBRK: call combrk ; Test character ret nc cp CtrlC ; .. maybe break ret nz ld de,$ABORT call strcn0 ; .. give up jp OS dseg Line: db 90 ds 90+1 TailType: db '-' Wild: db FALSE ; DEFAULT FLAG PB: dw 0,0 ARGV: ds 2*2 DU: ds 2 HdLine: dw 10 ; DEFAULT COUNT $ILL.CPU: db 'Requires Z80 CPU',cr,lf,eot $ILL.OS: db 'Requires CP/M 3.x',cr,lf,nul $HELP: db 'Call it:',cr,lf db tab,tab _PRG db ' [-n] {du:}files',cr,lf,lf db 'Display the last n lines of each ' db 'filename on the console.',cr,lf db 'The default value of n is 10 lines.' db cr,lf,lf db 'or:',cr,lf db tab,tab _PRG db ' +n {du:}files',cr,lf,lf db 'Display all lines started from line n of ' db 'each filename on the console.',cr,lf,nul $BANNER: _PRG db ' v' _VERS db cr,lf,nul $INVAL: db 'Badly formed number',cr,lf,nul $INVPARS: db 'Badly formed filename',cr,lf,nul $NOMATCH: db 'No match',cr,lf,nul $ABORT: db cr,lf,' *** ABORT ***',cr,lf,lf,nul $NOFILE: db ': No such file',cr,lf,nul $LEFT: db cr,lf,'==>',nul $RIGHT: db '<==',cr,lf,lf,nul $memry:: dw 0 SrcFile: ds FCBlen cseg ; ; ************ ; *** MAIN *** ; ************ ; TAIL: sub a ; Test Z80 CPU jp po,Z80ok ld de,$ILL.CPU call string jp warm ; .. exit Z80ok: call getver ; Get OS version ld de,$ILL.OS jr c,EndTail ld sp,(TPAtop) ; Get stack ld hl,ARGV ld de,DMA ld b,2 call cmdarg ; Fetch arguments ld de,$HELP jr c,EndTail ; .. error call GetNum ; Get linecount ld de,$INVAL jr c,EndTail ld hl,(ARGV) ld de,SrcFile call ParseFile ; Parse file ld de,$INVPARS jr c,EndTail ld hl,($memry) ld de,SrcFile call GetFiles ; Get files jr nc,GoaHEAD ; .. ok ld a,(Wild) ; Test flag cp TRUE ld de,$NOMATCH jr z,EndTail ; Tell no match ld de,SrcFile ; Get file call fidrus ; .. type ld de,$NOFILE EndTail: call strcn0 ; Give a message jp warm ; .. exit GoaHEAD: ld de,$BANNER call strcn0 ; Tell a bit ld a,(SrcFile) ld (FCB),a ; Set drive ld hl,($memry) ; Init field Loop: ld de,FCBnam push bc ld bc,_FCB-1 ldir ; Unpack file pop bc ld de,FCB call open ; Get file jr c,SkpFile ; .. skip it push bc push hl ld a,(Wild) or a ; Test one file call nz,TellFile ; Give header ld a,(TailType) ; Get type cp '-' ; Look for last lines push af call z,PrintLast ; .. yeap pop af call nz,PrintOff ; .. print from offset pop hl pop bc SkpFile: dec bc ld a,b or c ; Test end jr nz,Loop jp warm end TAIL