        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


err:				; 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	err

err2:	mov     dx,offset err2msg  ; print "disk is full".
        mov     cx,err2len
	mov	al,2		; return code = 2
	jmp	err

err3:	mov     dx,offset err3msg  ; print "bad parameter"
        mov     cx,err3len
	mov	al,3		; return code = 3
	jmp	err

err4:	mov     dx,offset err4msg  ; print "input device error"
        mov     cx,err4len
	mov	al,4		; return code = 4
	jmp	err


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
