        NAME    msscmd
; File MSSCMD.ASM
; Edit history:
; Last edit 1 Jan 1988
; 1 Jan 1988 version 2.30
; 8 Oct 1987 Add syntax of "\;" is a literal semicolon and not a comment
;  introducer (; prints as space, \; prints as ;) but only when doing a
;  TAKE file or macro. [jrd]
; 28 Aug 1987 Show Kermit prompt when in Take file and echoing Take cmd. [jrd]
; 18 Aug 1987 Change ESC to escape, for MASM 4.5+ [jrd]
; 27 June 1987 Add allowance for bare c/r's (comand.cmcr != 0) in cminbf.
;  Add global byte "intake" to say if reading from Take file (non-zero). [jrd]
; 1 Oct 1986 Version 2.29a
; 20 August 1986 Protect against buffer overflow and stop deleting of prompt
; 16 August 1986 Remove unneeded buffer tbuff. [jrd]
; 22 May 1986 Echo tabs as just a space, avoids corrupted cmd line. [jrd]
; [2.29] code frozen on 6 May 1986 [jrd]

        public comnd, cmcfrm, prserr, repars, cmgtch, comand
        public cmgetc, intake
        include mssdef.h

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

comand  cmdinfo <>
cmer00  db      cr,lf,'?Program error   Invalid COMND call$'
cmer03  db      cr,lf,'?Invalid command$'
cmer04  db      cr,lf,'?Invalid command or operand$'
cmin00  db      ' Confirm with carriage return$'
cmin01  db      ' One of the following:',cr,lf,'$'

cmthlp  dw      0                       ; Text ptr to help message for cmds
crlf    db      cr,lf,'$'
ctcmsg  db      '^C$'
prsp    db      ' $'                    ; Print a space.
hlpmsg  dw      0                       ; Address of help message.
escspc  db      BS,' ',BS,'$'           ; Clear escape.
clrspc  db      ' ',BS,'$'              ; Clear space.
temp    db      ?                       ; temp (counts char/line so far)
cmdstk  dw      ?
intake  db      ?                       ; last command line was from Take
prevch  db      0                       ; previous char read by cmgetc
noparse db      0                       ; semicolons not special, if non-zero
datas   ends

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

;       This routine parses the specified function in AH. Any additional
;       information is in DX and BX.
;       Returns +1 on success
;               +4 on failure (assumes a JMP follows the call)

CMND    PROC NEAR
comnd:  mov     comand.cmstat,ah        ; Save what we are presently parsing.
        mov     cmdstk,sp               ; save stack ptr locally.
        mov     prevch,0                ; clear old previous char
        mov     noparse,0         ; say recognize semicolons in Take files
        call    cminbf            ; Get chars until an action or a erase char
        mov     ah,comand.cmstat        ; Restore 'ah' for upcoming checks.
        cmp     ah,cmcfm                ; Parse a confirm?
        jz      cmcfrm                  ; Go get one.
        cmp     ah,cmkey                ; Parse a keyword?
        jnz     cm3
        jmp     cmkeyw                  ; Try and get one.
cm3:    cmp     ah,cmtxt                ; Parse arbitrary text.
        jnz     cm4
        jmp     cmtext
cm4:    cmp     ah,cmfile               ; parse text surrounded by whitespace
        jnz     cm5
        jmp     cmfil0
cm5:    mov     ah,prstr                ; Else give error
        mov     dx,offset cmer00        ; "?Unrecognized COMND call"
        int     dos
        ret

; This  routine gets a confirm.

cmcfrm: call    cmgtch                  ; Get a char.
        cmp     ah,0            ; Is it negative? (a terminator; a space or
                                ; a tab will not be returned here as they
                                ; will be seen as leading white space.)
        js      cmcfr0
        mov     comand.cmstat,cmcfm-1   ; make sure status isn't = cmcfm
        ret                             ; If not, return failure.
cmcfr0: and     ah,7FH                  ; Turn off the minus bit.
        cmp     ah,escape               ; Is it an escape?
        jne     cmcfr2
        mov     ah,conout
        mov     dl,bell                 ; Get a bell.
        int     dos
        mov     comand.cmaflg,0         ; Turn off the action flag.
        mov     bx,comand.cmcptr        ; Move pointer to before the escape.
        dec     bx
        mov     comand.cmcptr,bx
        mov     comand.cmdptr,bx
        dec     comand.cmccnt           ; Decrement the char count.
        jmp     cmcfrm                  ; Try again.
cmcfr2: cmp     ah,'?'                  ; Curious?
        jne     cmcfr3
        mov     ah,prstr                ; Print something useful.
        mov     dx,offset cmin00
        int     dos
        mov     dx,offset crlf          ; Print a crlf.
        int     dos
        mov     dx,comand.cmprmp        ; Reprint the prompt.
        int     dos
        mov     bx,comand.cmdptr        ; Get the pointer into the buffer.
        mov     byte ptr[bx],'$'        ; Put a $ there for printing.
        dec     comand.cmcptr           ; Decrement & save the buffer pointer
        mov     dx,offset comand.cmdbuf
        int     dos
        mov     comand.cmaflg,0         ; Turn off the action flag.
        jmp     repars                  ; Reparse everything.

cmcfr3: cmp     ah,ff                   ; Is it a form feed?
        jne     cmcfr4
        call    cmblnk                  ; If so blank the screen.
cmcfr4: jmp     rskp

;       This routine parses a keyword from the table pointed
;       to in DX.  The format of the table is as follows:
;
;       addr:   db      n         ; Where n is the # of entries in the table.
;               db      m         ; M is the size of the keyword.
;               db      'string$' ; Where string is the keyword.
;               dw      ab        ; Where ab is data to be returned.
;
;       The keywords must be in alphabetical order,
;       but they may be of mixed case. [jrd]

cmkeyw: mov     comand.cmhlp,bx         ; Save the help.
        mov     comand.cmptab,dx        ; Save the beginning of keyword table
        mov     bx,dx
        mov     ch,[bx]                 ; Get number of entries in table.
        inc     bx
        mov     dx,comand.cmdptr        ; Save command pointer.
        mov     comand.cmsptr,dx        ; Save pointer's here.
cmky1:  cmp     ch,0                    ; Any commands left to check?
        jne     cmky2
        jmp     cmky41                  ; no, go complain
cmky2:  dec     ch
        mov     cl,0            ; Keep track of how many chars read in so far
        call    cmgtch                  ; Get a char from the user
        cmp     ah,0                    ; Do we have a terminator?
        jns     cmky2x                  ; ns = no
        jmp     cmky4                   ; Negative number means we do.
cmky2x: inc     bx                      ; Point to first letter of keyword.
        inc     cl                      ; count the user's char
        mov     al,[bx]
        call    tolowr                  ; convert al (key) and ah (user)
        cmp     ah,al                   ; do they match?
        je      cmky3                   ; e = yes
        jg      cmky2y                  ; g = keychar < user's (keep looking)
        jmp     cmky41                ; Fail if ah preceeds al alphabetically
cmky2y: jmp     cmky6                   ; Not this keyword - try the next.
cmky3:  inc     bx                      ; We match here, how 'bout next char?
        mov     al,[bx]
        call    tolowr                  ; lower case the key word char
        cmp     al,'$'                  ; End of keyword?
        jne     cmky3x                  ; ne = not yet
        jmp     cmky7                   ; Succeed.
cmky3x: mov     dl,al                   ; Save al's char here.
        call    cmgtch
        inc     cl                      ; Read in another char.
        mov     al,dl
        call    tolowr                  ; lower case
        cmp     ah,escape+80H   ; Escape Recognition (escape w/minus bit on)?
        je      cmky3y
        cmp     ah,' '+80H              ; A space?
        je      cmky3y
        cmp     ah,cr+80H               ; Carriage return?
        je      cmky3y
        cmp     ah,'?'+80H              ; A question mark?
        je      cmky3y
        cmp     ah,al                   ; user = keyword char?
        je      cmky3                   ; e = yes. Check next letter
        jmp     cmky6           ; no. Go to end of keyword and try next

cmky3y: mov     comand.cmkptr,bx        ; Save bx here.
        mov     comand.cmsiz,cx         ; Save size info.
        mov     comand.cmchr,ah         ; Save char for latter.
        cmp     ah,'?'+80h              ; question mark?
        je      cmkyj1                  ; yes, always print possibilities
        call    cmambg                  ; See if input is ambiguous or not.
         jmp    cmky32                  ; Succeeded (not ambiguous).
        mov     ah,comand.cmchr
        cmp     ah,escape+80H           ; Escape?
        je      cmky3z
        jmp     cmky41                  ; Else fail.
cmky3z: mov     ah,conout               ; Ring a bell.
        mov     dl,bell
        int     dos
        mov     bx,comand.cmcptr        ; Move pointer to before the escape.
        dec     bx
        mov     comand.cmcptr,bx
        mov     comand.cmdptr,bx
        dec     comand.cmccnt           ; Decrement char count.
        mov     bx,comand.cmkptr        ; Failed - pretend user never typed
        mov     cx,comand.cmsiz         ; ... in a char.
        dec     cl                      ; Don't count the escape.
        dec     bx
        mov     comand.cmaflg,0         ; Reset the action flag.
        jmp     cmky3                   ; Keep checking.

; Question mark entered by user.  Print out all the keywords that match
cmkyj1: mov     dx,offset cmin01
        mov     ah,prstr
        int     dos
        mov     temp,0                  ; count # chars printed on this line
        mov     bx,comand.cmkptr        ; this is current keyword
        mov     cx,comand.cmsiz         ; we are cl chars into it
        mov     ch,0
        sub     bx,cx                   ; back up to beginning
        inc     bx                      ; not counting ?
        mov     comand.cmkptr,bx        ; save beginning of kw
cmkyj2: mov     bx,comand.cmkptr        ; start of keyword
        dec     bx
        mov     al,[bx]                 ; its length
        add     temp,al                 ; count chars printed so far
        cmp     temp,76                 ; will this take us beyond column 78?
        jle     cmkyj3                  ; le = ok so far
        mov     ah,prstr                ; print string
        mov     dx,offset crlf          ; break the line
        int     dos
        mov     temp,0                  ; and reset the count
cmkyj3: mov     dl,spc                  ; put two spaces before each keyword
        mov     ah,conout
        int     dos
        inc     temp                    ; count output chars
        int     dos
        inc     temp
        mov     dx,comand.cmkptr        ; get current keyword
        mov     ah,prstr
        int     dos                     ; print it
        mov     bx,comand.cmkptr        ; get keyword back
        dec     bx
        mov     al,[bx]                 ; get length
        mov     ah,0
        add     ax,5                    ; skip length, $, value, next length
        add     bx,ax                   ; this is next keyword
        mov     si,bx
        mov     di,comand.cmkptr        ; compare with last keyword
        mov     comand.cmkptr,bx        ; update this
        mov     cx,comand.cmsiz
        dec     ch                      ; are we at end of table?
        jl      cmkyj4                  ; l = yes, don't go on
        mov     comand.cmsiz,cx         ; else update count
        mov     ch,0
        dec     cl                      ; this includes ?
        jcxz    cmkyj2                  ; empty, just print it
        repe    cmpsb                   ; compare to previous string
        je      cmkyj2                  ; same, go print this one
cmkyj4: jmp     cmky50                  ; else go finish up

cmky32: mov     cx,comand.cmsiz         ; Restore info.
        mov     bx,comand.cmkptr        ; Our place in the keyword table.
        cmp     comand.cmchr,' '+80H    ; Space?
        je      cmky35
        cmp     comand.cmchr,'?'+80H    ; Question mark?
        je      cmky35
        cmp     comand.cmchr,cr+80H     ; Carriage return?
        je      cmky35
        dec     comand.cmcptr           ; Pointer into buffer of input.
        mov     dx,comand.cmcptr
cmky33: mov     ah,[bx]                 ; Get next char in keyword.
        call    tolowr                  ; convert it to lower case
        cmp     ah,'$'                  ; Are we done yet?
        jz      cmky34
        mov     di,dx
        mov     [di],ah
        inc     bx
        inc     dx
        inc     comand.cmccnt
        jmp     cmky33
cmky34: mov     ah,' '
        mov     di,dx
        mov     [di],ah                 ; Put a blank in the buffer.
        inc     dx
        mov     cx,comand.cmcptr        ; Remember where we were.
        mov     comand.cmcptr,dx        ; Update our pointers.
        mov     comand.cmdptr,dx
        mov     ah,'$'
        mov     di,dx
        mov     [di],ah                 ; Add '$' for printing.
        mov     ah,prstr
        mov     dx,cx                   ; Point to beginning of filled in data
        int     dos
        inc     bx                      ; Point to address we'll need.
        mov     bx,[bx]
        mov     comand.cmaflg,0         ; Turn off action flag.
        jmp     rskp

cmky35: inc     bx
        mov     ah,[bx]                 ; Find end of keyword.
        cmp     ah,'$'
        jne     cmky35
        inc     bx
        mov     bx,[bx]                 ; Address of next routine to call.
        jmp     rskp

cmky4:  and     ah,7FH                  ; Turn off minus bit.
        cmp     ah,'?'                  ; Need help?
        je      cmky5
        cmp     ah,' '                  ; Just a space - no error.
        je      cmky51
        cmp     ah,cr
        je      cmky51
        cmp     ah,tab
        je      cmky51
        cmp     ah,escape               ; Ignore escape?
        je      cmky43
cmky41: mov     ah,prstr
        mov     dx,offset cmer03
        int     dos
        jmp     prserr                  ; Parse error - give up.

cmky43: mov     ah,conout               ; Ring a bell.
        mov     dl,bell
        int     dos
        dec     comand.cmcptr           ;[ESC] don't trash BX here.
        dec     comand.cmdptr           ;[ESC] ditto
        dec     comand.cmccnt           ; Don't count the escape.
        mov     comand.cmaflg,0         ; Reset action flag.
        inc     ch                      ; Account for a previous 'dec'.
        jmp     cmky1                   ; Start over.

cmky5:  inc     bx                      ; point to actual keyword
        mov     comand.cmkptr,bx        ; remember current kw
        mov     cl,1                    ; code above expects to count '?'
        mov     comand.cmsiz,cx         ; and size
        mov     dx,comand.cmhlp
        or      dx,dx                   ; was any help given?
        jnz     cmky5a                  ; yes, use it
        jmp     cmkyj1                  ; else make our own message
cmky5a: mov     ah,prstr
        int     dos
cmky50: mov     ah,prstr
        mov     dx,offset crlf
        int     dos
        mov     dx,comand.cmprmp        ; Address of prompt.
        int     dos
        mov     bx,comand.cmdptr        ; Get pointer into buffer.
        mov     al,'$'
        mov     [bx],al                 ; Add dollar sign for printing.
        mov     dx,offset comand.cmdbuf
        int     dos
        dec     comand.cmcptr           ; Don't keep it in the buffer.
        dec     comand.cmccnt           ; Don't count it.
        mov     comand.cmaflg,0         ; Turn off the action flag.
        jmp     repars

cmky51: cmp     comand.cmcr,1           ; Are bare CR's allowed?
        je      cmky52                  ; Yes.
        mov     ah,prstr
        mov     dx,offset cmer04        ; Complain.
        int     dos
cmky52: jmp     prserr

cmky6:  inc     bx                      ; Find end of keyword.
        cmp     byte ptr [bx],'$'
        jne     cmky6
        add     bx,3                    ; Beginning of next command.
        mov     dx,comand.cmsptr        ; Get old cmdptr.
        mov     comand.cmdptr,dx        ; Restore.
        mov     comand.cmsflg,0FFH
        jmp     cmky1                   ; Keep trying.

cmky7:  call    cmgtch                  ; Get char.
        cmp     ah,0
        js      cmky71                  ; Ok if a terminator.
        dec     bx
        jmp     cmky6                   ; No match - try next keyword.
cmky71: cmp     ah,'?'+80h              ; question mark?
        jne     cmky72                  ; yes, don't return yet
        inc     cl              ; count the '?' Fix single char keywords
        jmp     cmky3y                  ; and process ? correctly
cmky72: inc     bx                      ; Get necessary data.
        mov     bx,[bx]
        cmp     ah,escape+80h           ; An escape?
        jne     cmky73
        mov     ah,prstr
        mov     dx,offset prsp          ; Print a space.
        int     dos
        mov     di,comand.cmcptr
        dec     di
        mov     ah,20H
        mov     [di],ah                 ; Replace escape char with space.
        mov     comand.cmaflg,0
        mov     comand.cmsflg,0FFH      ; Pretend they typed a space.
cmky73: jmp     rskp

; See if keyword is unambiguous or not from what the user has typed in.

cmambg: cmp     ch,0                    ; Any keywords left to check?
        jne     cmamb0
        ret                             ; If not then not ambiguous.
cmamb0: inc     bx                      ; Go to end of keyword ...
        mov     al,[bx]                 ; So we can check the next one.
        cmp     al,'$'
        jne     cmamb0
        add     bx,4                    ; Point to start of next keyword.
        dec     cl                      ; Don't count escape.
        mov     dx,comand.cmsptr        ; Buffer with input typed by user.
cmamb1: mov     ah,[bx]                 ; Keyword char.
        mov     di,dx
        mov     al,[di]                 ; Input char.
        call    tolowr                  ; convert to lower case
        cmp     ah,al           ; Keyword bigger than input (alphabetically)?
        jle     cmamb2                  ; No - keep checking.
        ret                             ; Yes   - not ambiguous.
cmamb2: inc     bx                      ; Advance one char.
        inc     dx
        dec     cl
        jnz     cmamb1
        jmp     rskp                    ; Fail - it's ambiguous.



; Parse arbitrary text up to a CR.  Put chars into data buffer sent to
; the host (pointed to by BX).   Called with text of help message in DX.
; Produces asciiz string. Return updated pointer in BX and input size in AH.

cmtext: mov     comand.cmptab,bx        ; Save pointer to data buffer.
        mov     cmthlp,dx               ; Save the help message.
        mov     cl,0                    ; Init the char count.
cmtxt1: mov     comand.cmsflg,0         ; Get all spaces.
        call    cmgtch                  ; Get a char.
        test    ah,80H                  ; is high-order bit on?
        jz      cmtxt5                  ; Nope, put into the buffer.
        and     ah,07FH
        cmp     ah,' '
        je      cmtxt5
        cmp     ah,escape               ; An escape?
        jne     cmtxt2
        mov     ah,conout
        mov     dl,bell                 ; Ring a bell.
        int     dos
        mov     comand.cmaflg,0         ; Reset action flag.
        dec     comand.cmcptr           ; Move pointer to before the escape.
        dec     comand.cmdptr
        dec     comand.cmccnt           ; Decrement count.
        jmp     cmtxt1                  ; Try again.
cmtxt2: cmp     ah,'?'                  ; Asking a question?
        jz      cmtx30
        cmp     ah,FF                   ; Formfeed?
        jne     cmtx2x
        call    cmblnk
cmtx2x: mov     ah,cl                   ; Return count in AH.
        mov     bx,comand.cmptab        ; Return updated pointer.
        jmp     rskp
cmtxt5: inc     cl                      ; 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
        jmp     cmtxt1
cmtx30: mov     comand.cmaflg,0         ; Reset action flag to zero.
        inc     comand.cmdptr           ; count the ?
        cmp     cl,0                    ; Is "?" first char?
        jne     cmtxt5                  ; No, just add to buffer.
        dec     comand.cmcptr   ;[ESC] (moved 3 lines) Don't keep in buffer.
        dec     comand.cmccnt           ;[ESC] Don't count it.
        dec     comand.cmdptr           ;[ESC] don't count if printing help.
        mov     ah,prstr                ; Else, give some help.
        mov     dx,cmthlp               ; Address of help message.
        int     dos
        mov     ah,prstr
        mov     dx,offset crlf          ; Print a crlf.
        int     dos
        mov     ah,prstr
        mov     dx,comand.cmprmp        ; Reprint the prompt.
        int     dos
        mov     bx,comand.cmdptr        ; Get the pointer into the buffer.
        mov     byte ptr [bx],'$'
        mov     ah,prstr
        mov     dx,offset comand.cmdbuf
        int     dos
        jmp     cmtxt1                  ; And keep going.

;       [jrd]
; Parse arbitrary text up to whitespace.  Put chars into data buffer sent to
; the host (pointed to by DX).   Called with text of help message in BX.
; Produces asciiz string. Return updated pointer in DX and input size in AH.
; Skips leading whitespace.
; Does a return skip exit. Replaces old cmifi routine.

cmfil0: push    dx
        mov     dx,bx                   ; interchange bx and bx internally
        pop     bx
        mov     comand.cmptab,bx        ; Save pointer to data buffer.
        mov     cmthlp,dx               ; Save the help message.
        mov     cl,0                    ; Init the char count.
        mov     comand.cmsflg,0ffH      ; skip over leading spaces and tabs.
cmfil1: call    cmgtch                  ; Get a char.
        test    ah,80H          ; is high-order bit on (i.e. terminator seen)?
        jnz     cmfi1a                  ; nz = yes
        jmp     cmfil5                  ; z = no, put char into the buffer.
cmfi1a: and     ah,07FH                 ; clear high order bit
        cmp     ah,escape               ; An escape?
        je      cmfi1b                  ; e = yes.
        cmp     ah,' '                  ; is it a space or control char?
        jg      cmfil2                  ; g = no. keep it
        jmp     cmfi2x          ; else, a space or control char; end here.

cmfi1b: mov     ah,conout               ; handle escape char; ie, complain
        mov     dl,bell                 ; Ring a bell.
        int     dos
        mov     comand.cmaflg,0         ; Reset action flag.
        dec     comand.cmcptr           ; Move pointer to before the escape.
        dec     comand.cmdptr
        dec     comand.cmccnt           ; Decrement count.
        jmp     cmfil1                  ; Try again.
cmfil2: cmp     ah,'?'                  ; Asking a question?
        jz      cmfil3                  ; z = yes
        cmp     ah,ff                   ; Formfeed?
        jne     cmfi2x                  ; ne = no
        call    cmblnk                  ; a FF. clear the command line and end

cmfi2x: mov     bx,comand.cmptab        ; Pointer into destination array.
        mov     byte ptr[bx],0          ; Put null terminator into the buffer
        inc     bx
        mov     ah,cl                   ; Return count in AH.
        mov     dx,comand.cmptab        ; Return updated pointer.
        xchg    dx,bx                   ; re-interchange bx and dx
        jmp     rskp

cmfil3: mov     comand.cmaflg,0         ; Reset action flag to zero.
        inc     comand.cmdptr           ; count the ?
        cmp     cl,0                    ; Is "?" first char?
        jne     cmfil5                  ; No, just add to buffer.
        dec     comand.cmcptr   ;[ESC] (moved 3 lines) Don't keep in buffer.
        dec     comand.cmccnt           ;[ESC] Don't count it.
        dec     comand.cmdptr           ;[ESC] don't count if displaying help.
        mov     ah,prstr                ; Else, give some help.
        mov     dx,cmthlp               ; Address of help message.
        int     dos
        mov     dx,offset crlf          ; Print a crlf.
        int     dos
        mov     dx,comand.cmprmp        ; Reprint the prompt.
        int     dos
        mov     bx,comand.cmdptr        ; Get the pointer into the buffer.
        mov     byte ptr [bx],'$'
        mov     dx,offset comand.cmdbuf
        int     dos
        jmp     cmfil1                  ; And keep going.
cmfil5: inc     cl                      ; Increment 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.


cmgetc: mov     ah,taklev               ; get current Take level
        mov     intake,ah               ; remember here for later callers
        cmp     ah,0                    ; in a Take file?
        jne     cmget1                  ; ne = yes
        jmp     cmge10                  ; no take file, get from keyboard
cmget1: push    bx
        push    si
        mov     bx,takadr
        mov     ax,[bx].takcnt          ; size of item
        or      ax,[bx].takcnt+2        ; empty?
        jnz     cmget5                  ; nz = no
cmget2: mov     al,byte ptr [bx].taktyp ; get type of take
        cmp     al,0ffh                 ; is it really a macro?
        je      cmget4                  ; yes, better not try to close it
        mov     bx,word ptr [bx].takhnd ; get file handle
        mov     ah,close2               ; use 2.0 close
        int     dos
cmget4: dec     taklev
        sub     takadr,size takinfo
        pop     si
        pop     bx
        mov     al,cr                   ; end with carriage return...
        mov     prevch,al               ; remember last read character
        mov     noparse,0
        ret

cmget5: cmp     [bx].takchl,0           ; Any chars left in buffer?
        jne     cmget6                  ; ne = yes
        call    takrd                   ; else read another buffer
        cmp     [bx].takchl,0           ; anything in the file?
        je      cmget2                  ; e = no, quit
cmget6: dec     [bx].takchl
        sub     [bx].takcnt,1           ; DEC doesn't set carry!!
        sbb     [bx].takcnt+2,0
        mov     si,[bx].takptr
        lodsb
        mov     [bx].takptr,si
        cmp     al,ctlz                 ; maybe control-z?
        je      cmget2          ; yes, close take file (has to be before pops)
        pop     si
        pop     bx
        cmp     al,lf                   ; linefeed?
        jne     cmget7
        mov     prevch,al               ; remember last read character
        jmp     cmgetc                  ; yes, ignore it.
cmget7: cmp     al,';'                  ; maybe a semicolon?
        jne     cmget7c                 ; ne = no, accept as-is
        cmp     prevch,5ch              ; was previous char backslash escape?
        jne     cmget9                  ; ne = no, semicolon starts a comment
        cmp     noparse,0               ; parsing this part of the command?
        jne     cmget7a                 ; ne = no, observe but do not store
        pop     bx
        mov     bx,comand.cmcptr        ; Get the pointer into the cmd buffer.
        mov     byte ptr[bx-1],al       ; stuff in the literal semicolon
        pop     bx
cmget7a:mov     prevch,al               ; remember last read character
        cmp     flags.takflg,0          ; echoing take files?
        je      cmget7b                 ; e = no
        push    dx                      ; backspace cursor over backslash
        push    ax                      ; and print the semicolon
        mov     ah,conout
        mov     dl,BS
        int     dos
        mov     dl,al
        int     dos
        pop     ax
cmget7b:pop     dx
        jmp     cmgetc                  ; get next char

cmget7c:mov     prevch,al               ; normal character acceptance pathway
        cmp     flags.takflg,0          ; Echo contents of take file?
        je      cmget8                  ; e = no
        push    dx
        mov     dl,al
        mov     ah,conout
        int     dos
        pop     dx
cmget8: ret                             ; else just return...

; semicolon seen, echo and ignore chars until cr
cmget9: push    ax
        mov     al,spc                  ; replace semicolon with a space
        call    cmget7c                 ; echo the char
        pop     ax
cmget9a:mov     prevch,al               ; remember last read character
        mov     noparse,1               ; defeat semicolon parsing
        call    cmgetc                  ; get a character
        cmp     al,cr                   ; carriage return?
        jne     cmget9a                 ; no, keep reading
        mov     prevch,al               ; remember last read character
        mov     noparse,0               ; done with escaped semicolon actions
        ret                             ; else return it

cmge10: mov     ah,coninq               ; Get a char.
        int     dos
        or      al,al
        jnz     cmge11                  ; ignore null bytes of special keys
        int     dos                     ; read and discard scan code byte
        jmp     cmge10                  ; try again
cmge11: and     al,7fh                  ; only allow 7-bit characters.
        push    ax                      ; save the char
        cmp     al,del
        je      cmgex
        cmp     al,' '                  ; printable?
        jae     cmge12                  ; yes, no translation needed
        cmp     al,cr                   ; this is printable
        je      cmge12
        cmp     al,lf
        je      cmge12
cmgex:  mov     al,' '                  ; else echo a space
cmge12: mov     dl,al                   ; put char here
        cmp     comand.cmquiet,0        ; quiet mode?
        jnz     cmge13                  ; yes, skip echoing...
        mov     ah,conout
        int     dos                     ; echo it ourselves...
cmge13: pop     ax                      ; and return it
        cmp     al,'C'-40H              ; control-C?
        je      cmge15                  ; yes, go handle
        cmp     al,tab
        jne     cmge14
        mov     al,' '
cmge14: ret
cmge15: mov     dx,offset ctcmsg
        mov     ah,prstr
        int     dos
        mov     flags.cxzflg,'C'        ; remember ^C'd
        mov     sp,cmdstk               ; restore command stack ptr
        ret                             ; and fail

; Come here is user types ^W when during input.
cntrlw: mov     ah,prstr
        mov     dx,offset escspc
        int     dos
        dec     comand.cmccnt           ; Don't include it in the count.
        dec     comand.cmcptr           ; Back up past the ^W.
        mov     cl,comand.cmccnt
        mov     ch,0
        jcxz    ctlw2
        pushf
        push    es
        std                             ; Scan  backward
        mov     ax,ds
        mov     es,ax                   ; Point to the data area.
        mov     di,comand.cmcptr        ; Looking from here.
        dec     di
        mov     al,' '
        repe    scasb                   ; Look for non-space.
        je      ctlw1                   ; All spaces, nothing else 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
        cld                             ; reset direction flag
        mov     comand.cmccnt,cl        ; update count
        mov     cx,comand.cmcptr        ; remember old ptr
        mov     comand.cmcptr,di        ; update pointer
        sub     cx,di                   ; this is characters moved
        mov     dl,BS                   ; output backspaces
        mov     ah,conout
ctlw3:  int     dos                     ; backup cursor
        loop    ctlw3
        call    clearl                  ; clear line
        pop     es
        popf
        ret                             ; and return
ctlw2:  mov     ah,conout
        mov     dl,bell
        int     dos
        ret

cminbf: push    dx
        push    bx
        mov     cx,dx                   ; Save value here too.
        cmp     comand.cmaflg,0         ; Is the action char flag set?
        je      cminb1
        jmp     cminb9                  ; If so get no more chars.
cminb1: cmp     comand.cmccnt,size cmdbuf       ; max buffer size
        jb      cminb1a                 ; b = not full
        push    ax                      ; full.
        push    dx
        mov     ah,conout               ; complain
        mov     dl,bell
        int     dos
        pop     dx
        pop     ax
        jmp     cminb6                  ; set action flag
cminb1a:inc     comand.cmccnt           ; Increment the char count.
        call    cmgetc
        mov     ah,al                   ; Keep char in 'ah'.
        mov     bx,comand.cmcptr        ; Get the pointer into the buffer.
        mov     [bx],ah                 ; Put it in the buffer.
        inc     bx
        mov     comand.cmcptr,bx
        cmp     ah,'W'-64               ; Is it a ^W?
        jne     cmnb11
        call    cntrlw                  ; Kill the previous word.
        jmp     repars
cmnb11: cmp     ah,25O                  ; Is it a ^U?
        jne     cminb2
cmnb12: call    ctlu                    ; Clear out the line.
        mov     ah,prstr
        mov     dx,comand.cmprmp        ; Print the prompt.
        int     dos
        mov     bx,offset comand.cmdbuf
        mov     comand.cmcptr,bx        ; Reset the point to the start.
        mov     comand.cmccnt,0         ; Zero the count.
        mov     dx,cx                   ; Preserve original value of dx.
        jmp     repars                  ; Go start over.
cminb2: cmp     ah,bs                   ; Or backspace?
        jz      cminb3
        cmp     ah,del                  ; Delete?
        jne     cminb4
cminb3: mov     ah,comand.cmccnt ; Decrement the char count by two (char + BS)
        dec     ah
        dec     ah
        cmp     ah,0                    ; Have we gone too far?
        jns     cmnb32                  ; If not proceed.
        mov     ah,conout               ; Ring the bell.
        mov     dl,bell
        int     dos
        jmp     cmnb12                  ; Go reprint prompt and reparse.
cmnb32: mov     comand.cmccnt,ah        ; Save the new char count.
        call    dodel                   ; Delete a character.
        mov     ah,prstr                ; Erase the character.
        mov     dx,offset clrspc
        int     dos
        dec     comand.cmcptr           ; Get the pointer into the buffer.
        dec     comand.cmcptr           ; Back up in the buffer.
cmnb34: jmp     repars                  ; Go reparse everything.
cminb4: cmp     ah,'?'                  ; Is it a question mark.
        jz      cminb6
        cmp     ah,escape               ; Is it an escape?
        jz      cminb8
        cmp     ah,cr                   ; Is it a carriage return?
        jz      cminb5
        cmp     ah,lf                   ; Is it a line feed?
        jz      cminb5
        cmp     ah,ff                   ; Is it a formfeed?
        jne     cminb7
        call    cmblnk                  ; FF: clear the screen and
        call    locate                  ; Home the cursor.
        push    bx                      ; make the FF parse like a cr
        mov     bx,comand.cmcptr        ; permanent storage of next char
        mov     byte ptr [bx-1],cr      ; pretend a carriage return were typed
        pop     bx
cminb5: cmp     comand.cmccnt,1         ; Have we parsed any chars yet?
        jnz     cminb6
        cmp     comand.cmcr,0           ; bare cr's allowed?
        jne     cminb6                  ; ne = yes
        jmp     prserr                  ; If not, just start over.
cminb6: mov     comand.cmaflg,0FFH      ; Set the action flag.
        jmp     cminb9
cminb7: jmp     cminb1                  ; Get another char.

cminb8: mov     ah,prstr                ; Don't print the escape char.
        mov     dx,offset escspc
        int     dos
        jmp     cminb6

cminb9: pop     bx
        pop     dx
        ret

cmgtch: push    cx
        push    bx
        push    dx
cmgtc1: cmp     comand.cmaflg,0         ; is action flag set?
        jne     cmgt10
        call    cminbf          ; If the action char flag is not set get more.
cmgt10: mov     bx,comand.cmdptr        ; Get a pointer into the buffer.
        mov     ah,[bx]                 ; Get the next char.
        inc     bx
        mov     comand.cmdptr,bx
        cmp     ah,' '                  ; Is it a space?
        jz      cmgtc2
        cmp     ah,tab                  ; Or a tab?
        jne     cmgtc3
cmgtc2: cmp     comand.cmsflg,0         ; space flag, was last char a space?
        jne     cmgtc1                  ; Yes, get another char.
        mov     comand.cmsflg,0FFH      ; Set the space flag.
        mov     ah,' '
        pop     dx
        pop     bx
        jmp     cmgtc5
cmgtc3: mov     al,0
        mov     comand.cmsflg,al        ; Zero the space flag.
        pop     dx
        pop     bx
        cmp     ah,escape
        jz      cmgtc5
        cmp     ah,'?'                  ; Is the user curious?
        jz      cmgtc4
        cmp     ah,cr
        jz      cmgtc4
        cmp     ah,lf
        jz      cmgtc4
        cmp     ah,ff
        je      cmgtc4
        pop     cx
        ret                             ; Not an action char, just return.
cmgtc4: dec     comand.cmdptr
cmgtc5: or      ah,80H                  ; Make the char negative to indicate
        pop     cx
        ret                             ; it is a terminator.
CMND    ENDP

;       This address is jumped to on reparse.

PARSE   PROC NEAR
repars: mov     sp,comand.cmostp        ; new sp <-- old sp
        mov     bx,offset comand.cmdbuf
        mov     comand.cmdptr,bx
        mov     comand.cmsflg,0ffh
        jmp     comand.cmrprs           ; go back to reparse address

;       This address can be jumped to on a parsing error.

prserr: mov     sp,comand.cmostp        ; Set new sp to old one.
        mov     bx,offset comand.cmdbuf
        mov     comand.cmcptr,bx        ; Initialize the command pointer.
        mov     comand.cmdptr,bx
        mov     ah,0
        mov     comand.cmaflg,ah        ; Zero the flags.
        mov     comand.cmccnt,ah
        mov     comand.cmsflg,0FFH
        cmp     taklev,0                ; in take cmd?
        je      prser2                  ; e = no
        cmp     flags.takflg,0          ; echo contents of take file?
        je      prser1                  ; e = no
prser2: mov     ah,prstr
        mov     dx,offset crlf
        int     dos
        mov     ah,prstr                ; Print the prompt.
        mov     dx,comand.cmprmp        ; Get the prompt.
        int     dos
                                ; Instead return to before the prompt call.
prser1: jmp     comand.cmrprs
PARSE   ENDP

; Convert ascii characters in al and ah to lowercase. [jrd]
; All registers are preserved except AX, of course.

TOLOWR PROC NEAR
        test    ah,80H                  ; sign  bit set?
        jnz     tolow1                  ; nz = yes
        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: test    al,80H                  ; sign bit set?
        jnz     tolow2                  ; nz = yes
        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
