| .MODEL | SMALL | |
INCLUDE | PCMAC.INC | | |
| ASSUME | SS:NOTHING | |
; | | | |
| .STACK | 100 | |
; | | | ; Constants |
BUFLEN | EQU | 512 | ; Must be a power of 2 |
CR | EQU | 13 | |
LF | EQU | 10 | |
WANTED | EQU | 20 | ; Number of lines of tail desired |
CtrlZ | EQU | 26 | ; Sometimes used to end text files |
; | | | |
| .DATA | | |
Buffer | DB | BUFLEN DUP (?) | |
Handle | DW | ? | ; The file handle |
LCount | DW | ? | ; Number of Line Feeds encountered |
Start | DD | ? | ; loc of buf srart in file; least sig first |
UsageMsg | DB | 'usage: tail filename',CR,LF,'$' | |
; | | | |
| .CODE | | |
| EXTRN | ParseCmd : NEAR, CseCmd : NEAR, CCheck : NEAR | |
Tail | PROC | | |
| mov | ax, @data | |
| mov | ds, ax | |
| call | ParseCmd | ; Get filename from command line |
| test | si, si | |
| jnz | DoOpen | |
| _PutStr | UsageMsg | |
| _Exit | 2 | |
DoOpen: | | | |
| push | ds | |
| _Open | si, Read, es | |
| pop | ds | |
| call | CCheck | |
| mov | Handle, ax | |
| mov | LCount, Wanted + 1 | ; Initialize line count |
| _LSeek | Handle, FromEnd, 0 | ; Find length of file |
| call | CCheck | |
| add | ax, BufLEN - 1 | ; Round end of file to next |
| | | ; even multiple |
| adc | dx, 0; | ; of BUFLEN (NOTE |
| and | ax, NOT (BUFLEN - 1) | ; MULTIPLE PRECISION |
| mov | WORD PTR [Start], ax | ; BUFLEN must be a pwr |
| mov | WORD PTR [Start + 2], dx | ; of 2 FOR THE 'AND' |
| | | ; TO WORK |
| mov | ax, ds | |
| mov | es, ax | |
| pushf | | |
| std | | |
ReadNextRecord: | | | |
| sub | WORD PTR [Start], | BUFLEN |
| sbb | WORD PTR [Start + 2], 0 | |
| jge | DoRead | |
| mov | WORD PTR [Start], 0 | ; If < 0, we're at start |
| mov | WORD PTR [Start + 2], 0; | |
| jmp | Done | |
DoRead: | | | |
| _LSeek | Handle, FromStart, <WORD PTR Start>, <WORD PTR Start+2> | |
| call | CCheck | |
| _Read | Handle, buffer, BUFLEN | |
| call | CCheck | |
| mov | cx, ax | ; save number of characters read |
| jcxz | ReadNextRecord | ; shouldn't happen |
| mov | di, OFFSET Buffer -1 | ; Set up ptr to end of buffer |
| add | di, cx | |
| mov | al, LF | Character to search for |
LineLoop: | | | |
| repne | scasb | ; stop on LF or start of buffer |
| jne | ReadNextRecord | ; LF not found |
| dec | LCount | |
| jnz | LineLoop | |
| sub | di, OFFSET Buffer - 2 | ; di already one past LF |
| add | WORD PTR [Start], di | ; update to start of tail |
| adc | WORD PTR [Start + 2], 0 | |
Done: | | | |
| _Lseek | Handle, FromSTart, <WORD PTR Start>, <WORD PTR Start + 2> + 2> | |
| call | CCheck | |
| cld | | ; Reset direction up |
OutputLoop: | | | |
| _Read | Handle, buffer, BUFLEN | |
| call | CCheck | |
| mov | cx, ax; | ; number of bytes read => cx |
| jcxz | EOF | |
| mov | si, OFFSET Buffer | |
DisplayLoop: | | | |
| lodsb | | |
| cmp | al, CtrlZ | ; Sometimes text file terminator |
| je | EOF | |
| _PutCh | al | |
| loop | DisplayLoop | |
| jmp | OutputLoop | |
EOF: | | | |
|
| popf | | |
| _Exit | 0 | ; normal termination |
Tail | ENDP | | |
| END | Tail | |
| | | |