        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
