name trim
page 55,132
title 'TRIM - excerpt lines of a file'
;
; TRIM --- excerpts selected columns from each line
; of a file and writes them to the selected
; output device or file.
;
; A "filter" for MS-DOS or PC-DOS version 2 or higher,
; after the fashion of Unix. Reads from the standard input
; (redirectable) and writes to the standard output (redirectable).
; Error messages are directed to the standard error device.
;
; TRIM can be (and usually would be) used in a pipe, e.g.
;
; | TRIM 7,45 |
;
; transmits only the characters in columns 7 to 45 (inclusive)
; of each line. A minus sign reverses the action, e.g.
;
; | TRIM -7,45 |
;
; transmits all characters except those in columns 7 to 45.
;
; Special actions:
;
; | TRIM 0 | deletes trailing spaces from lines, and
; | TRIM -0 | also discards empty lines
;
; By A. K. Head, 6 Duffryn Place, Melbourne, Australia 3142
; and Ray Duncan, Laboratory Microsystems Inc.
;
command equ 80h ; buffer for command tail
fcb1 equ 5ch ; default file control block #1
fcb2 equ 6ch ; default file control block #2
buflen equ 16384 ; buffer length, alter to taste
cr equ 0dh ; ASCII carriage return
lf equ 0ah ; ASCII line feed
ff equ 0ch ; ASCII form feed
eof equ 01ah ; End-of-file marker
tab equ 09h ; ASCII tab code
blank equ 20h ; ASCII blank
; DOS 2.x pre-defined handles
stdin equ 0000 ; standard input file
stdout equ 0001 ; standard output file
stderr equ 0002 ; standard error file
stdaux equ 0003 ; standard auxilliary file
stdprn equ 0004 ; standard printer file
cseg segment para public 'CODE'
assume cs:cseg,ds:cseg
org 100H ; start .COM at 100H
start: jmp near ptr trim
param1 dw 0 ; command parameter #1
param2 dw 0 ; command parameter #2
sign dw 0 ; nonzero if "-" in command
count dw 0 ; column count, current line
topin dw 0 ; chars in input buffer - 1
char db 0 ; current character
trim proc far
xor si,si ; initialize buffer pointers
xor di,di
mov bx,fcb1+1 ; addr of parsed parameter 1
call getprm ; convert it
cmp ax,0
je trunc ; zero parameter, go truncate
; trailing blanks etc.
mov param1,ax ; save first parameter
mov bx,fcb2+1 ; addr of parsed parameter 2
cmp byte ptr [bx],blank ; is it present at all?
jne trim0 ; yes, proceed
jmp err3 ; no, exit
trim0:
call getprm ; convert it
mov param2,ax ; save 2nd parameter
cmp ax,param1 ; is end column < start column?
jnb trim1
jmp err3 ; yes, exit with error message
trim1: mov count,0 ; starting a new line,
; initialize column counter
trim2: inc count ; count characters
call cin ; read a character
mov al,char ; is it carriage return?
and al,07fh ; (ignore high bit in case
cmp al,cr ; this is Wordstar file)
je trim5 ; yes, found end of line
mov ax,count ; fetch current char count
cmp sign,0 ; is this include or exclude call?
jne trim4 ; jump, -, exclude range
; proceed, +, include range
cmp ax,param1 ; is column counter within
; desired range?
jb trim2 ; no, discard this char.
cmp ax,param2
ja trim2 ; no, discard this char.
trim3: call cout ; yes, use this character
jmp trim2 ; get next char.
trim4: cmp ax,param1 ; is column counter outside
; of excluded range?
jb trim3 ; yes, use this character
cmp ax,param2
ja trim3 ; yes, use this character
jmp trim2 ; no, discard this character
trim5: ; found end of line
call cout ; write carriage return
call cin ; read presumed line feed
call cout ; write line feed
jmp trim1
trunc: ; come here if zero parameter
; to delete trailing blanks from
; all lines. If - sign was in
; command parameter, also delete
; empty lines completely.
trunc1: mov count,0 ; initialize column counter
xor bp,bp ; init line pointer
trunc2: call cin ; read a character
mov al,char ; is it carriage return
and al,07fh ; (ignore high bit in case
cmp al,cr ; this is Wordstar file)
je trunc3 ; yes, go process end of line
mov al,char ; transfer char. to forming line
mov byte ptr ds:[line+bp],al
inc bp
cmp char,blank ; is character a space code?
je trunc2 ; yes, get next char
mov count,bp ; no, update column count
jmp trunc2 ; get next char.
trunc3: xor bp,bp ; text string now in LINE
call cin ; discard line feed
cmp count,0 ; was line empty?
jne trunc4 ; no, go output it
cmp sign,0 ; deleting empty lines?
jne trunc1 ; yes, discard this one
jmp trunc5 ; no, send cr-lf sequence
trunc4: ; now transfer LINE to BUFOUT
mov al,byte ptr ds:[bp+line]
mov char,al ; get next char and
call cout ; send it to output
inc bp
cmp bp,count ; entire line sent yet?
jb trunc4 ; no, send another char
trunc5: mov char,cr ; send carriage return
call cout
mov char,lf ; and line feed
call cout
jmp trunc1
exit: cmp di,0 ; output buffer empty?
je exit1 ; yes
call outbuf ; no, flush it
exit1: mov ax,4c00h ; exit with return code=0
int 21h ; if no errors were encountered
error: ; print error message and exit.
; DS:DX = addr of message
; CX = length of message
; AL = return code
push ax ; save return code
mov ah,40h ; function 40 = write
mov bx,stderr ; handle for error output
int 21h
pop ax ; retrieve return code
mov ah,4ch ; function 4C = exit
int 21h
err1: mov dx,offset err1msg ; print "output device error"
mov cx,err1len
mov al,1 ; return code = 1
jmp error
err2: mov dx,offset err2msg ; print "disk is full".
mov cx,err2len
mov al,2 ; return code = 2
jmp error
err3: mov dx,offset err3msg ; print "bad parameter"
mov cx,err3len
mov al,3 ; return code = 3
jmp error
err4: mov dx,offset err4msg ; print "input device error"
mov cx,err4len
mov al,4 ; return code = 4
jmp error
trim endp
cout proc near ; output contents of "char"
; with autobuffering
mov al,char
mov byte ptr [di+bufout],al
inc di
cmp di,buflen ; buffer full yet?
jb cout1
call outbuf ; write buffer
cout1: ret ; back to caller
cout endp
outbuf proc near ; write buffer to std output
mov ah,40h ; function 40 = write
mov bx,stdout ; predefined handle
mov cx,di ; number of characters
lea dx,bufout ; DS:DX = buffer addr
int 21h ; request DOS service
jc err1 ; jump, device write error
cmp ax,di
jne err2 ; jump, disk is full
xor di,di ; initialize output buff pointer
ret ; back to caller
outbuf endp
cin proc near ; input next char with buffering
inc si ; bump input buffer pointer
cmp si,topin ; buffer exhausted?
jbe cin2 ; no, jump
mov ah,3fh ; yes, read some more data
mov bx,stdin ; predefined handle
mov cx,buflen ; max length to read
lea dx,bufin ; DS:DX = input buffer addr
int 21h ; request DOS service
jc err4 ; jump, input device error
cmp ax,0 ; end of file?
jne cin1
jmp exit ; yes, goto success exit point
cin1: dec ax ; save offset of top of data
mov topin,ax
xor si,si ; zero input buffer pointer
cin2: mov al,byte ptr [si+bufin]
mov char,al ; get next char
ret
cin endp
getprm proc near ; convert numeric parameter to
; binary and return it in AX
xor ax,ax ; initialize forming answer
mov cl,[bx] ; get first char
cmp cl,'-' ; is it minus sign?
jne getp2 ; no, jump
inc sign ; yes, set flag and
getp1: inc bx ; bump command string pointer
; past the "-" sign
mov cl,[bx] ; get next char
getp2: cmp cl,'0' ; at least 1 legal digit?
jb getp6 ; no, exit
cmp cl,'9'
ja getp6 ; no, exit
jmp getp4
getp3: inc bx ; advance through string
mov cl,[bx]
cmp cl,'0' ; make sure legal digit 0-9
jb getp5 ; not digit, jump
cmp cl,'9'
ja getp5 ; not digit, jump
mov dl,10 ; previous answer * 10
mul dl
getp4: sub cl,'0' ; add in the new digit
xor ch,ch
add ax,cx
cmp ah,0 ; new answer > 255?
je getp3 ; no, keep converting
jmp err3 ; yes, illegal parameter, exit
getp5: cmp byte ptr[bx],blank ; if not digit, must be blank
jne getp6 ; exit, bad parameter
ret ; back to caller
getp6: jmp err3 ; ... since too far to reach
; direct with conditional branch
getprm endp
err1msg db cr,lf
db 'trim: output device error'
db cr,lf
err1len equ (this byte)-(offset err1msg)
err2msg db cr,lf
db 'trim: disk is full.'
db cr,lf
err2len equ (this byte)-(offset err2msg)
err3msg db cr,lf
db 'trim: bad parameter'
db cr,lf
err3len equ (this byte)-(offset err3msg)
err4msg db cr,lf
db 'trim: input device error'
db cr,lf
err4len equ (this byte)-(offset err4msg)
bufin equ this byte ; data is read here
; from the standard input
bufout equ bufin+buflen ; data to be written to
; standard output is built here
line equ bufout+buflen ; temporary line buffer
cseg ends
end start