DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T m

⟦9a8011a1e⟧ TextFile

    Length: 62356 (0xf394)
    Types: TextFile
    Names: »msscmd.asm«

Derivation

└─⟦9ae75bfbd⟧ Bits:30007242 EUUGD3: Starter Kit
    └─⟦71044c191⟧ »EurOpenD3/misc/kermit.ms-2.32.tar.Z« 
        └─⟦31f2d420d⟧ 
            └─⟦this⟧ »msscmd.asm« 

TextFile

        NAME    msscmd
; File MSSCMD.ASM
; Edit history:
; Last edit 21 Nov 1988
; 21 Nov 1988 Version 2.32
; 17 Oct 1988 Make command keyword search failure yield global kstatus of
;  failure status, to inform Take/Macros of a defective command.
; 4 Aug 1988 Fix keyword count initialization in help display.
; 1 July 1988 Version 2.31
; 7 June 1988 Add comand.impdo flag to permit a keyword search failure to
;  retry as a DO cmd (macro table). Used only by main Kermit command level.
; 27 May 1988 Allow "-<cr>" as line continuation pair and let "\-<cr>"
;  stand for "-<end of line>". Add comand.cmblen to sense buffer overflow on
;  calls to cmtxt; if comand.cmblen is left at zero then use 128 byte limit
;  (comand.cmblen is cleared at command end).
; 15 May 1988 Make :label keywords no-ops, make query ordinary char in TAKEs.
; 6 May 1988 Show ambiguous keywords at parse time, do graceful interaction.
; 28 March 1988 Permit '\%x' (x = '0' or above) in commands as 'variables'
;  (substituted string of text). Words are obtained from Macro table mcctab.
;  DEF MAC and DO MAC define variables. DEF macro uses comand.cmper > 0 to
;  allow '\%x'to be stored as a literal. Variables work in any command except
;  DEF MAC. Major redesign of whole parser. [jrd]
; 4 March 1988 Rewrite keyword parsing to permit non-alphabetized tables
;  and 8-bit characters. Add byte comand.cmwhite which when non-zero permits
;  leading whitespace in cmtxt and cmfile commands; it is reset at command
;  completion. Move procedure Prompt here from mssker. [jrd]
; 27 Feb 1988 Add capability of stdin being a file. [jrd]
; 1 Jan 1988 version 2.30

        public comnd, comand, prserr
        public intake, isdev, iseof, prompt, tolowr
        include mssdef.h

datas   segment public 'datas'
        extrn   flags:byte, trans:byte, taklev:byte, takadr:word, mcctab:byte
        extrn   kstatus:byte

comand  cmdinfo <>
cmer00  db      cr,lf,'?Program internal error, recovering$'
cmer01  db      cr,lf,'?More parameters are needed$'
cmer02  db      cr,lf,'?Word "$'
cmer03  db      '" is not usable here$'
cmer04  db      '" is ambiguous$'
cmer07  db      cr,lf,'?Ignoring extra characters "$'
cmer08  db      '"$'
cmer09  db      cr,lf,'?Text exceeded available buffer capacity$'
cmin00  db      ' Confirm with carriage return$'
cmin01  db      ' One of the following:',cr,lf,'$'
stkmsg  db      cr,lf,bell,'?Exhausted work space! Circular definition?$'
crlf    db      cr,lf,'$'
ctcmsg  db      5eh,'C$'
temp    dw      0                       ; temp (counts char/line so far)
errflag db      0                       ; non-zero to suppress cmcfrm errors
kwstat  db      0                       ; get-keyword status
cmdstk  dw      0                       ; stack pointer at comand call time
intake  db      0                       ; last command line was from Take
prevch  db      0                       ; previous char read by cmgetc
noparse db      0                       ; semicolons not special, if non-zero
subcnt  db      0                       ; count of chars matched in '\%'
datas   ends

code    segment public 'code'
        extrn   ctlu:near, cmblnk:near, clearl:near, locate:near, takrd:near
        extrn   takclos:near, docom:near
        assume  cs:code, ds:datas, es:nothing

;       This routine parses the specified function in AH. Any additional
;       information is in DX and BX.
;       Returns rskp on success and ret on failure

COMND   PROC NEAR
        mov     cmdstk,sp               ; save stack ptr for longjmp exit
        mov     noparse,0               ; recognize semicolons in Take files
        cmp     ah,cmcfm                ; Parse a confirm?
        jne     cm2                     ; nz = no
        jmp     cmcfrm                  ; get a confirm
cm2:    cmp     ah,cmkey                ; Parse a keyword?
        jne     cm3
        jmp     cmkeyw                  ; Try and get one
cm3:    cmp     ah,cmtxt                ; Parse arbitrary text
        jne     cm4
        jmp     cmtext
cm4:    cmp     ah,cmfile               ; parse text surrounded by whitespace
        jne     cm5
        jmp     cmfil0
cm5:    mov     ah,prstr                ; Else give error
        mov     dx,offset cmer00        ; "?Program internal error"
        int     dos
        jmp     prserr
COMND   ENDP

; This routine parses a keyword from the table pointed at by DX, help text
; point to by BX. Format of the table is as follows (use macro mkeyw):
;       addr:   db      N         ; Where N is the # of entries in the table
;               db      M         ; M is the size of the keyword (excl '$')
;               db      'string$' ; String is the keyword
;               dw      value     ; Value is data to be returned
; Keywords may be in any order and in mixed case.
; Return is rskp for success and ret for failure.

; comand.cmptab: pointer to keyword table (supplied by caller)
; comand.cmhlp: pointer to help message (supplied by caller)
; comand.cmsptr: pointer to current user word text
; comand.cmsiz: length of user text, excluding terminator
; comand.cmcr: 0 = empty lines not allowed, 1 = empty lines allowed
; comand.cmwhite: non-zero allows leading whitespace for cmtxt and cmfile,
;                 reset automatically at end of call
; comand.cmwptr: buffer write pointer to next free byte
; comand.cmrptr: buffer read pointer for next free byte
; comand.cmper: 0 to do \%x substitution. Set to 0 at end of call
; comand.impdo: non-zero permits keyword failure to retry as DO command, reset
;                automatically at time of failure.
cmkeyw  proc    near
        mov     comand.cmsiz,0          ; user word length
        mov     ax,comand.cmrptr        ; get command reading pointer
        mov     comand.cmsptr,ax        ; set pointer for start of user word
        mov     comand.cmhlp,bx         ; save the help pointer
        mov     comand.cmptab,dx        ; save the beginning of keyword table
        mov     bx,dx
        cmp     byte ptr[bx],0          ; get number of entries in table
        jne     cmky1
        jmp     cmky7                   ; e = no keywords to check, error
cmky1:  mov     comand.cmsflg,0ffh      ; skip leading spaces/tabs
        call    cmgtch                  ; get char from the user into ah
        jc      cmky3                   ; c = terminator
        mov     dx,comand.cmrptr        ; next byte to read
        dec     dx                      ; where we just read a char
        mov     comand.cmsptr,dx        ; remember start of keyword
        inc     comand.cmsiz            ; start counting user chars
cmky2:  call    cmgtch                  ; read until terminator
        jc      cmky3                   ; c = terminator
        inc     comand.cmsiz            ; count user chars
        jmp     short cmky2             ; no terminator yet

cmky3:  cmp     ah,'?'                  ; need help?
        jne     cmky4                   ; ne = no
        jmp     cmkyhlp                 ; do help and exit
cmky4:  cmp     ah,escape               ; escape?
        jne     cmky6                   ; ne = no
        call    cmkyesc                 ; process escape
        jc      cmky5                   ; c = failure (no unique keyword yet)
        mov     comand.cmper,0          ; reset to variable recognition
        mov     comand.cmkeep,0
        mov     comand.impdo,0          ; clear flag to prevent loops
        mov     comand.cmquiet,0        ; permit echoing again
        jmp     rskp                    ; return successfully to user

cmky5:  cmp     comand.cmsiz,0          ; started a word yet?
        je      cmky1                   ; e = no, ignore escape, keep looking
        jmp     cmkyhlp                 ; ne = yes, show type of error

cmky6:  cmp     comand.cmsiz,0          ; length of user's text, empty?
        je      cmky7                   ; e = yes, parse error
        push    bx
        mov     bx,comand.cmsptr        ; point at first user character
        cmp     byte ptr[bx],':'        ; start of a label?
        pop     bx
        jne     cmky6a                  ; ne = no, return success
        mov     comand.cmsiz,1          ; say just one byte
cmky6a:
        call    getkw                   ; get unique kw, point to it with bx
        jc      cmky8                   ; c = not found
        add     bl,[bx]                 ; add length of keyword text (CNT)
        adc     bh,0
        add     bx,2                    ; point at value field
        mov     bx,[bx]                 ; bx = return value following keyword
        mov     comand.cmper,0          ; reset to variable recognition
        mov     comand.cmkeep,0
        mov     comand.impdo,0          ; clear flag to prevent loops
        mov     comand.cmquiet,0        ; permit echoing again
        mov     errflag,0
        jmp     rskp                    ; return successfully
                                        ; all other terminators come here
cmky7:  cmp     comand.cmsiz,0          ; empty table or empty user's text?
        jne     cmky8                   ; ne = no
        cmp     comand.cmcr,0           ; empty lines allowed?
        jne     cmky10                  ; ne = yes, do not complain
        push    dx
        mov     ah,prstr
        mov     dx,offset cmer01        ; command word expected
        int     dos
        pop     dx
        mov     comand.cmquiet,0        ; permit echoing again
        mov     comand.impdo,0          ; clear flag to prevent loops
        ret                             ; do ret exit

cmky8:  cmp     comand.impdo,0          ; failed here, ok to try Macro table?
        je      cmky8a                  ; e = no, use regular exit path
        mov     comand.impdo,0          ; yes, but clear flag to prevent loops
        mov     comand.cmrptr,offset comand.cmdbuf ; reinit read pointer
        mov     comand.cmquiet,1        ; suppress echoing of same keyword
        mov     bx,offset docom         ; return DO as "found" keyword
        jmp     rskp                    ; return success to invoke DO

cmky8a: mov     comand.cmquiet,0        ; permit echoing again
        mov     errflag,1               ; say already doing error recovery
        or      kstatus,1               ; global command status, failure
        call    isdev                   ; reading pretyped lines?
        jnc     cmky9                   ; nc = yes, consume rest of line
        cmp     taklev,0                ; in a Take file?
        jne     cmky9                   ; ne = yes
        call    cmskw                   ; display offending keyword
        dec     comand.cmrptr           ; interactive, backup to terminator
        mov     bx,comand.cmrptr        ; look at it
        cmp     byte ptr [bx],' '       ; got here on space terminator?
        jne     cmky10                  ; ne = no, (cr,lf,ff) exit failure
        mov     ah,prstr                ; start a fresh line
        mov     dx,offset crlf
        int     dos
        call    bufreset                ; cut back buffer to just before term
        jmp     repars                  ; reparse interactive lines

cmky9:  call    cmcfrm                  ; get formal end of command line
         nop                            ;  to maintain illusion of typeahead
         nop                            ;  and let user backspace to correct
         nop                            ;  mistakes (we reparse everything)
        call    cmskw                   ; display offending keyword
cmky10: ret                             ; do ret exit
cmkeyw  endp

;;;;;; start support routines for keyword parsing.

cmkyesc proc    near                    ; deal with escape terminator
        call    bufreset                ; reset buffer to end just before ESC
        cmp     comand.cmsiz,0          ; user word length, empty?
        jne     cmkye2                  ; ne = have user text, else complain
cmkye1: call    esceoc                  ; do normal escape end-of-command
        stc                             ; say failure to fill out word
        ret
                                        ; add unique keyword to buffer
cmkye2: call    getkw                   ; is there a matching keyword?
        jc      cmkye1                  ; c = ambiguous or not found
        push    bx                      ; unique, bx points to structure
        push    si
        mov     cl,[bx]                 ; length of keyword
        mov     ch,0
        inc     bx                      ; point to first letter
        mov     si,comand.cmwptr        ; where next char goes
        mov     dx,comand.cmsiz         ; length of user word
        add     bx,dx                   ; add chars known so far
        sub     cx,dx                   ; calculate number yet to add
        add     comand.cmsiz,cx
        jcxz    cmkye4                  ; z = none
        mov     ah,conout               ; display new char
cmkye3: mov     al,[bx]                 ; get a keyword letter
        inc     bx
        call    tolowr                  ; lowercase
        mov     [si],al                 ; store it
        inc     si
        mov     dl,al
        int     dos                     ; display it
        loop    cmkye3                  ; do all new chars
cmkye4: mov     byte ptr[si],' '        ; insert space terminator in buffer
        mov     dl,' '                  ; display it
        mov     ah,conout
        int     dos
        inc     si
        mov     comand.cmrptr,si        ; move token pointer after the space
        mov     comand.cmwptr,si        ; next free slot is after the space
        mov     comand.cmsflg,0ffh      ; set space-seen flag
        pop     si
        pop     bx                      ; bx = keyword structure
        add     bl,[bx]                 ; add length of keyword text
        adc     bh,0
        add     bx,2                    ; point at value field
        mov     bx,[bx]                 ; bx = return value following keyword
        clc                             ; carry clear for success
        ret
cmkyesc endp

esceoc  proc    near                    ; do normal escape end-of-command
        push    ax
        push    dx
        mov     ah,conout               ; ring the bell
        mov     dl,bell
        int     dos
        pop     dx
        pop     ax
        call    bufreset                ; reset buffer
        stc                             ; say error condition
        ret
esceoc  endp

; Help. Question mark entered by user.  Display all the keywords that match
; user text. If text is null then use external help if available; otherwise,
; display all keywords in the table. Removes question mark from buffer and
; invokes reparse of command line to-date. User word starts at .cmsptr and
; is .cmsiz bytes long.
cmkyhlp proc    near
        mov     cx,0                    ; clear number of keyword (none yet)
        cmp     comand.cmsiz,0          ; user text given?
        jne     cmkyh1                  ; ne = yes, use matching keywords
        cmp     comand.cmhlp,0          ; external help given?
        jne     cmkyh6                  ; yes, use it instead of full table
cmkyh1: mov     temp,0                  ; count # chars printed on this line
        mov     bx,comand.cmptab        ; beginning of kw table
        mov     ch,[bx]                 ; length of table
        mov     cl,0                    ; no keywords or help displayed yet
        inc     bx                      ; point at CNT field
cmkyh2: cmp     comand.cmsiz,0          ; length of user word
        je      cmkyh3                  ; e = null, use full table
        call    cmpwrd                  ; compare keyword with user word
        jc      cmkyh5                  ; c = no match, get another keyword
cmkyh3: mov     al,[bx]                 ; length of table keyword
        add     byte ptr temp,al        ; count chars printed so far
        cmp     temp,76                 ; will this take us beyond column 78?
        jbe     cmkyh4                  ; be = no, line has more room
        mov     ah,prstr
        mov     dx,offset crlf          ; break the line
        int     dos
        mov     temp,0                  ; and reset the count
cmkyh4: cmp     cl,0                    ; any keywords found yet?
        jne     cmkyh4a                 ; ne = yes
        mov     dx,offset cmin01        ; start with One of the following: msg
        mov     ah,prstr
        int     dos
        inc     cl                      ; say one keyword has been found
cmkyh4a:mov     dl,spc                  ; put two spaces before each keyword
        mov     ah,conout
        int     dos
        int     dos
        add     temp,2                  ; count output chars
        mov     dx,bx                   ; get current keyword structure
        inc     dx                      ;  text part
        mov     ah,prstr
        int     dos                     ; display it
cmkyh5: dec     ch                      ; are we at end of table?
        jle     cmkyh7                  ; le = yes, quit now
        add     bl,[bx]                 ; next keyword, add CNT chars to bx
        adc     bh,0
        add     bx,4                    ; skip CNT, '$' and 16 bit value
        jmp     cmkyh2                  ; go examine this keyword

cmkyh6: mov     dx,comand.cmhlp         ; external help text
        mov     ah,prstr
        int     dos
        inc     cl                      ; say gave help already
cmkyh7: cmp     cl,0                    ; found any keywords?
        jne     cmkyh9                  ; ne = yes
        mov     cx,comand.cmsiz         ; length of word
        cmp     cx,0
        jg      cmkyh8                  ; g = something to show
        push    dx
        mov     ah,prstr
        mov     dx,offset cmer01        ; command word expected
        int     dos
        pop     dx
        jmp     prserr
cmkyh8: mov     kwstat,0                ; set keyword not-found status
        call    cmskw                   ; display offending keyword
cmkyh9: mov     ah,prstr                ; start a fresh line
        mov     dx,offset crlf
        int     dos
        call    bufreset                ; cut back buffer to just before '?'
        jmp     repars
cmkyhlp endp

; See if keyword is ambiguous or not from what the user has typed in.
; Return carry set if word is ambiguous or not found, carry clear otherwise.
; Uses table pointed at by comand.cmptab, user text pointed at by
; comand.cmsptr and length in comand.cmsiz.
cmambg  proc    near
        push    bx
        push    cx
        push    dx
        mov     dl,0                    ; count keyword matches so far
        mov     bx,comand.cmptab        ; look at start of keyword table
        mov     cl,[bx]                 ; get number of entries in table
        mov     ch,0                    ; use cx as a counter
        jcxz    cmamb8                  ; z = no table so always ambiguous
        inc     bx                      ; look at CNT byte of keyword
cmamb4: call    cmpwrd                  ; user vs table words, same?
        jc      cmamb6                  ; c = no match
        inc     dl                      ; count this as a match
        cmp     dl,1                    ; more than one match?
        ja      cmamb8                  ; a = yes, quit now
cmamb6: add     bl,[bx]                 ; add CNT chars to bx
        adc     bh,0
        add     bx,4                    ; skip CNT, '$' and 16 bit value
        loop    cmamb4                  ; do rest of keyword table
cmamb7: cmp     dl,1                    ; how many matches were found?
        jne     cmamb8                  ; ne = none or more than 1: ambiguous
        pop     dx                      ; restore main registers
        pop     cx
        pop     bx
        clc
        ret                             ; ret = not ambiguous
cmamb8: pop     dx                      ; restore main registers
        pop     cx
        pop     bx
        stc
        ret                             ; return ambiguous or not found
cmambg  endp

; Compare user text with keyword, abbreviations are considered a match.
; Enter with bx pointing at keyword table CNT field for a keyword.
; Return carry clear if they match, set if they do not match. User text
; pointed at by comand.cmsptr and length is in comand.cmsiz.
; Registers preserved.

cmpwrd  proc    near
        push    cx
        mov     cx,comand.cmsiz         ; length of user's text
        jcxz    cmpwrd2                 ; z: null user word matches no keyword
        cmp     cl,[bx]                 ; user's text longer than keyword?
        ja      cmpwrd2                 ; a = yes, no match
        push    ax
        push    bx
        push    si
        inc     bx                      ; point at table's keyword text
        mov     si,comand.cmsptr        ; buffer ptr to user input
        cld
cmpwrd1:lodsb                           ; user text
        mov     ah,[bx]                 ; keyword text
        inc     bx                      ; next keyword letter
        call    tolowr                  ; force lower case on both chars
        cmp     ah,al                   ; same?
        loope   cmpwrd1                 ; e = same so far
        pop     si
        pop     bx
        pop     ax
        jne     cmpwrd2                 ; ne = mismatch
        pop     cx                      ; recover keyword counter
        clc                             ; they match
        ret
cmpwrd2:pop     cx                      ; recover keyword counter
        stc                             ; they do not match
        ret
cmpwrd  endp

; Get pointer to keyword structure using user text. Uses keyword table
; pointed at by comand.cmptab and comand.cmsiz holding length
; of user's keyword (cmpwrd needs comand.cmsptr pointing at user's
; keyword and length of comand.cmsiz). Structure pointer returned in BX.
; Return carry clear for success and carry set for failure. Modifies BX.
getkw   proc    near
        push    cx
        mov     kwstat,0                ; keyword status, set to not-found
        cmp     comand.cmsiz,0          ; length of user word, empty?
        je      getkw3                  ; e = yes, fail
        mov     bx,comand.cmptab        ; table of keywords
        mov     cl,[bx]                 ; number of keywords in table
        mov     ch,0
        jcxz    getkw3                  ; z = none, fail
        inc     bx                      ; point to first
getkw1: call    cmpwrd                  ; compare user vs table words
        jc      getkw2                  ; c = failed to match word, try next
        mov     kwstat,1                ; say found one keyword, maybe more
        push    dx
        mov     dx,comand.cmsiz         ; users word length
        cmp     [bx],dl                 ; same length (end of keyword)?
        pop     dx
        je      getkw4                  ; e = yes, exact match. Done
        call    cmambg                  ; ambiguous?
        jnc     getkw4                  ; nc = unique, done, return with bx
        mov     kwstat,2                ; say more than one such keyword
getkw2: add     bl,[bx]                 ; next keyword, add CNT chars to bx
        adc     bh,0
        add     bx,4                    ; skip CNT, '$' and 16 bit value
        loop    getkw1                  ; do all, exhaustion = failure
getkw3: pop     cx
        stc                             ; return failure
        ret
getkw4: pop     cx
        clc                             ; return success
        ret
getkw   endp

; show offending keyword message. Comand.cmsptr points to user word,
; comand.cmsiz has length. Modifies AX, CX, and DX.
cmskw   proc    near
        mov     ah,prstr                ; not one of the above terminators
        mov     dx,offset cmer02        ; '?Word "'
        int     dos
        mov     cx,comand.cmsiz         ; length of word
        jcxz    cmskw3                  ; z = null
        mov     ah,conout
        push    si
        mov     si,comand.cmsptr        ; point to word
        cld
cmskw1: lodsb
        cmp     al,' '                  ; control code?
        jae     cmskw2                  ; ae = no
        push    ax
        mov     dl,5eh                  ; caret
        int     dos
        pop     ax
        add     al,'A'-1                ; plus ascii bias
cmskw2: mov     dl,al                   ; display chars in word
        int     dos
        loop    cmskw1
        pop     si
cmskw3: mov     dx,offset cmer03        ; '" not usable here.'
        cmp     kwstat,1                ; kywd status from getkw, not found?
        jb      cmskw4                  ; b = not found, a = ambiguous
        mov     dx,offset cmer04        ; '" ambiguous'
cmskw4: mov     ah,prstr
        int     dos
        ret
cmskw   endp
;;;;;;;;;; end of support routines for keyword parsing.

; Parse arbitrary text up to a CR. Enter with BX = pointer to output buffer,
; DX pointing to help text. Produces asciiz string. Return updated pointer in
; BX and output size in AH. Leading spaces are omitted unless comand.cmwhite
; is non-zero (cleared upon exit). It does not need to be followed by the
; usual call to confirm the line. Byte comand.cmblen can be used to specify
; the length of the caller's buffer; cleared to zero by this command to
; imply a length of 127 bytes (default) and if zero at startup use 127 bytes.
cmtext  proc    near
        mov     comand.cmptab,bx        ; save pointer to data buffer
        mov     comand.cmhlp,dx         ; save the help message
        mov     cx,0                    ; init the char count
        cmp     comand.cmblen,0         ; length of user's buffer given?
        jne     cmtxt0                  ; ne = yes
        mov     comand.cmblen,127       ; else set 127 byte limit plus null
cmtxt0: mov     comand.cmsflg,0ffh      ; skip initial spaces
        cmp     comand.cmwhite,0        ; allow leading whitespace?
        je      cmtxt1a                 ; e = no
cmtxt1: mov     comand.cmsflg,0         ; get all spaces
cmtxt1a:call    cmgtch                  ; get a char
        jnc     cmtxt5                  ; nc = non-terminator, put in buffer
        cmp     ah,' '                  ; space?
        je      cmtxt5                  ; e = yes, record it
        cmp     ah,escape               ; escape?
        jne     cmtxt2                  ; ne = no
        call    esceoc                  ; do normal escape end-of-command
        jmp     cmtxt0                  ; try again

cmtxt2: cmp     ah,'?'                  ; asking a question?
        je      cmtxt3                  ; e = yes
        cmp     ah,cr                   ; formal carriage return?
        je      cmtxt2a                 ; e = yes
        inc     comand.cmrptr           ; accept char into buffer
        jmp     cmtxt5                  ;  and record the char
cmtxt2a:mov     ah,cl                   ; return count in AH
        mov     bx,comand.cmptab        ; return updated pointer
        mov     byte ptr[bx],0          ; put terminator into the buffer
        mov     comand.cmwhite,0        ; clear leading whitespace flag
        mov     comand.cmper,0          ; reset to variable recognition
        mov     comand.cmblen,0         ; set user buffer length to unknown
        mov     comand.cmkeep,0
        jmp     rskp
                                        ; Help processor
cmtxt3: inc     comand.cmrptr           ; count the ?
        cmp     cx,0                    ; Is "?" first char?
        jne     cmtxt5                  ; ne = no, just add to buffer
        dec     comand.cmrptr
        mov     comand.cmsiz,0          ; no keyword for help
        mov     comand.cmwhite,0        ; clear leading whitespace flag
        cmp     comand.cmhlp,0          ; external help given?
        jne     cmtxt3a                 ; ne = yes
        mov     comand.cmhlp,offset cmin00 ; confirm with c/r msg
        mov     comand.cmblen,0         ; set user buf length to unknown
cmtxt3a:jmp     cmkyhlp                 ; do help process

cmtxt5: inc     cx                      ; increment the count
        mov     bx,comand.cmptab        ; pointer into destination array
        mov     [bx],ah                 ; put char into the buffer
        inc     bx
        mov     al,0
        mov     [bx],al                 ; insert null terminator
        mov     comand.cmptab,bx
        cmp     ch,0                    ; overflowed into high byte?
        jne     cmtxt6                  ; ne = yes
        cmp     cl,comand.cmblen        ; buffer filled?
        ja      cmtxt6                  ; a = yes, declare error
        jb      cmtxt5a                 ; a = not filled yet
        mov     ah,conout               ; notify user that the buffer is full
        mov     dl,bell
        int     dos
cmtxt5a:jmp     cmtxt1
cmtxt6: mov     ah,prstr
        mov     dx,offset cmer09
        int     dos
        jmp     prserr                  ; declare parse error
cmtext  endp


; Parse arbitrary text up to whitespace.  Enter with DX pointing to output
; buffer and BX pointing to help text. Produces asciiz string. Return updated
; pointer in DX and input size in AH. Skips leading whitespace unless
; comand.cmwhite is non-zero (cleared upon exit). Does a return skip exit.

cmfil0  proc    near
        mov     comand.cmptab,dx        ; save pointer to data buffer
        mov     comand.cmhlp,bx         ; save the help message
        mov     comand.cmsiz,0          ; init the char count
cmfil0a:cmp     comand.cmwhite,0        ; allow leading whitespace?
        jne     cmfil1                  ; ne = yes
        mov     comand.cmsflg,0ffh      ; omit leading space
cmfil1: call    cmgtch                  ; get a char
        jc      cmfi1a                  ; c = terminator
        jmp     cmfil5                  ; put char into the buffer
cmfi1a: cmp     ah,escape               ; escape?
        je      cmfi1b                  ; e = yes
        jmp     cmfil2                  ; process other terminators

cmfi1b: call    esceoc                  ; do normal escape end-of-command
        jmp     cmfil0a                 ; try again
cmfil2: cmp     ah,'?'                  ; asking a question?
        je      cmfil3                  ; e = yes
        xchg    dx,bx                   ; re-interchange bx and dx
        mov     comand.cmwhite,0        ; clear whitespace flag
        mov     comand.cmper,0          ; reset to variable recognition
        mov     comand.cmkeep,0
        mov     bx,comand.cmptab        ; pointer into destination array
        mov     byte ptr[bx],0          ; put null terminator into the buffer
        inc     bx
        mov     ah,byte ptr comand.cmsiz ; return count in AH
        mov     dx,comand.cmptab        ; return updated pointer
        xchg    dx,bx                   ; re-interchange bx and dx
        mov     comand.cmwhite,0        ; clear whitespace flag
        mov     comand.cmper,0          ; reset to variable recognition
        mov     comand.cmkeep,0
        jmp     rskp                    ; return success

cmfil3: inc     comand.cmrptr           ; count the ?
        cmp     comand.cmsiz,0          ; Is "?" first char?
        jne     cmfil5                  ; ne = no, just add to buffer
        dec     comand.cmrptr
        mov     comand.cmsiz,0
        cmp     comand.cmhlp,0          ; external help given?
        jne     cmfil3a                 ; ne = yes
        mov     comand.cmhlp,offset cmin00 ; confirm with c/r msg
cmfil3a:jmp     cmkyhlp                 ; do help process

cmfil5: inc     comand.cmsiz            ; inrement the count
        mov     bx,comand.cmptab        ; pointer into destination array
        mov     [bx],ah                 ; put char into the buffer
        inc     bx
        mov     comand.cmptab,bx
        jmp     cmfil1                  ; the end of cmfil0
cmfil0  endp

; This routine gets a confirm (CR) and displays any extra non-blank text.
; errflag non-zero means suppress "extra text" display in this routine
; because another routine is handling errors.
cmcfrm  proc    near
        mov     comand.cmper,1          ; do not react to \%x substitutions
cmcfr1: mov     comand.cmsflg,0ffh      ; set space-seen flag (skip spaces)
        call    cmgtch                  ; get a char
        push    comand.cmrptr
        pop     temp                    ; remember first non-space position
        jc      cmcfr4                  ; c = terminator
        dec     temp                    ; backup to text char
cmcfr3: mov     comand.cmsflg,0ffh      ; set space-seen flag (skip spaces)
        call    cmgtch
        jnc     cmcfr3                  ; read until terminator
cmcfr4: cmp     ah,' '
        je      cmcfr3                  ; ignore ending on space
        cmp     ah,escape               ; escape?
        jne     cmcfr5                  ; ne = no
        call    esceoc                  ; do standard end of cmd on escape
        mov     ax,comand.cmrptr
        cmp     ax,temp                 ; started text yet?
        je      cmcfr1                  ; e = no
        jmp     short cmcfr3            ; try again
cmcfr5: cmp     ah,'?'                  ; curious?
        jne     cmcfr6                  ; ne = no
        mov     comand.cmhlp,offset cmin00 ; msg Confirm with c/r
        mov     comand.cmsiz,0          ; no keyword
        mov     errflag,0
        jmp     cmkyhlp                 ; do help
cmcfr6: cmp     ah,cr                   ; the confirmation char?
        jne     cmcfr3                  ; ne = no
        cmp     errflag,0               ; already doing one error?
        jne     cmcfr7                  ; ne = yes, skip this one
        mov     cx,comand.cmrptr        ; pointer to terminator
        mov     dx,temp                 ; starting place
        sub     cx,dx                   ; end minus starting point = length
        cmp     cx,0                    ; string present?
        jle     cmcfr7                  ; le = nothing to display
        push    dx                      ; save source pointer
        mov     ah,prstr
        mov     dx,offset cmer07        ; ?Ignoring extras
        int     dos
        pop     dx
        mov     bx,1                    ; stdout handle, cx=count, dx=src ptr
        mov     ah,write2               ; allow embedded dollar signs
        int     dos
        mov     ah,prstr
        mov     dx,offset cmer08        ; trailer msg
        int     dos
cmcfr7: mov     errflag,0
        mov     comand.cmper,0          ; reset to variable recognition
        mov     comand.cmkeep,0
        jmp     rskp                    ; return confirmed
cmcfrm  endp

;;; Routines to get and edit incoming text.

; Detect '\%x' (x = '0' or above) and substitute the matching Macro string
; in place of the '\%x' phrase in the user's buffer. If comand.cmper != 0
; then treat '\%' as literal characters. If no matching parameter exists
; just remove '\%x'. Returns carry clear if nothing done, else carry set and
; new text already placed in user's buffer. comand.cmwptr and comand.cmcnt
; are updated. Uses depth-first recursion algorithm. All registers preserved.
subst   proc    near
        cmp     comand.cmper,0          ; should we recognize '\%'?
        jne     subst2                  ; ne = no, treat as literals
        cmp     ah,'\'                  ; is it the first char of the pattern?
        jne     subst1                  ; ne = no, try next
        mov     subcnt,1                ; say first is matched
        jmp     short subst2            ; exit successfully
subst1: cmp     subcnt,1                ; first char matched already?
        ja      subst3                  ; a = first two have been matched
        jb      subst2                  ; b = none yet
        inc     subcnt                  ; assume a match follows
        cmp     ah,'%'                  ; second match char, same?
        je      subst2                  ; e = yes
        mov     subcnt,0                ; mismatch, clear match counter
subst2: clc                             ; carry clear = no substitution done
        ret
subst3: mov     subcnt,0                ; clear match counter
        cmp     ah,'0'                  ; third char is '0' or above?
        jb      subst2                  ; b = out of range, no match
        push    bx                      ; save working regs
        push    cx
        sub     comand.cmrptr,3         ; reread commands where backslash was
        call    bufreset                ; reset buffer to this point
        push    comand.cmptab           ; save current keyword parsing parms
        push    comand.cmsptr
        push    comand.cmsiz
        mov     bx,comand.cmrptr        ; points at backslash
        mov     comand.cmsptr,bx        ; direct keyword routine to it
        mov     comand.cmsiz,3          ; three bytes (\%x) of user text
        mov     comand.cmptab,offset mcctab ; use Macro table for new text
        call    getkw                   ; get ptr, bx, to matching keyword
        pop     comand.cmsiz            ; restore borrowed keyword parameters
        pop     comand.cmsptr
        pop     comand.cmptab
        jc      substx                  ; c = not found, keep after pops
        mov     cl,byte ptr[bx]         ; length of found word
        add     cl,2                    ; plus count field and '$'
        mov     ch,0
        add     bx,cx                   ; point to 16 bit value (string ptr)
        mov     bx,[bx]                 ; point to string structure
        mov     cl,[bx]                 ; length of string (ch=0 from above)
        inc     bx                      ; skip length byte, bx=string address
        jcxz    substx                  ; z = nothing left to transfer
        cld
subst4: mov     ah,[bx]                 ; get a char
        inc     bx
        push    di                      ; assume di is used by other routines
        mov     di,comand.cmrptr
        mov     [di],ah                 ; store char (without es:di)
        inc     di
        mov     comand.cmwptr,di        ; where to store next char
        mov     comand.cmrptr,di        ; move read pointer too
        cmp     di,offset comand.cmdbuf+size cmdbuf ; reached max buffer size?
        pop     di
        jae     subst6                  ; ae = yes, no more room
        cmp     sp,20*2                 ; still some stack space?
        jle     subst6                  ; le = insufficient room
        call    SUBST                   ; rescan what we stored (recursion)
        jnc     subst5                  ; nc = no substitution, so no kbd test
        push    ax                      ; break out of loops with Control-C
        push    dx
        mov     ah,constat              ; check console status for Control-C
        int     dos                     ;  our control-break handler sees it
        pop     dx                      ;  and cmgetc reads it
        pop     ax
subst5: loop    subst4
substx: pop     cx
        pop     bx
        stc                             ; set carry to say have stored chars
        ret
subst6: mov     ah,prstr
        mov     dx,offset stkmsg        ; out of work space msg
        int     dos
        jmp     prserr                  ; and declare parse error
subst   endp

; Read chars from Take file, keyboard, or redirected stdin. Edit and remove
; BS & DEL, Tab becomes space, act on Control-C, pass Control-U and Control-W.
; Do echoing unless comand.cmquiet is non-zero. Do semicolon comments in Take
; and indirect stdin files (\; means literal semicolon). Return char in AL.
CMGETC  proc    near                    ; Basic raw character reader
        mov     ah,taklev               ; get current Take level
        mov     intake,ah               ; remember here for later callers
cmget01:cmp     prevch,0                ; left over char yet to be exported?
        je      cmget02                 ; e = no
        mov     al,prevch               ; get old char
        mov     prevch,0                ; clear storage
        jmp     cmget6                  ; analyze it
cmget02:cmp     taklev,0                ; in a Take file?
        jne     cmget1                  ; ne = yes, do Take reader section
        call    isdev                   ; is stdin a device or a file?
        jnc     cmget20                 ; nc = file (redirection of stdin)
        jmp     cmget10                 ; c = device, do separately

cmget20:call    iseof                   ; see if file is empty
        jc      cmget21                 ; c = EOF on disk file
        mov     ah,coninq               ; read the char from file, not device
        int     dos
        cmp     al,cr                   ; is it a cr?
        je      cmget01                 ; yes, ignore and read next char
        cmp     al,ctlz                 ; Control-Z?
        je      cmget21                 ; e = yes, same as EOF here
        cmp     al,lf                   ; LF's end lines from disk files
        jne     cmget23                 ; ne = not LF, pass along as is
        mov     al,cr                   ; make LF a CR for this parser
        call    iseof                   ; see if this is the last char in file
        jnc     cmget23                 ; nc = not EOF, process new CR
cmget21:mov     flags.extflg,1          ; EOF on disk file, set exit flag
cmget23:jmp     short cmget6            ; do echoing and return

cmget1: push    bx                      ; read from Take file
        mov     bx,takadr
        cmp     [bx].takcnt,0           ; bytes remaining in Take buffer
        jne     cmget4                  ; ne = not empty
        cmp     [bx].taktyp,0feh        ; type of Take (file?)
        jne     cmget3                  ; ne = no (macro)
        call    takrd                   ; read another buffer
        cmp     [bx].takcnt,0           ; anything in the file?
        jne     cmget4                  ; ne = yes
cmget3: pop     bx                      ; clear stack
        jmp     cmget5                  ; close take file

cmget4: push    si
        mov     si,[bx].takptr          ; read a char from Take buffer
        cld
        lodsb
        mov     [bx].takptr,si          ; move buffer pointer
        pop     si
        dec     [bx].takcnt             ; decrease number of bytes remaining
        pop     bx
        cmp     al,ctlz                 ; Control-Z?
        jne     cmget6                  ; ne = no
cmget5: cmp     comand.cmkeep,0         ; keep Take/macro open after eof?
        jne     cmget5a                 ; ne = yes
        call    takclos                 ; close take file
cmget5a:mov     al,cr                   ; report cr as last char
        mov     noparse,0               ; and say end of comment
                                        ; start common code
cmget6: cmp     al,lf                   ; line feed?
        jne     cmget8                  ; ne = no
        jmp     cmget01                 ; yes, ignore and read another char
                                        ; handle comments (echo but not parse)
cmget8: cmp     noparse,0               ; parsing?
        jne     cmget9                  ; ne = yes, do echo and no parse
        cmp     prevch,0                ; have previous char to analyze?
        jne     cmget8c                 ; ne = yes
        cmp     al,'\'                  ; start of '\;'?
        jne     cmget8a                 ; ne = no
        mov     prevch,al               ; yes, maybe. save '\' til later
        jmp     cmget02                 ; read next char
cmget8a:cmp     al,';'                  ; possible start of comment?
        jne     cmget8e                 ; no, export al
        mov     al,' '                  ; replace ';' with space for comment
        jmp     cmget9                  ; go start a comment

cmget8c:cmp     al,';'                  ; end of '\;'?
        je      cmget8d                 ; e = yes, omit leading backslash
        xchg    prevch,al               ; no, save new, recover old '\'
        jmp     short cmget8e           ; export '\'
cmget8d:mov     prevch,0                ; clear old '\'
cmget8e:jmp     cmget12                 ; do parsing
        mov     prevch,0                ; clear previous char
        jmp     cmget02                 ; get next char

                                        ; echo comment
cmget9: cmp     flags.takflg,0          ; echoing take files?
        je      cmget9a                 ; e = no
        push    ax
        push    dx
        mov     ah,conout               ; echo current char
        mov     dl,al
        int     dos
        pop     dx
        pop     ax
cmget9a:mov     noparse,0               ; cr ends comment
        cmp     al,cr                   ; end of comment?
        je      cmget13                 ; e = yes, export cr
        mov     noparse,1               ; still in comment
        jmp     cmget01                 ; read more chars

                                        ; read from tty device
cmget10:mov     ah,coninq               ; Get a char from device, not file
        int     dos                     ;  with no echoing
        or      al,al
        jnz     cmget12                 ; ignore null bytes of special keys
        int     dos                     ; read and discard scan code byte
        jmp     cmget10                 ; try again

cmget12:cmp     al,'C'and 1Fh           ; Control-C?
        je      cmget14                 ; e = yes
        cmp     al,TAB                  ; tab is replaced by space
        jne     cmget13                 ; ne = not tab
        mov     al,' '
cmget13:ret                             ; normal exit, char is in AL

cmget14:mov     ah,prstr                ; Control-C handler
        push    dx
        mov     dx,offset ctcmsg        ; show Control-C
        int     dos
        pop     dx
        mov     prevch,0
        mov     flags.cxzflg,'C'        ; tell others the news
        mov     sp,cmdstk               ; restore command entry stack pointer
        ret                             ;  and fail immediately (a longjmp)
cmgetc  endp

; Read chars from user (cmgetc). Detect terminators. Reads from buffer
; comand.cmbuf. Set read pointer comand.cmrptr to next free buffer byte if
; char is not a terminator: chars CR, LF, FF, '?' (returns carry set for
; terminators). Do ^U, ^W editing, convert FF to CR plus clear screen.
; Edit "-<cr>" as line continuation, "\-<cr>" as "-<end of line>".
; Return char in AH.
CMINBF  proc    near                    ; Buffer reader, final editor
        push    di
        mov     di,comand.cmwptr
        cmp     di,offset comand.cmdbuf+size cmdbuf-3 ; max buffer size - 3
        pop     di
        jb      cminb1                  ; b = not full
        mov     ah,conout               ; almost full, notify user
        push    dx
        mov     dl,bell
        int     dos
        mov     dx,offset comand.cmdbuf+size cmdbuf
        cmp     comand.cmrptr,dx        ; max buffer size?
        pop     dx
        jb      cminb1                  ; b = more room
        mov     ah,prstr
        push    dx
        mov     dx,offset cmer09        ; command too long
        int     dos
        pop     dx
        jmp     prserr                  ; overflow = parse error

cminb1: push    bx
        mov     bx,comand.cmrptr
        mov     ah,[bx]                 ; get current command char while here
        cmp     bx,comand.cmwptr        ; do we need to read more?
        pop     bx                      ; no if cmrptr < cmwptr
        jb      cminb2                  ; b: cmrptr < cmwptr (have extra here)
        call    cmgetc                  ; no readahead, read another into al
        mov     ah,al                   ; keep char in 'ah'
        push    bx
        mov     bx,comand.cmwptr        ; Get the pointer into the buffer
        mov     [bx],ah                 ; Put it in the buffer
        inc     bx
        mov     comand.cmwptr,bx        ; inc write pointer
        pop     bx
                                        ; Char to be delivered is in ah
cminb2: cmp     ah,'W' and 1fh          ; Is it a ^W?
        jne     cminb3
        call    cntrlw                  ; Kill the previous word
        jnc     cminbf                  ; nc = no change, get another char
        jmp     repars                  ; need a new command scan (cleans stk)

cminb3: cmp     ah,'U' and 1fh          ; Is it a ^U?
        jne     cminb3a                 ; ne = no
        mov     comand.cmwptr,offset comand.cmdbuf ;reset buffer write pointer
        jmp     repars                  ; Go start over (cleans stack)
                                        ; BS and DEL
cminb3a:cmp     ah,DEL                  ; Delete code?
        je      cminb3b                 ; e = yes
        cmp     ah,BS                   ; Backspace (a delete operator)?
        jne     cminb4                  ; ne = no
cminb3b:call    bufdel                  ; delete char from buffer
        jc      cminb3c                 ; c = did erasure
        jmp     cminbf                  ; no erasure, ignore BS, get more
cminb3c:jmp     repars                  ; could have deleted previous token

cminb4: push    bx                      ; look for hyphen or \hyphen
        cmp     ah,cr                   ; check for hyphen line continuation
        jne     cminb4b                 ; ne = not end of line
        mov     bx,comand.cmwptr        ; Get the pointer into the buffer
        cmp     bx,offset comand.cmdbuf-2 ; do we have a previous char?
        jb      cminb4b                 ; b = no
        cmp     byte ptr[bx-2],'-'      ; previous char was a hyphen?
        jne     cminb4b                 ; ne = no
        pop     bx
        call    bufdel                  ; delete the hyphen
        jmp     repars
cminb4b:pop     bx
                                        ; Echoing done here
        cmp     comand.cmquiet,0        ; quiet mode?
        jne     cminb5                  ; yes, skip echoing
        cmp     taklev,0                ; in a take file?
        je      cminb4a                 ; e = no
        cmp     flags.takflg,0          ; echo take file?
        je      cminb5                  ; e = no
cminb4a:push    ax                      ; save the char
        cmp     ah,' '                  ; printable?
        jae     cminb4c                 ; yes, no translation needed
        cmp     ah,cr                   ; this is printable
        je      cminb4c
        cmp     ah,lf
        je      cminb4c
        cmp     ah,escape               ; escape?
        je      cminb4d                 ; do not echo this character
        push    ax                      ; show controls as caret char
        push    dx
        mov     dl,5eh                  ; caret
        mov     ah,conout
        int     dos
        pop     dx
        pop     ax
        add     ah,'A'-1                ; make control code printable
cminb4c:push    dx
        mov     dl,ah
        mov     ah,conout
        int     dos                     ; echo it ourselves
        pop     dx
cminb4d:pop     ax                      ; and return char in ah

cminb5: cmp     ah,cr                   ; Is it a carriage return?
        je      cminb6
        cmp     ah,lf                   ; Is it a line feed?
        je      cminb6
        cmp     ah,ff                   ; Is it a formfeed?
        jne     cminb7                  ; none of the above, report bare char
        call    cmblnk                  ; FF: clear the screen and
        push    bx
        push    cx
        push    dx
        call    locate                  ; Home the cursor
        mov     bx,comand.cmwptr        ; make the FF parse like a cr
        mov     byte ptr [bx-1],cr      ; pretend a carriage return were typed
        pop     dx
        pop     cx
        pop     bx
cminb6: cmp     comand.cmwptr,offset comand.cmdbuf ; parsed any chars yet?
        jne     cminb7                  ; ne = yes
        cmp     comand.cmcr,0           ; bare cr's allowed?
        jne     cminb7                  ; ne = yes
        jmp     prserr                  ; If not, just start over
cminb7: clc
        ret
cminbf  endp

; Read chars from cminbf. Comand.cmrptr points to next char to be read.
; Compresses repeated spaces if comand.cmsflg is non-zero. Exit with
; comand.cmrptr pointing at a terminator or otherwise at next free slot.
; Non-space then space acts as a terminator but comand.cmrptr is incremented.
; Substitution variables, '\%x', are detected and expanded. Return char in AH.

CMGTCH  proc    near                    ; return char in AH, from rescan buf
        call    cminbf                  ; get char from buffer or user
        push    bx
        mov     bx,comand.cmrptr        ; get read pointer into the buffer
        mov     ah,[bx]                 ; read the next char
        inc     bx
        mov     comand.cmrptr,bx        ; where to read next time
        pop     bx
        call    subst                   ; examine for text substitution
        jnc     cmgtc1                  ; nc = no substitutions done
        jmp     repars                  ; reparse line with new material

cmgtc1: cmp     ah,' '                  ; Is it a space?
        jne     cmgtc3                  ; ne = no
cmgtc2: cmp     comand.cmsflg,0         ; space flag, was last char a space?
        jne     cmgtch                  ; ne = yes, get another char
        mov     comand.cmsflg,0FFH      ; Set the space(s)-seen flag
        mov     ah,' '                  ; character for caller
        stc                             ; set carry for terminator
        ret                             ; return space as a terminator
cmgtc3: mov     comand.cmsflg,0         ; clear the space-seen flag
        cmp     ah,escape               ; terminators remain in buffer but
        je      cmgtc4                  ;  are ready to be overwritten
        cmp     ah,'?'                  ; Is the user curious?
        jne     cmgtc3a                 ; ne = no
        cmp     taklev,0                ; in a Take file?
        jne     cmgtc3b                 ; ne = yes, make query ordinary char
        je      cmgtc4
cmgtc3a:cmp     ah,cr
        je      cmgtc4
        cmp     ah,lf
        je      cmgtc4
        cmp     ah,ff
        je      cmgtc4
cmgtc3b:clc                             ; carry clear for non-terminator
        ret
cmgtc4: dec     comand.cmrptr           ; point at terminating char
        stc                             ; set carry to say it is a terminator
        ret
cmgtch  endp

; Reset comand.cmdbuf write pointer (.cmwptr) to where the read pointer
; (.cmrptr) is now. Discards material not yet read.
bufreset proc   near
        push    comand.cmrptr           ; where next visible char is read
        pop     comand.cmwptr           ; where new char goes in buffer
        ret
bufreset endp

; Delete character from screen and adjust buffer. Returns carry clear if
; no erasure, carry set otherwise.
bufdel  proc    near
        dec     comand.cmrptr           ; remove previous char from buffer
        cmp     comand.cmrptr,offset comand.cmdbuf ; back too far?
        jae     bufde2                  ; ae = no, material can be erased
        mov     comand.cmrptr,offset comand.cmdbuf ; set to start of buffer
        call    bufreset                ; reset buffer
        clc                             ; say no erasure
        ret
bufde2: call    bufreset                ; reset buffer
        stc                             ; say did erasure
        ret
bufdel  endp

; Come here is user types ^W when during input. Remove word from buffer.
cntrlw  proc    near
        push    ax
        push    cx
        push    dx
        mov     cx,comand.cmrptr        ; char beyond what user sees
        mov     comand.cmwptr,cx        ; truncate buffer there
        sub     cx,offset comand.cmdbuf ; compute chars in buffer
        clc                             ; say have not yet modified line
        jcxz    ctlw2                   ; z = nothing to do, exit no-carry
        push    es
        std                             ; scan backward
        mov     ax,ds
        mov     es,ax                   ; point to the data are
        mov     di,comand.cmwptr        ; looking from here
        dec     di
        mov     al,' '
        repe    scasb                   ; look for non-space
        je      ctlw1                   ; all spaces, nothing to do
        inc     di                      ; move back to non-space
        inc     cx
        repne   scasb                   ; look for a space
        jne     ctlw1                   ; no space, leave ptrs alone
        inc     di
        inc     cx                      ; skip back over space
ctlw1:  inc     di
        pop     es
        cld                             ; reset direction flag
        mov     comand.cmwptr,di        ; update pointer
        stc                             ; set carry to say modified line
ctlw2:  pop     dx
        pop     cx
        pop     ax
        ret
cntrlw  endp

; Jump to REPARS to do a rescan of the existing buffer.
; Jump to PRSERR on a parsing error (quits command, clears old read material)

PRSERR  PROC NEAR
        mov     comand.cmwptr,offset comand.cmdbuf ; initialize write pointer
        mov     ah,prstr
        mov     dx,offset crlf          ; leave old line, start a new one
        int     dos
                                        ; reparse current line
REPARS: mov     comand.cmrptr,offset comand.cmdbuf ; reinit read pointer
        mov     comand.cmper,0          ; reset to variable recognition
        mov     comand.cmsflg,0FFH      ; strip leading spaces
        cmp     taklev,0                ; in Take cmd?
        je      prser2                  ; e = no
        cmp     flags.takflg,0          ; echo contents of Take file?
        je      prser3                  ; e = no
prser2: call    ctlu                    ; clear display's line, reuse it
        mov     ah,prstr
        mov     dx,comand.cmprmp        ; display the prompt
        int     dos
prser3: mov     bx,0ffffh               ; returned keyword value
        mov     sp,comand.cmostp        ; set new sp to old one
        jmp     comand.cmrprs           ; jump to just before the prompt call
PRSERR  ENDP

; This routine prints the prompt and specifies the reparse address.
; Enter with pointer to prompt string in dx.
PROMPT  PROC  NEAR
        mov     comand.cmprmp,dx        ; save the prompt
        pop     ax                      ; Get the return address
        mov     comand.cmrprs,ax        ; Save as address to go to on reparse
        mov     comand.cmostp,sp        ; Save for later restoration
        push    ax                      ; Put it on the stack again
        mov     ax,offset comand.cmdbuf
        mov     comand.cmwptr,ax        ; reset buffer read/write pointers
        mov     comand.cmrptr,ax
        mov     ax,0
        mov     comand.cmper,0          ; allow substitutions
        mov     comand.cmsflg,0FFH      ; remove leading spaces
        cmp     flags.takflg,0          ; look at Take flag
        jne     promp1                  ; ne=supposed to echo, skip this check
        cmp     taklev,0                ; inside a take file?
        je      promp1                  ; no, keep going
        ret                             ; yes, return
promp1: mov     ah,prstr
        mov     dx,offset crlf
        int     dos
        mov     ah,prstr                ; display the prompt
        mov     dx,comand.cmprmp
        int     dos
        ret
PROMPT  ENDP

ISDEV   PROC    NEAR                    ; Set carry if STDIN is non-disk
        push    ax
        push    bx
        push    dx
        mov     al,0                    ; get device info
        mov     ah,ioctl
        mov     bx,0                    ; handle 0 is stdin
        int     dos
        and     dl,80h                  ; bit set if handle is for a device
        rcl     dl,1                    ; put it into the carry bit
        pop     dx
        pop     bx
        pop     ax
        ret                             ; carry set if device
ISDEV   ENDP

ISEOF   PROC    NEAR                    ; Set carry if STDIN is at EOF
        push    ax                      ;  but only if stdin is a non-device
        push    bx
        push    dx
        mov     al,0                    ; get device info
        mov     ah,ioctl
        mov     bx,0                    ; handle 0 is stdin
        int     dos
        test    dl,80h                  ; bit set if handle is for a device
        mov     ah,ioctl
        mov     al,6                    ; get handle input status, set al
        jnz     iseof1                  ; nz = device, always ready (al != 0)
        int     dos
iseof1: or      al,al                   ; EOF?
        pop     dx
        pop     bx
        pop     ax
        jnz     iseof2                  ; nz = no
        stc                             ; set carry for eof
        ret
iseof2: clc                             ; clear carry for not-eof
        ret
ISEOF   ENDP
; Convert ascii characters in al and ah to lowercase. [jrd]
; All registers are preserved except AX, of course.

TOLOWR PROC NEAR
        cmp     ah,'A'                  ; less that cap A?
        jl      tolow1                  ; l = yes. leave untouched
        cmp     ah,'Z'+1                ; more than cap Z?
        jns     tolow1                  ; ns = yes
        or      ah,20H                  ; convert to lowercase
tolow1: cmp     al,'A'                  ; less that cap A?
        jl      tolow2                  ; l = yes. leave untouched
        cmp     al,'Z'+1                ; more than cap Z?
        jns     tolow2                  ; ns = yes
        or      al,20H                  ; convert to lowercase
tolow2: ret
TOLOWR  endp

; Jumping to this location is like retskp.  It assumes the instruction
;   after the call is a jmp addr.

RSKP    PROC    NEAR
        pop     bp
        add     bp,3
        push    bp
        ret
RSKP    ENDP

; Jumping here is the same as a ret.

R       PROC    NEAR
        ret
R       ENDP

code    ends
        end