home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / extra / nyenhuis3.arc / MSSCMD.ASM < prev    next >
Assembly Source File  |  1990-01-14  |  50KB  |  1,465 lines

  1.     NAME    msscmd
  2. ; File MSSCMD.ASM
  3.     include mssdef.h
  4. ; Edit history:
  5. ; Last edit 14 Jan 1990
  6. ; 21 Nov 1988 Version 2.32
  7. ; 17 Oct 1988 Make command keyword search failure yield global kstatus of
  8. ;  failure status, to inform Take/Macros of a defective command.
  9. ; 4 Aug 1988 Fix keyword count initialization in help display.
  10. ; 1 July 1988 Version 2.31
  11. ; 7 June 1988 Add comand.impdo flag to permit a keyword search failure to
  12. ;  retry as a DO cmd (macro table). Used only by main Kermit command level.
  13. ; 27 May 1988 Allow "-<cr>" as line continuation pair and let "\-<cr>" 
  14. ;  stand for "-<end of line>". Add comand.cmblen to sense buffer overflow on
  15. ;  calls to cmtxt; if comand.cmblen is left at zero then use 128 byte limit
  16. ;  (comand.cmblen is cleared at command end).
  17. ; 15 May 1988 Make :label keywords no-ops, make query ordinary char in TAKEs.
  18. ; 6 May 1988 Show ambiguous keywords at parse time, do graceful interaction.
  19. ; 28 March 1988 Permit '\%x' (x = '0' or above) in commands as 'variables'
  20. ;  (substituted string of text). Words are obtained from Macro table mcctab.
  21. ;  DEF MAC and DO MAC define variables. DEF macro uses comand.cmper > 0 to
  22. ;  allow '\%x'to be stored as a literal. Variables work in any command except
  23. ;  DEF MAC. Major redesign of whole parser. [jrd]
  24. ; 4 March 1988 Rewrite keyword parsing to permit non-alphabetized tables
  25. ;  and 8-bit characters. Add byte comand.cmwhite which when non-zero permits
  26. ;  leading whitespace in cmline and cmword commands; it is reset at command
  27. ;  completion. Move procedure Prompt here from mssker. [jrd]
  28. ; 27 Feb 1988 Add capability of stdin being a file. [jrd]
  29. ; 1 Jan 1988 version 2.30
  30.  
  31.      public comnd, comand, prserr, isdev, iseof, prompt, tolowr
  32.     public parstate, pardone, parfail, nparam, param, lparam, ninter
  33.     public inter, atparse, atpclr, atdispat, cspb
  34.  
  35. data     segment    public 'data'
  36.     extrn    flags:byte, taklev:byte, takadr:word, mcctab:byte
  37.     extrn    kstatus:byte
  38.                 ; Start Patch structure. Must be first.
  39. dspb    db    128 dup (0)    ; data segment patch buffer
  40.                 ;  with space for other material, if req'd
  41.                 ; end of Patch structure
  42. comand    cmdinfo    <>
  43. cmer00  db      cr,lf,'?Program internal error, recovering$'
  44. cmer01    db    cr,lf,'?More parameters are needed$'
  45. cmer02    db    cr,lf,'?Word "$'
  46. cmer03    db    '" is not usable here$'
  47. cmer04    db    '" is ambiguous$'
  48. cmer07    db    cr,lf,'?Ignoring extra characters "$'
  49. cmer08    db    '"$'
  50. cmer09    db    cr,lf,'?Text exceeded available buffer capacity$'
  51. cmin00  db      ' Press ENTER to execute command$'
  52. cmin01    db    ' One of the following:',cr,lf,'$'
  53. stkmsg    db    cr,lf,bell,'?Exhausted work space! Circular definition?$'
  54. crlf    db      cr,lf,'$'
  55. ctcmsg    db    5eh,'C$'
  56. errflag    db    0            ; non-zero to suppress cmcfrm errors
  57. kwstat    db    0            ; get-keyword status
  58. prevch    db    0            ; previous char read by cmgetc
  59. noparse    db    0            ; semicolons not special, if non-zero
  60. subcnt    db    0            ; count of chars matched in '\%'
  61. cmsflg    db    0        ; Non-zero when the last char was a space
  62. cmdbuf    db    255 DUP (0)        ; Buffer for command parsing
  63.     even
  64. cmdstk    dw    0            ; stack pointer at comand call time
  65. cmptab    dw    0            ; Address of present keyword table
  66. cmhlp    dw    0            ; Address of present help
  67. cmwptr    dw    0            ; Pointer for next char write
  68. cmrptr    dw    0            ; Pointer for next char read
  69. cmsiz    dw    0            ; Size info of user input
  70. cmsptr    dw    0            ; Place to save a pointer
  71. temp    dw    0            ; temp (counts char/line so far)
  72.  
  73.     even                ; Control sequence storage area
  74. maxparam equ    16            ; number of ESC and DCS Parameters
  75. maxinter equ    16            ; number of ESC and DCS Intermediates
  76. parstate dw    0            ; parser state, init to startup
  77. pardone dw    0            ; where to jmp after Final char seen
  78. parfail    dw    0            ; where to jmp if parser fails
  79. nparam    dw    0            ; number of received Parameters
  80. param    dw    maxparam dup (0)    ; Parameters for ESC
  81. lparam    db    0            ; a single letter Parameter for ESC
  82. ninter    dw    0            ; number of received Intermediates
  83. inter    db    maxinter dup (0)    ; Intermediates for ESC 
  84. data    ends
  85.  
  86. code    segment    public 'code'
  87.     extrn    ctlu:near, cmblnk:near, locate:near, takrd:near
  88.     extrn    takclos:near, docom:near, prtasz:near
  89.     assume    cs:code, ds:data, es:nothing
  90.  
  91.                 ; Patch area. Must be first in MSK's Code Seg
  92. cspb    dw    256 dup(?)    ; code segment patch buffer
  93.                 ; end of Patch area
  94.  
  95. ;       This routine parses the specified function in AH. Any additional
  96. ;       information is in DX and BX.
  97. ;       Returns carry clear on success and carry set on failure
  98.  
  99. COMND    PROC NEAR
  100.     mov    cmdstk,sp        ; save stack ptr for longjmp exit
  101.     mov    noparse,0          ; recognize semicolons in Take files
  102.         cmp    ah,cmeol        ; Parse a confirm?
  103.         jne    cm2            ; nz = no
  104.     jmp    cmcfrm            ; get a Carriage Return end of line
  105. cm2:     cmp    ah,cmkey        ; Parse a keyword?
  106.     jne    cm3
  107.         jmp    cmkeyw            ; Try and get one
  108. cm3:    cmp    ah,cmline        ; parse line of text
  109.     jne    cm4
  110.     jmp    cmtext
  111. cm4:    cmp    ah,cmword        ; parse arbitrary word
  112.     jne    cm5
  113.     jmp    cmfil0
  114. cm5:    mov    ah,prstr        ; Else give error
  115.     mov    dx,offset cmer00    ; "?Program internal error"
  116.     int    dos
  117.     jmp    prserr
  118. COMND    ENDP
  119.  
  120. ; This routine parses a keyword from the table pointed at by DX, help text
  121. ; point to by BX. Format of the table is as follows (use macro mkeyw):
  122. ;    addr:    db    N      ; Where N is the # of entries in the table
  123. ;        db    M      ; M is the size of the keyword (excl '$')
  124. ;        db    'string$' ; String is the keyword
  125. ;        dw    value      ; Value is data to be returned
  126. ; Keywords may be in any order and in mixed case.
  127. ; Return is rskp for success and ret for failure.
  128.  
  129. ; cmptab: pointer to keyword table (supplied by caller)
  130. ; cmhlp: pointer to help message (supplied by caller)
  131. ; cmsptr: pointer to current user word text
  132. ; cmsiz: length of user text, excluding terminator
  133. ; comand.cmcr: 0 = empty lines not allowed, 1 = empty lines allowed
  134. ; comand.cmwhite: non-zero allows leading whitespace for cmline and cmword,
  135. ;                 reset automatically at end of call
  136. ; cmwptr: buffer write pointer to next free byte
  137. ; cmrptr: buffer read pointer for next free byte
  138. ; comand.cmper:    0 to do \%x substitution. Set to 0 at end of call
  139. ; comand.impdo: non-zero permits keyword failure to retry as DO command, reset
  140. ;         automatically at time of failure.
  141. cmkeyw    proc    near
  142.     mov    cmsiz,0            ; user word length
  143.     mov    ax,cmrptr        ; get command reading pointer
  144.     mov    cmsptr,ax        ; set pointer for start of user word
  145.     mov    cmhlp,bx        ; save the help pointer
  146.         mov    cmptab,dx        ; save the beginning of keyword table
  147.     mov    bx,dx
  148.     cmp    byte ptr[bx],0        ; get number of entries in table
  149.     jne    cmky1
  150.     jmp    cmky7            ; e = no keywords to check, error
  151. cmky1:    mov    cmsflg,0ffh        ; skip leading spaces/tabs
  152.     call    cmgtch            ; get char from the user into ah
  153.     jc    cmky3            ; c = terminator
  154.     mov    dx,cmrptr        ; next byte to read
  155.     dec    dx            ; where we just read a char
  156.     mov    cmsptr,dx        ; remember start of keyword
  157.     inc    cmsiz            ; start counting user chars
  158. cmky2:    call    cmgtch            ; read until terminator
  159.     jc    cmky3            ; c = terminator
  160.     inc    cmsiz            ; count user chars
  161.     jmp    short cmky2        ; no terminator yet
  162.  
  163. cmky3:    cmp    ah,'?'                  ; need help?
  164.     jne    cmky4            ; ne = no
  165.     jmp    cmkyhlp            ; do help and exit
  166. cmky4:    cmp    ah,escape        ; escape?
  167.     jne    cmky6            ; ne = no
  168.     call    cmkyesc            ; process escape
  169.     jc    cmky5            ; c = failure (no unique keyword yet)
  170.     mov    comand.cmper,0        ; reset to variable recognition
  171.     mov    comand.cmkeep,0
  172.     mov    comand.impdo,0        ; clear flag to prevent loops
  173.     mov    comand.cmquiet,0    ; permit echoing again
  174.     clc
  175.     ret                ; return successfully to user
  176.  
  177. cmky5:    cmp    cmsiz,0            ; started a word yet?
  178.     je    cmky1            ; e = no, ignore escape, keep looking
  179.     jmp    cmkyhlp            ; ne = yes, show type of error
  180.  
  181. cmky6:    cmp    cmsiz,0            ; length of user's text, empty?
  182.     je    cmky7            ; e = yes, parse error
  183.     push    bx
  184.     mov    bx,cmsptr        ; point at first user character
  185.     cmp    byte ptr[bx],':'    ; start of a label?
  186.     pop    bx
  187.     jne    cmky6a            ; ne = no, return success
  188.     mov    cmsiz,1            ; say just one byte
  189. cmky6a:
  190.     call    getkw            ; get unique kw, point to it with bx
  191.     jc    cmky8            ; c = not found
  192.     add    bl,[bx]            ; add length of keyword text (CNT)
  193.     adc    bh,0
  194.     add    bx,2            ; point at value field
  195.     mov    bx,[bx]            ; bx = return value following keyword
  196.     mov    comand.cmper,0        ; reset to variable recognition
  197.     mov    comand.cmkeep,0
  198.     mov    comand.impdo,0        ; clear flag to prevent loops
  199.     mov    comand.cmquiet,0    ; permit echoing again
  200.     mov    errflag,0
  201.     clc
  202.     ret                ; return successfully
  203.                     ; all other terminators come here
  204. cmky7:    cmp    cmsiz,0            ; empty table or empty user's text?
  205.     jne    cmky8            ; ne = no
  206.     cmp    comand.cmcr,0        ; empty lines allowed?
  207.     jne    cmky10            ; ne = yes, do not complain
  208.     push    dx
  209.     mov    ah,prstr
  210.     mov    dx,offset cmer01    ; command word expected
  211.     int    dos
  212.     pop    dx
  213.     mov    comand.cmquiet,0    ; permit echoing again
  214.     mov    comand.impdo,0        ; clear flag to prevent loops
  215.     stc                ; failure
  216.     ret
  217.  
  218. cmky8:    cmp    comand.impdo,0        ; failed here, ok to try Macro table?
  219.     je    cmky8a            ; e = no, use regular exit path
  220.     mov    comand.impdo,0        ; yes, but clear flag to prevent loops
  221.     mov    cmrptr,offset cmdbuf    ; reinit read pointer
  222.     mov    comand.cmquiet,1    ; suppress echoing of same keyword
  223.     mov    bx,offset docom        ; return DO as "found" keyword
  224.     clc
  225.     ret                ; return success to invoke DO
  226.  
  227. cmky8a:    mov    errflag,1        ; say already doing error recovery
  228.     or    kstatus,1        ; global command status, failure
  229.     mov    comand.cmquiet,0    ; permit echoing again
  230.     call    isdev            ; reading pretyped lines?
  231.     jnc    cmky9            ; nc = yes, consume rest of line
  232.     cmp    taklev,0        ; in a Take file?
  233.     jne    cmky9            ; ne = yes
  234.     call    cmskw            ; display offending keyword
  235.     dec    cmrptr            ; interactive, backup to terminator
  236.     mov    bx,cmrptr        ; look at it
  237.     cmp    byte ptr [bx],' '    ; got here on space terminator?
  238.     jne    cmky10            ; ne = no, (cr,lf,ff) exit failure
  239.     mov    ah,prstr        ; start a fresh line
  240.     mov    dx,offset crlf
  241.     int    dos
  242.     call    bufreset        ; cut back buffer to just before term
  243.     jmp    repars            ; reparse interactive lines
  244.  
  245. cmky9:    call    cmcfrm            ; get formal end of command line
  246.                     ;  to maintain illusion of typeahead
  247.                     ;  and let user backspace to correct
  248.                     ;  mistakes (we reparse everything)
  249.     call    cmskw            ; display offending keyword
  250. cmky10:    mov    comand.cmquiet,0    ; permit echoing again
  251.     stc                ; say failure
  252.     ret
  253. cmkeyw    endp
  254.  
  255. ;;;;;; start support routines for keyword parsing.
  256.  
  257. cmkyesc    proc    near            ; deal with escape terminator
  258.     call    bufreset        ; reset buffer to end just before ESC
  259.     cmp    cmsiz,0         ; user word length, empty?
  260.     jne    cmkye2            ; ne = have user text, else complain
  261. cmkye1:    call    esceoc            ; do normal escape end-of-command
  262.     stc                ; say failure to fill out word
  263.     ret
  264.                     ; add unique keyword to buffer
  265. cmkye2:    call    getkw            ; is there a matching keyword?
  266.     jc    cmkye1            ; c = ambiguous or not found
  267.     push    bx            ; unique, bx points to structure
  268.     push    si
  269.     mov    cl,[bx]            ; length of keyword
  270.     mov    ch,0
  271.     inc    bx            ; point to first letter
  272.     mov    si,cmwptr        ; where next char goes
  273.     mov    dx,cmsiz        ; length of user word
  274.     add    bx,dx            ; add chars known so far
  275.     sub    cx,dx            ; calculate number yet to add
  276.     add    cmsiz,cx
  277.     jcxz    cmkye4            ; z = none
  278.     mov    ah,conout        ; display new char
  279. cmkye3:    mov    al,[bx]            ; get a keyword letter
  280.     inc    bx
  281.     call    tolowr            ; lowercase
  282.     mov    [si],al            ; store it
  283.     inc    si
  284.     mov    dl,al
  285.     int    dos            ; display it
  286.     loop    cmkye3            ; do all new chars
  287. cmkye4:    mov    byte ptr[si],' '    ; insert space terminator in buffer
  288.     mov    dl,' '            ; display it
  289.     mov    ah,conout
  290.     int    dos
  291.     inc    si
  292.     mov    cmrptr,si        ; move token pointer after the space
  293.     mov    cmwptr,si        ; next free slot is after the space
  294.     mov    cmsflg,0ffh        ; set space-seen flag
  295.     pop    si
  296.     pop    bx            ; bx = keyword structure
  297.     add    bl,[bx]            ; add length of keyword text
  298.     adc    bh,0
  299.     add    bx,2            ; point at value field
  300.     mov    bx,[bx]            ; bx = return value following keyword
  301.     clc                ; carry clear for success
  302.     ret
  303. cmkyesc    endp
  304.  
  305. esceoc    proc    near            ; do normal escape end-of-command
  306.     push    ax
  307.     push    dx
  308.     mov    ah,conout        ; ring the bell
  309.     mov    dl,bell
  310.     int    dos
  311.     pop    dx
  312.     pop    ax
  313.     call    bufreset        ; reset buffer
  314.     stc                ; say error condition
  315.     ret
  316. esceoc    endp
  317.  
  318. ; Help. Question mark entered by user.  Display all the keywords that match
  319. ; user text. If text is null then use external help if available; otherwise,
  320. ; display all keywords in the table. Removes question mark from buffer and
  321. ; invokes reparse of command line to-date. User word starts at .cmsptr and
  322. ; is .cmsiz bytes long.
  323. cmkyhlp    proc    near
  324.     mov    cx,0            ; clear number of keyword (none yet)
  325.     cmp    cmsiz,0            ; user text given?
  326.     jne    cmkyh1            ; ne = yes, use matching keywords
  327.     cmp    cmhlp,0            ; external help given?
  328.     jne    cmkyh6            ; yes, use it instead of full table
  329. cmkyh1:    mov    temp,0            ; count # chars printed on this line
  330.     mov    bx,cmptab        ; beginning of kw table
  331.     mov    ch,[bx]            ; length of table
  332.     mov    cl,0            ; no keywords or help displayed yet
  333.     inc    bx            ; point at CNT field
  334. cmkyh2:    cmp    cmsiz,0            ; length of user word
  335.     je    cmkyh3            ; e = null, use full table
  336.     call    cmpwrd            ; compare keyword with user word
  337.     jc    cmkyh5            ; c = no match, get another keyword
  338. cmkyh3:    mov    al,[bx]            ; length of table keyword
  339.     add    byte ptr temp,al    ; count chars printed so far
  340.     cmp    temp,76            ; will this take us beyond column 78?
  341.     jbe    cmkyh4            ; be = no, line has more room
  342.     mov    byte ptr temp,al    ; reset the count
  343.     mov    ah,prstr
  344.     mov    dx,offset crlf        ; break the line
  345.     int    dos
  346. cmkyh4:    cmp    cl,0            ; any keywords found yet?
  347.     jne    cmkyh4a            ; ne = yes
  348.     mov    dx,offset cmin01    ; start with One of the following: msg
  349.     mov    ah,prstr
  350.     int    dos
  351.     inc    cl            ; say one keyword has been found
  352. cmkyh4a:mov    dl,spc            ; put two spaces before each keyword
  353.     mov    ah,conout
  354.     int    dos
  355.     int    dos
  356.     add    temp,2            ; count output chars
  357.     mov    dx,bx            ; get current keyword structure
  358.     inc    dx            ;  text part
  359.     mov    ah,prstr
  360.     int    dos            ; display it
  361. cmkyh5:    dec    ch            ; are we at end of table?
  362.     jle    cmkyh7            ; le = yes, quit now
  363.     add    bl,[bx]            ; next keyword, add CNT chars to bx
  364.     adc    bh,0
  365.     add    bx,4            ; skip CNT, '$' and 16 bit value
  366.     jmp    cmkyh2            ; go examine this keyword
  367.  
  368. cmkyh6:    mov    dx,cmhlp        ; external help text
  369.     mov    ah,prstr
  370.     int    dos
  371.     inc    cl            ; say gave help already
  372. cmkyh7:    cmp    cl,0            ; found any keywords?
  373.     jne    cmkyh9            ; ne = yes
  374.     mov    cx,cmsiz        ; length of word
  375.     cmp    cx,0
  376.     jg    cmkyh8            ; g = something to show
  377.     push    dx
  378.     mov    ah,prstr
  379.     mov    dx,offset cmer01    ; command word expected
  380.     int    dos
  381.     pop    dx
  382.     jmp    prserr
  383. cmkyh8:    mov    kwstat,0        ; set keyword not-found status
  384.     call    cmskw            ; display offending keyword
  385. cmkyh9:    mov    ah,prstr        ; start a fresh line
  386.     mov    dx,offset crlf
  387.     int    dos
  388.     call    bufreset        ; cut back buffer to just before '?'
  389.         jmp    repars
  390. cmkyhlp    endp
  391.  
  392. ; See if keyword is ambiguous or not from what the user has typed in.
  393. ; Return carry set if word is ambiguous or not found, carry clear otherwise.
  394. ; Uses table pointed at by comand.cmptab, user text pointed at by
  395. ; comand.cmsptr and length in comand.cmsiz.
  396. cmambg    proc    near
  397.     push    bx
  398.     push    cx
  399.     push    dx
  400.     mov    dl,0            ; count keyword matches so far
  401.     mov    bx,cmptab        ; look at start of keyword table
  402.     mov    cl,[bx]            ; get number of entries in table
  403.     mov    ch,0            ; use cx as a counter
  404.     jcxz    cmamb8            ; z = no table so always ambiguous
  405.     inc    bx            ; look at CNT byte of keyword
  406. cmamb4:    call    cmpwrd            ; user vs table words, same?
  407.     jc    cmamb6            ; c = no match
  408.     inc    dl            ; count this as a match
  409.     cmp    dl,1            ; more than one match?
  410.     ja    cmamb8            ; a = yes, quit now
  411. cmamb6:    add    bl,[bx]            ; add CNT chars to bx
  412.     adc    bh,0
  413.     add    bx,4            ; skip CNT, '$' and 16 bit value
  414.     loop    cmamb4            ; do rest of keyword table
  415.     cmp    dl,1            ; how many matches were found?
  416.     jne    cmamb8            ; ne = none or more than 1: ambiguous
  417.     pop    dx            ; restore main registers
  418.     pop    cx
  419.     pop    bx
  420.     clc
  421.     ret                ; ret = not ambiguous
  422. cmamb8:    pop    dx            ; restore main registers
  423.     pop    cx
  424.     pop    bx
  425.     stc
  426.     ret                ; return ambiguous or not found
  427. cmambg    endp
  428.  
  429. ; Compare user text with keyword, abbreviations are considered a match.
  430. ; Enter with bx pointing at keyword table CNT field for a keyword.
  431. ; Return carry clear if they match, set if they do not match. User text
  432. ; pointed at by comand.cmsptr and length is in comand.cmsiz.
  433. ; Registers preserved.
  434.  
  435. cmpwrd    proc    near
  436.     push    cx
  437.     mov    cx,cmsiz        ; length of user's text
  438.     jcxz    cmpwrd2            ; z: null user word matches no keyword
  439.     cmp    cl,[bx]            ; user's text longer than keyword?
  440.     ja    cmpwrd2            ; a = yes, no match
  441.     push    ax
  442.     push    bx
  443.     push    si
  444.     inc    bx                ; point at table's keyword text
  445.     mov    si,cmsptr        ; buffer ptr to user input
  446.     cld
  447. cmpwrd1:lodsb                ; user text
  448.     mov    ah,[bx]            ; keyword text
  449.     inc    bx            ; next keyword letter
  450.     call    tolowr            ; force lower case on both chars
  451.     cmp    ah,al            ; same?
  452.     loope    cmpwrd1            ; e = same so far
  453.     pop    si
  454.     pop    bx
  455.     pop    ax
  456.     jne    cmpwrd2            ; ne = mismatch
  457.     pop    cx            ; recover keyword counter
  458.     clc                ; they match
  459.     ret
  460. cmpwrd2:pop    cx            ; recover keyword counter
  461.     stc                ; they do not match
  462.     ret
  463. cmpwrd    endp
  464.  
  465. ; Get pointer to keyword structure using user text. Uses keyword table
  466. ; pointed at by comand.cmptab and comand.cmsiz holding length
  467. ; of user's keyword (cmpwrd needs comand.cmsptr pointing at user's
  468. ; keyword and length of comand.cmsiz). Structure pointer returned in BX.
  469. ; Return carry clear for success and carry set for failure. Modifies BX.
  470. getkw    proc    near
  471.     push    cx
  472.     mov    kwstat,0        ; keyword status, set to not-found
  473.     cmp    cmsiz,0            ; length of user word, empty?
  474.     je    getkw3            ; e = yes, fail
  475.     mov    bx,cmptab        ; table of keywords
  476.     mov    cl,[bx]            ; number of keywords in table
  477.     mov    ch,0
  478.     jcxz    getkw3            ; z = none, fail
  479.     inc    bx            ; point to first
  480. getkw1:    call    cmpwrd            ; compare user vs table words
  481.     jc    getkw2            ; c = failed to match word, try next
  482.     mov    kwstat,1        ; say found one keyword, maybe more
  483.     push    dx
  484.     mov    dx,cmsiz        ; users word length
  485.     cmp    [bx],dl            ; same length (end of keyword)?
  486.     pop    dx
  487.     je    getkw4            ; e = yes, exact match. Done
  488.     call    cmambg            ; ambiguous?
  489.     jnc    getkw4            ; nc = unique, done, return with bx
  490.     mov    kwstat,2        ; say more than one such keyword
  491. getkw2:    add    bl,[bx]            ; next keyword, add CNT chars to bx
  492.     adc    bh,0
  493.     add    bx,4            ; skip CNT, '$' and 16 bit value
  494.     loop    getkw1            ; do all, exhaustion = failure
  495. getkw3:    pop    cx
  496.     stc                ; return failure
  497.     ret
  498. getkw4:    pop    cx
  499.     clc                ; return success
  500.     ret
  501. getkw    endp
  502.  
  503. ; show offending keyword message. Comand.cmsptr points to user word,
  504. ; comand.cmsiz has length. Modifies AX, CX, and DX.
  505. cmskw    proc    near
  506.     cmp    comand.cmquiet,0    ; Quiet mode?
  507.     je    cmskw0            ; e = no, regular mode
  508.     ret                ; else say nothing
  509. cmskw0:    mov    ah,prstr        ; not one of the above terminators
  510.     mov    dx,offset cmer02    ; '?Word "'
  511.     int    dos
  512.     mov    cx,cmsiz        ; length of word
  513.     jcxz    cmskw3            ; z = null
  514.     mov    ah,conout
  515.     push    si
  516.     mov    si,cmsptr        ; point to word
  517.     cld
  518. cmskw1:    lodsb
  519.     cmp    al,' '            ; control code?
  520.     jae    cmskw2            ; ae = no
  521.     push    ax
  522.     mov    dl,5eh            ; caret
  523.     int    dos
  524.     pop    ax
  525.     add    al,'A'-1        ; plus ascii bias
  526. cmskw2:    mov    dl,al            ; display chars in word
  527.     int    dos
  528.     loop    cmskw1
  529.     pop    si
  530. cmskw3:    mov    dx,offset cmer03    ; '" not usable here.'
  531.     cmp    kwstat,1        ; kywd status from getkw, not found?
  532.     jb    cmskw4            ; b = not found, a = ambiguous
  533.     mov    dx,offset cmer04    ; '" ambiguous'
  534. cmskw4:    mov    ah,prstr
  535.         int    dos
  536.     ret
  537. cmskw    endp
  538. ;;;;;;;;;; end of support routines for keyword parsing.
  539.  
  540. ; Parse    arbitrary text up to a CR. Enter with BX = pointer to output buffer,
  541. ; DX pointing to help text. Produces asciiz string. Return updated pointer in
  542. ; BX and output size in AH. Leading spaces are omitted unless comand.cmwhite
  543. ; is non-zero (cleared upon exit). It does not need to be followed by the
  544. ; usual call to confirm the line. Byte comand.cmblen can be used to specify
  545. ; the length of the caller's buffer; cleared to zero by this command to
  546. ; imply a length of 127 bytes (default) and if zero at startup use 127 bytes.
  547. cmtext    proc    near
  548.     mov    cmptab,bx        ; save pointer to data buffer
  549.     mov    cmhlp,dx        ; save the help message
  550.     mov    cx,0            ; init the char count
  551.     cmp    comand.cmblen,0        ; length of user's buffer given?
  552.     jne    cmtxt0            ; ne = yes
  553.     mov    comand.cmblen,127    ; else set 127 byte limit plus null
  554. cmtxt0:    mov    cmsflg,0ffh        ; skip initial spaces
  555.     cmp    comand.cmwhite,0    ; allow leading whitespace?
  556.     je    cmtxt1a            ; e = no
  557. cmtxt1:    mov    cmsflg,0        ; get all spaces
  558. cmtxt1a:call    cmgtch            ; get a char
  559.     jnc    cmtxt5            ; nc = non-terminator, put in buffer
  560.     cmp    ah,' '            ; space?
  561.     je    cmtxt5            ; e = yes, record it
  562.     cmp    ah,escape        ; escape?
  563.     jne    cmtxt2            ; ne = no
  564.     call    esceoc            ; do normal escape end-of-command
  565.     jmp    cmtxt0            ; try again
  566.  
  567. cmtxt2:    cmp    ah,'?'            ; asking a question?
  568.     je    cmtxt3            ; e = yes
  569.     cmp    ah,cr            ; formal carriage return?
  570.     je    cmtxt2a            ; e = yes
  571.     inc    cmrptr            ; accept char into buffer
  572.     jmp    cmtxt5            ;  and record the char
  573. cmtxt2a:mov    ah,cl            ; return count in AH
  574.     mov    bx,cmptab        ; return updated pointer
  575.     mov    byte ptr[bx],0        ; put terminator into the buffer
  576.     mov    comand.cmwhite,0    ; clear leading whitespace flag
  577.     mov    comand.cmper,0        ; reset to variable recognition
  578.     mov    comand.cmblen,0        ; set user buffer length to unknown
  579.     mov    comand.cmkeep,0
  580.     clc
  581.     ret
  582.                     ; Help processor
  583. cmtxt3:    inc    cmrptr            ; count the ?
  584.     cmp    cx,0            ; Is "?" first char?
  585.     jne    cmtxt5            ; ne = no, just add to buffer
  586.     dec    cmrptr
  587.     mov    cmsiz,0            ; no keyword for help
  588.     mov    comand.cmwhite,0    ; clear leading whitespace flag
  589.     cmp    cmhlp,0            ; external help given?
  590.     jne    cmtxt3a            ; ne = yes
  591.     mov    cmhlp,offset cmin00    ; confirm with c/r msg
  592.     mov    comand.cmblen,0        ; set user buf length to unknown
  593. cmtxt3a:jmp    cmkyhlp            ; do help process
  594.  
  595. cmtxt5:    inc    cx            ; increment the count
  596.     mov    bx,cmptab        ; pointer into destination array
  597.     mov    [bx],ah            ; put char into the buffer
  598.     inc    bx
  599.     mov    al,0
  600.     mov    [bx],al            ; insert null terminator
  601.     mov    cmptab,bx
  602.     cmp    ch,0            ; overflowed into high byte?
  603.     jne    cmtxt6            ; ne = yes
  604.     cmp    cl,comand.cmblen    ; buffer filled?
  605.     ja    cmtxt6            ; a = yes, declare error
  606.     jb    cmtxt5a            ; a = not filled yet
  607.     mov    ah,conout        ; notify user that the buffer is full
  608.     mov    dl,bell
  609.     int    dos
  610. cmtxt5a:jmp    cmtxt1
  611. cmtxt6:    mov    ah,prstr
  612.     mov    dx,offset cmer09
  613.     int    dos
  614.     jmp    prserr            ; declare parse error
  615. cmtext    endp
  616.  
  617.  
  618. ; Parse arbitrary text up to whitespace.  Enter with DX pointing to output
  619. ; buffer and BX pointing to help text. Produces asciiz string. Return updated
  620. ; pointer in DX and input size in AH. Skips leading whitespace unless 
  621. ; comand.cmwhite is non-zero (cleared upon exit). Does a return skip exit.
  622.  
  623. cmfil0    proc    near
  624.     mov    cmptab,dx        ; save pointer to data buffer
  625.     mov    cmhlp,bx        ; save the help message
  626.     mov    cmsiz,0            ; init the char count
  627. cmfil0a:cmp    comand.cmwhite,0    ; allow leading whitespace?
  628.     jne    cmfil1            ; ne = yes
  629.     mov    cmsflg,0ffh        ; omit leading space
  630. cmfil1:    call    cmgtch            ; get a char
  631.     jc    cmfi1a            ; c = terminator 
  632.     jmp    cmfil5            ; put char into the buffer
  633. cmfi1a:    cmp    ah,escape        ; escape?
  634.     je    cmfi1b            ; e = yes
  635.     jmp    cmfil2            ; process other terminators
  636. cmfi1b:    call    esceoc            ; do normal escape end-of-command
  637.     jmp    cmfil0a            ; try again
  638.  
  639. cmfil2:    cmp    ah,'?'            ; asking a question?
  640.     je    cmfil3            ; e = yes
  641.     xchg    dx,bx            ; re-interchange bx and dx
  642.     mov    comand.cmwhite,0    ; clear whitespace flag
  643.     mov    comand.cmper,0        ; reset to variable recognition
  644.     mov    comand.cmkeep,0        ; do not keep Take file open
  645.     mov    bx,cmptab        ; pointer into destination array
  646.     mov    byte ptr[bx],0        ; put null terminator into the buffer
  647.     inc    bx
  648.     mov    ah,byte ptr cmsiz    ; return count in AH
  649.     mov    dx,cmptab        ; return updated pointer
  650.     xchg    dx,bx            ; re-interchange bx and dx
  651.     clc
  652.     ret                ; return success
  653.  
  654. cmfil3:    inc    cmrptr            ; count the ?
  655.     cmp    cmsiz,0            ; Is "?" first char?
  656.     jne    cmfil5            ; ne = no, just add to buffer
  657.     dec    cmrptr
  658.     mov    cmsiz,0
  659.     cmp    cmhlp,0            ; external help given?
  660.     jne    cmfil3a            ; ne = yes
  661.     mov    cmhlp,offset cmin00    ; confirm with c/r msg
  662. cmfil3a:jmp    cmkyhlp            ; do help process
  663.  
  664. cmfil5:    inc    cmsiz            ; increment the count
  665.     mov    bx,cmptab        ; pointer into destination array
  666.     mov    [bx],ah            ; put char into the buffer
  667.     inc    bx
  668.     mov    cmptab,bx
  669.     jmp    cmfil1            ; the end of cmfil0
  670. cmfil0    endp
  671.  
  672. ; This routine gets a confirm (CR) and displays any extra non-blank text.
  673. ; errflag non-zero means suppress "extra text" display in this routine
  674. ; because another routine is handling errors.
  675. cmcfrm    proc    near
  676.     mov    comand.cmper,1        ; do not react to \%x substitutions
  677. cmcfr1:    mov    cmsflg,0ffh        ; set space-seen flag (skip spaces)
  678.     call    cmgtch            ; get a char
  679.     push    cmrptr
  680.     pop    temp            ; remember first non-space position
  681.     jc    cmcfr4            ; c = terminator
  682.     dec    temp            ; backup to text char
  683. cmcfr3:    mov    cmsflg,0ffh        ; set space-seen flag (skip spaces)
  684.     call    cmgtch
  685.     jnc    cmcfr3            ; read until terminator
  686. cmcfr4:    cmp    ah,' '
  687.     je    cmcfr3            ; ignore ending on space
  688.     cmp    ah,escape        ; escape?
  689.     jne    cmcfr5            ; ne = no
  690.     call    esceoc            ; do standard end of cmd on escape
  691.     mov    ax,cmrptr
  692.     cmp    ax,temp            ; started text yet?
  693.     je    cmcfr1            ; e = no
  694.     jmp    short cmcfr3        ; try again
  695. cmcfr5: cmp    ah,'?'            ; curious?
  696.         jne    cmcfr6            ; ne = no
  697.     mov    cmhlp,offset cmin00    ; msg Confirm with c/r
  698.     mov    cmsiz,0            ; no keyword
  699.     mov    errflag,0
  700.     jmp    cmkyhlp            ; do help
  701. cmcfr6:    cmp    ah,cr            ; the confirmation char?
  702.     jne    cmcfr3            ; ne = no
  703.     cmp    errflag,0        ; already doing one error?
  704.     jne    cmcfr7            ; ne = yes, skip this one
  705.     mov    cx,cmrptr        ; pointer to terminator
  706.     mov    dx,temp            ; starting place
  707.     sub    cx,dx            ; end minus starting point = length
  708.     cmp    cx,0            ; string present?
  709.     jle    cmcfr7            ; le = nothing to display
  710.     push    dx            ; save source pointer
  711.     mov    ah,prstr
  712.     mov    dx,offset cmer07    ; ?Ignoring extras
  713.     int    dos
  714.     pop    dx
  715.     mov    bx,1            ; stdout handle, cx=count, dx=src ptr
  716.     mov    ah,write2        ; allow embedded dollar signs
  717.     int    dos
  718.     mov    ah,prstr
  719.     mov    dx,offset cmer08    ; trailer msg
  720.     int    dos
  721. cmcfr7:    mov    errflag,0
  722.     mov    comand.cmper,0        ; reset to variable recognition
  723.     mov    comand.cmkeep,0
  724.     clc                ; return confirmed
  725.     ret
  726. cmcfrm    endp
  727.  
  728. ;;; Routines to get and edit incoming text.
  729.  
  730. ; Detect '\%x' (x = '0' or above) and substitute the matching Macro string
  731. ; in place of the '\%x' phrase in the user's buffer. If comand.cmper != 0
  732. ; then treat '\%' as literal characters. If no matching parameter exists
  733. ; just remove '\%x'. Returns carry clear if nothing done, else carry set and
  734. ; new text already placed in user's buffer. comand.cmwptr and comand.cmcnt
  735. ; are updated. Uses depth-first recursion algorithm. All registers preserved.
  736. subst    proc    near
  737.     cmp    comand.cmper,0        ; should we recognize '\%'?
  738.     jne    subst2            ; ne = no, treat as literals
  739.     cmp    ah,'\'            ; is it the first char of the pattern?
  740.     jne    subst1            ; ne = no, try next
  741.     mov    subcnt,1        ; say first is matched
  742.     jmp    short subst2        ; exit successfully
  743. subst1:    cmp    subcnt,1        ; first char matched already?
  744.     ja    subst3            ; a = first two have been matched
  745.     jb    subst2            ; b = none yet
  746.     inc    subcnt            ; assume a match follows
  747.     cmp    ah,'%'            ; second match char, same?
  748.     je    subst2            ; e = yes
  749.     mov    subcnt,0        ; mismatch, clear match counter
  750. subst2:    clc                ; carry clear = no substitution done
  751.     ret
  752. subst3:    mov    subcnt,0        ; clear match counter
  753.     cmp    ah,'0'            ; third char is '0' or above?
  754.     jb    subst2            ; b = out of range, no match
  755.     push    bx            ; save working regs
  756.     push    cx
  757.     push    es
  758.     sub    cmrptr,3        ; reread commands where backslash was
  759.     call    bufreset        ; reset buffer to this point
  760.     push    cmptab            ; save current keyword parsing parms
  761.     push    cmsptr
  762.     push    cmsiz
  763.     mov    bx,cmrptr        ; points at backslash
  764.     mov    cmsptr,bx        ; direct keyword routine to it
  765.     mov    cmsiz,3            ; three bytes (\%x) of user text
  766.     mov    cmptab,offset mcctab     ; use Macro table for new text
  767.     call    getkw            ; get ptr, bx, to matching keyword
  768.     pop    cmsiz            ; restore borrowed keyword parameters
  769.     pop    cmsptr
  770.     pop    cmptab
  771.     jc    substx            ; c = not found, keep after pops
  772.     mov    cl,[bx]            ; length of found word
  773.     mov    ch,0
  774.     add    cx,2            ; plus count field and '$'
  775.     add    bx,cx            ; point to 16 bit value (string ptr)
  776.     mov    es,[bx]            ; point to string structure segment
  777.     mov    bx,0
  778.     mov    cl,es:[bx]        ; length of string
  779.     mov    ch,0
  780.     inc    bx            ; skip length byte, es:bx=string addr
  781.     jcxz    substx            ; z = nothing left to transfer
  782.     cld
  783. subst4:    mov    ah,es:[bx]        ; get a char
  784.     inc    bx
  785.     push    di            ; assume di is used by other routines
  786.     mov    di,cmrptr
  787.     mov    [di],ah            ; store char (without es:di)
  788.     inc    di
  789.     mov    cmwptr,di        ; where to store next char
  790.     mov    cmrptr,di        ; move read pointer too
  791.     cmp    di,offset cmdbuf+size cmdbuf ; reached max buffer size?
  792.     pop    di
  793.     jae    subst6            ; ae = yes, no more room
  794.     cmp    sp,20*2            ; still some stack space?
  795.     jle    subst6            ; le = insufficient room
  796.     call    SUBST            ; rescan what we stored (recursion)
  797.     jnc    subst5            ; nc = no substitution, so no kbd test
  798.     push    ax            ; break out of loops with Control-C
  799.     push    dx
  800.     mov    ah,constat        ; check console status for Control-C
  801.     int    dos            ;  our control-break handler sees it
  802.     pop    dx            ;  and cmgetc reads it
  803.     pop    ax
  804. subst5:    loop    subst4
  805. substx:    pop    es
  806.     pop    cx
  807.     pop    bx
  808.     stc                ; set carry to say have stored chars
  809.     ret
  810. subst6:    mov    ah,prstr
  811.     mov    dx,offset stkmsg    ; out of work space msg
  812.     int    dos
  813.     jmp    prserr            ; and declare parse error
  814. subst    endp
  815.  
  816. ; Read chars from Take file, keyboard, or redirected stdin. Edit and remove
  817. ; BS & DEL, Tab becomes space, act on Control-C, pass Control-U and Control-W.
  818. ; Do echoing unless comand.cmquiet is non-zero. Do semicolon comments in Take
  819. ; and indirect stdin files (\; means literal semicolon). Return char in AL.
  820. CMGETC    proc    near            ; Basic raw character reader
  821. cmget01:cmp    prevch,0        ; left over char yet to be exported?
  822.     je    cmget02            ; e = no
  823.     mov    al,prevch        ; get old char
  824.     mov    prevch,0        ; clear storage
  825.     jmp    cmget6            ; analyze it
  826. cmget02:cmp    taklev,0        ; in a Take file?
  827.     jne    cmget1            ; ne = yes, do Take reader section
  828.     call    isdev            ; is stdin a device or a file?
  829.     jnc    cmget20            ; nc = file (redirection of stdin)
  830.     jmp    cmget10            ; c = device, do separately
  831.  
  832. cmget20:call    iseof            ; see if file is empty
  833.     jc    cmget21            ; c = EOF on disk file
  834.     mov    ah,coninq        ; read the char from file, not device
  835.     int    dos
  836.     cmp    al,cr            ; is it a cr?
  837.     je    cmget01            ; yes, ignore and read next char
  838.     cmp    al,ctlz            ; Control-Z?
  839.     je    cmget21            ; e = yes, same as EOF here
  840.     cmp    al,lf            ; LF's end lines from disk files
  841.     jne    cmget8            ; ne = not LF, pass along as is
  842.     mov    al,cr            ; make LF a CR for this parser
  843.     call    iseof            ; see if this is the last char in file
  844.     jnc    cmget8            ; nc = not EOF, process new CR
  845. cmget21:mov    flags.extflg,1        ; EOF on disk file, set exit flag
  846.     jmp    short cmget6        ; do echoing and return
  847.  
  848. cmget1:    push    bx            ; read from Take file
  849.     mov    bx,takadr        ; offset of this Take structure
  850.     cmp    [bx].takcnt,0        ; bytes remaining in Take buffer
  851.     jne    cmget4            ; ne = not empty
  852.     cmp    [bx].taktyp,0feh    ; type of Take (file?)
  853.     jne    cmget3            ; ne = no (macro)
  854.     call    takrd            ; read another buffer
  855.     cmp    [bx].takcnt,0        ; anything in the buffer?
  856.     jne    cmget4            ; ne = yes
  857. cmget3:    pop    bx            ; clear stack
  858.     jmp    cmget5            ; close take file
  859.  
  860. cmget4:    push    si
  861.     push    es
  862.     mov    es,[bx].takbuf        ; segment of Take buffer
  863.     mov    si,[bx].takptr        ; current offset in Take buffer
  864.     mov    al,es:[si]        ; read a char from Take buffer
  865.     inc    si
  866.     mov    [bx].takptr,si        ; move buffer pointer
  867.     dec    [bx].takcnt        ; decrease number of bytes remaining
  868.     pop    es
  869.     pop    si
  870.     pop    bx
  871.     cmp    al,ctlz            ; Control-Z?
  872.     jne    cmget6            ; ne = no
  873. cmget5:    cmp    comand.cmkeep,0        ; keep Take/macro open after eof?
  874.     jne    cmget5a            ; ne = yes
  875.     call    takclos            ; close take file
  876. cmget5a:mov    al,cr            ; report cr as last char
  877.     mov    noparse,0        ; and say end of comment
  878.                     ; start common code
  879. cmget6:    cmp    al,lf            ; line feed?
  880.     jne    cmget8            ; ne = no
  881.     jmp    cmget01            ; yes, ignore and read another char
  882.                     ; handle comments (echo but not parse)
  883. cmget8:    cmp    noparse,0        ; parsing?
  884.     jne    cmget9            ; ne = yes, do echo and no parse
  885.     cmp    prevch,0        ; have previous char to analyze?
  886.     jne    cmget8c            ; ne = yes
  887.     cmp    al,'\'            ; start of '\;'?
  888.     jne    cmget8a            ; ne = no
  889.     mov    prevch,al        ; yes, maybe. save '\' til later
  890.     jmp    cmget02            ; read next char
  891. cmget8a:cmp    al,';'            ; possible start of comment?
  892.     jne    cmget8e            ; no, export al
  893.     mov    al,' '            ; replace ';' with space for comment
  894.     jmp    cmget9            ; go start a comment
  895.  
  896. cmget8c:cmp    al,';'            ; end of '\;'?
  897.     je    cmget8d            ; e = yes, omit leading backslash
  898.     xchg    prevch,al        ; no, save new, recover old '\'
  899.     jmp    short cmget8e        ; export '\'
  900. cmget8d:mov    prevch,0        ; clear old '\'
  901. cmget8e:jmp    cmget12            ; do parsing
  902.  
  903.                     ; echo comment
  904. cmget9:    cmp    flags.takflg,0        ; echoing take files?
  905.     je    cmget9a            ; e = no
  906.     push    ax
  907.     push    dx
  908.     mov    ah,conout        ; echo current char
  909.     mov    dl,al
  910.     int    dos
  911.     pop    dx
  912.     pop    ax
  913. cmget9a:mov    noparse,0        ; cr ends comment
  914.     cmp    al,cr            ; end of comment?
  915.     je    cmget13            ; e = yes, export cr
  916.     mov    noparse,1        ; still in comment
  917.     jmp    cmget01            ; read more chars
  918.  
  919.                     ; read from tty device
  920. cmget10:mov    ah,coninq        ; Get a char from device, not file
  921.     int    dos            ;  with no echoing
  922.     or    al,al
  923.     jnz    cmget12            ; ignore null bytes of special keys
  924.     int    dos            ; read and discard scan code byte
  925.     jmp    cmget10            ; try again
  926.  
  927. cmget12:cmp    al,'C'and 1Fh        ; Control-C?
  928.     je    cmget14            ; e = yes
  929.     cmp    al,TAB            ; tab is replaced by space
  930.     jne    cmget13            ; ne = not tab
  931.     mov    al,' '
  932.     ret
  933. cmget13:cmp    al,LF            ; LF becomes CR interactively
  934.     jne    cmget13a
  935.     mov    al,CR
  936. cmget13a:ret                ; normal exit, char is in AL
  937.  
  938. cmget14:mov    ah,prstr        ; Control-C handler
  939.     push    dx
  940.     mov    dx,offset ctcmsg    ; show Control-C
  941.     int    dos
  942.     pop    dx
  943.     mov    prevch,0
  944.     mov    flags.cxzflg,'C'    ; tell others the news
  945.     mov    sp,cmdstk        ; restore command entry stack pointer
  946.     stc
  947.     ret                ;  and fail immediately (a longjmp)
  948. cmgetc    endp
  949.  
  950. ; Read chars from user (cmgetc). Detect terminators. Reads from buffer
  951. ; comand.cmbuf. Set read pointer comand.cmrptr to next free buffer byte if
  952. ; char is not a terminator: chars CR, LF, FF, '?' (returns carry set for
  953. ; terminators). Do ^U, ^W editing, convert FF to CR plus clear screen.
  954. ; Edit "-<cr>" as line continuation, "\-<cr>" as "-<end of line>".
  955. ; Return char in AH.
  956. CMINBF    proc    near            ; Buffer reader, final editor
  957.     push    di
  958.     mov    di,cmwptr        ; write pointer
  959.     cmp    di,offset cmdbuf+size cmdbuf-3 ; max buffer size - 3
  960.     pop    di
  961.     jb    cminb1            ; b = not full
  962.     mov    ah,conout        ; almost full, notify user
  963.     push    dx
  964.     mov    dl,bell
  965.     int    dos
  966.     mov    dx,offset cmdbuf+size cmdbuf
  967.     cmp    cmrptr,dx        ; max buffer size?
  968.     pop    dx
  969.     jb    cminb1            ; b = more room
  970.     mov    ah,prstr
  971.     push    dx
  972.     mov    dx,offset cmer09    ; command too long
  973.     int    dos
  974.     pop    dx
  975.     jmp    prserr            ; overflow = parse error
  976.  
  977. cminb1:    push    bx
  978.     mov    bx,cmrptr        ; read pointer
  979.     mov    ah,[bx]            ; get current command char while here
  980.     cmp    bx,cmwptr        ; do we need to read more?
  981.     pop    bx            ; no if cmrptr < cmwptr
  982.     jb    cminb2            ; b: cmrptr < cmwptr (have extra here)
  983.     call    cmgetc            ; no readahead, read another into al
  984.     mov    ah,al            ; keep char in 'ah'
  985.     push    bx
  986.     mov    bx,cmwptr        ; Get the pointer into the buffer
  987.     mov    [bx],ah            ; Put it in the buffer
  988.     inc    bx
  989.     mov    cmwptr,bx        ; inc write pointer
  990.     pop    bx
  991.                     ; Char to be delivered is in ah
  992. cminb2:    cmp    ah,'W' and 1fh        ; Is it a ^W?
  993.     jne    cminb3
  994.     call    cntrlw            ; Kill the previous word
  995.     jnc    cminbf            ; nc = no change, get another char
  996.     jmp    repars            ; need a new command scan (cleans stk)
  997.  
  998. cminb3:    cmp    ah,'U' and 1fh        ; Is it a ^U?
  999.     jne    cminb3a            ; ne = no
  1000.     mov    cmwptr,offset cmdbuf    ; reset buffer write pointer
  1001.     jmp    repars            ; Go start over (cleans stack)
  1002.                     ; BS and DEL
  1003. cminb3a:cmp    ah,DEL            ; Delete code?
  1004.     je    cminb3b            ; e = yes
  1005.     cmp    ah,BS            ; Backspace (a delete operator)?
  1006.     jne    cminb4            ; ne = no
  1007. cminb3b:call    bufdel            ; delete char from buffer
  1008.     jc    cminb3c            ; c = did erasure
  1009.     jmp    cminbf            ; no erasure, ignore BS, get more
  1010. cminb3c:jmp    repars            ; could have deleted previous token
  1011.  
  1012. cminb4:    push    bx            ; look for hyphen or \hyphen
  1013.     cmp    ah,cr            ; check for hyphen line continuation
  1014.     jne    cminb4b            ; ne = not end of line
  1015.     mov    bx,cmwptr        ; Get the pointer into the buffer
  1016.     cmp    bx,offset cmdbuf-2    ; do we have a previous char?
  1017.     jb    cminb4b            ; b = no
  1018.     cmp    byte ptr[bx-2],'-'    ; previous char was a hyphen?
  1019.     jne    cminb4b            ; ne = no
  1020.     pop    bx
  1021.     call    bufdel            ; delete the hyphen
  1022.     jmp    repars
  1023. cminb4b:pop    bx
  1024.                     ; Echoing done here
  1025.     cmp    comand.cmquiet,0    ; quiet mode?
  1026.     jne    cminb5            ; yes, skip echoing
  1027.     cmp    taklev,0        ; in a take file?
  1028.     je    cminb4a            ; e = no
  1029.     cmp    flags.takflg,0        ; echo take file?
  1030.     je    cminb5            ; e = no
  1031. cminb4a:push    ax            ; save the char
  1032.     cmp    ah,' '            ; printable?
  1033.     jae    cminb4c            ; yes, no translation needed
  1034.     cmp    ah,cr            ; this is printable
  1035.     je    cminb4c
  1036.     cmp    ah,lf
  1037.     je    cminb4c
  1038.     cmp    ah,escape        ; escape?
  1039.     je    cminb4d            ; do not echo this character
  1040.     push    ax            ; show controls as caret char
  1041.     push    dx
  1042.     mov    dl,5eh            ; caret
  1043.     mov    ah,conout
  1044.     int    dos
  1045.     pop    dx
  1046.     pop    ax
  1047.     add    ah,'A'-1        ; make control code printable
  1048. cminb4c:push    dx
  1049.     mov    dl,ah
  1050.     mov    ah,conout
  1051.     int    dos            ; echo it ourselves
  1052.     pop    dx
  1053. cminb4d:pop    ax            ; and return char in ah
  1054.  
  1055. cminb5:    cmp    ah,cr            ; Is it a carriage return?
  1056.     je    cminb6
  1057.     cmp    ah,lf            ; Is it a line feed?
  1058.     je    cminb6
  1059.     cmp    ah,ff            ; Is it a formfeed?
  1060.     jne    cminb7            ; none of the above, report bare char
  1061.     call    cmblnk            ; FF: clear the screen and
  1062.     push    bx
  1063.     push    cx
  1064.     push    dx
  1065.     call    locate            ; Home the cursor
  1066.     mov    bx,cmwptr        ; make the FF parse like a cr
  1067.     mov    byte ptr [bx-1],cr    ; pretend a carriage return were typed
  1068.     pop    dx
  1069.     pop    cx
  1070.     pop    bx
  1071. cminb6: cmp    cmwptr,offset cmdbuf    ; parsed any chars yet?
  1072.     jne    cminb7            ; ne = yes
  1073.     cmp    comand.cmcr,0        ; bare cr's allowed?
  1074.     jne    cminb7            ; ne = yes
  1075.     jmp    prserr            ; If not, just start over
  1076. cminb7:    clc
  1077.     ret
  1078. cminbf    endp
  1079.  
  1080. ; Read chars from cminbf. Comand.cmrptr points to next char to be read.
  1081. ; Compresses repeated spaces if cmsflg is non-zero. Exit with cmrptr pointing
  1082. ; at a terminator or otherwise at next free slot.
  1083. ; Non-space then space acts as a terminator but cmrptr is incremented.
  1084. ; Substitution variables, '\%x', are detected and expanded. Return char in AH.
  1085.  
  1086. CMGTCH    proc    near            ; return char in AH, from rescan buf
  1087.     call    cminbf            ; get char from buffer or user
  1088.     push    bx
  1089.     mov    bx,cmrptr        ; get read pointer into the buffer
  1090.     mov    ah,[bx]            ; read the next char
  1091.     inc    bx
  1092.     mov    cmrptr,bx        ; where to read next time
  1093.     pop    bx
  1094.     call    subst            ; examine for text substitution
  1095.     jnc    cmgtc1            ; nc = no substitutions done
  1096.     jmp    repars            ; reparse line with new material
  1097.  
  1098. cmgtc1:    cmp    ah,' '            ; Is it a space?
  1099.     jne    cmgtc3            ; ne = no
  1100.     cmp    cmsflg,0        ; space flag, was last char a space?
  1101.     jne    cmgtch            ; ne = yes, get another char
  1102.     mov    cmsflg,0FFH        ; Set the space(s)-seen flag
  1103.     mov    ah,' '            ; character for caller
  1104.     stc                ; set carry for terminator
  1105.     ret                ; return space as a terminator
  1106. cmgtc3: mov    cmsflg,0        ; clear the space-seen flag
  1107.     cmp    ah,escape        ; terminators remain in buffer but
  1108.     je    cmgtc4            ;  are ready to be overwritten
  1109.     cmp    ah,'?'            ; Is the user curious?
  1110.     jne    cmgtc3a            ; ne = no
  1111.     cmp    taklev,0        ; in a Take file?
  1112.     jne    cmgtc3b            ; ne = yes, make query ordinary char
  1113.     je    cmgtc4
  1114. cmgtc3a:cmp    ah,cr
  1115.     je    cmgtc4
  1116.     cmp    ah,lf
  1117.     je    cmgtc4
  1118.     cmp    ah,ff
  1119.     je    cmgtc4
  1120. cmgtc3b:clc                ; carry clear for non-terminator
  1121.     ret
  1122. cmgtc4: dec    cmrptr            ; point at terminating char
  1123.     stc                ; set carry to say it is a terminator
  1124.     ret
  1125. cmgtch    endp
  1126.  
  1127. ; Reset comand.cmdbuf write pointer (.cmwptr) to where the read pointer
  1128. ; (.cmrptr) is now. Discards material not yet read.
  1129. bufreset proc    near
  1130.     push    cmrptr            ; where next visible char is read
  1131.     pop    cmwptr            ; where new char goes in buffer
  1132.     ret
  1133. bufreset endp
  1134.  
  1135. ; Delete character from screen and adjust buffer. Returns carry clear if
  1136. ; no erasure, carry set otherwise.
  1137. bufdel    proc    near
  1138.     dec    cmrptr            ; remove previous char from buffer
  1139.     cmp    cmrptr,offset cmdbuf    ; back too far?
  1140.     jae    bufde2            ; ae = no, material can be erased
  1141.     mov    cmrptr,offset cmdbuf    ; set to start of buffer
  1142.     call    bufreset        ; reset buffer
  1143.     clc                ; say no erasure
  1144.     ret
  1145. bufde2:    call    bufreset        ; reset buffer
  1146.     stc                ; say did erasure
  1147.     ret
  1148. bufdel    endp
  1149.  
  1150. ; Come here is user types ^W when during input. Remove word from buffer.
  1151. cntrlw    proc    near
  1152.     push    ax
  1153.     push    cx
  1154.     push    dx
  1155.     mov    cx,cmrptr        ; char beyond what user sees
  1156.     mov    cmwptr,cx        ; truncate buffer there
  1157.     sub    cx,offset cmdbuf    ; compute chars in buffer
  1158.     clc                ; say have not yet modified line
  1159.     jcxz    ctlw2            ; z = nothing to do, exit no-carry
  1160.     push    es
  1161.     std                ; scan backward
  1162.     mov    ax,ds
  1163.     mov    es,ax            ; point to the data are
  1164.     mov    di,cmwptr        ; looking from here
  1165.     dec    di
  1166.     mov    al,' '
  1167.     repe    scasb            ; look for non-space
  1168.     je    ctlw1            ; all spaces, nothing to do
  1169.     inc    di            ; move back to non-space
  1170.     inc    cx
  1171.     repne    scasb            ; look for a space
  1172.     jne    ctlw1            ; no space, leave ptrs alone
  1173.     inc    di
  1174.     inc    cx            ; skip back over space
  1175. ctlw1:    inc    di
  1176.     pop    es
  1177.     cld                ; reset    direction flag
  1178.     mov    cmwptr,di        ; update pointer
  1179.     stc                ; set carry to say modified line
  1180. ctlw2:    pop    dx
  1181.     pop    cx
  1182.     pop    ax
  1183.     ret
  1184. cntrlw    endp
  1185.  
  1186. ; Jump to REPARS to do a rescan of the existing buffer.
  1187. ; Jump to PRSERR on a parsing error (quits command, clears old read material)
  1188.  
  1189. PRSERR    PROC NEAR 
  1190.     mov    cmwptr,offset cmdbuf    ; initialize write pointer
  1191.     mov    ah,prstr
  1192.     mov    dx,offset crlf        ; leave old line, start a new one
  1193.     int    dos
  1194.                     ; reparse current line
  1195. REPARS:    mov    cmrptr,offset cmdbuf    ; reinit read pointer
  1196.     mov    comand.cmper,0        ; reset to variable recognition
  1197.     mov    cmsflg,0FFH        ; strip leading spaces
  1198.     cmp    taklev,0        ; in Take cmd?
  1199.     je    prser2            ; e = no
  1200.     cmp    flags.takflg,0        ; echo contents of Take file?
  1201.     je    prser3            ; e = no
  1202. prser2:    call    ctlu            ; clear display's line, reuse it
  1203.     mov    dx,comand.cmprmp    ; display the asciiz prompt
  1204.     call    prtasz
  1205. prser3:    mov    bx,0ffffh        ; returned keyword value
  1206.     mov    sp,comand.cmostp    ; set new sp to old one
  1207.     jmp    comand.cmrprs        ; jump to just before the prompt call
  1208. PRSERR    ENDP
  1209.  
  1210. ; This routine prints the prompt and specifies the reparse address.
  1211. ; Enter with pointer to prompt string in dx. 
  1212. PROMPT    PROC  NEAR
  1213.     mov    comand.cmprmp,dx    ; save the prompt
  1214.     pop    ax            ; Get the return address
  1215.     mov    comand.cmrprs,ax    ; Save as address to go to on reparse
  1216.     mov    comand.cmostp,sp    ; Save for later restoration
  1217.     push    ax            ; Put it on the stack again
  1218.     mov    ax,offset cmdbuf
  1219.     mov    cmwptr,ax        ; reset buffer read/write pointers
  1220.     mov    cmrptr,ax
  1221.     mov    ax,0
  1222.     mov    comand.cmper,0        ; allow substitutions
  1223.     mov    cmsflg,0FFH        ; remove leading spaces
  1224.     cmp    flags.takflg,0        ; look at Take flag
  1225.     jne    promp1            ; ne=supposed to echo, skip this check
  1226.     cmp    taklev,0        ; inside a take file?
  1227.     je    promp1            ; no, keep going
  1228.     ret                ; yes, return
  1229. promp1:    mov    ah,prstr
  1230.     mov    dx,offset crlf
  1231.     int    dos
  1232.     mov    dx,comand.cmprmp    ; prompt pointer
  1233.     call    prtasz            ; show asciiz prompt string
  1234.     clc
  1235.     ret
  1236. PROMPT    ENDP
  1237.  
  1238. ISDEV    PROC    NEAR            ; Set carry if STDIN is non-disk
  1239.     push    ax
  1240.     push    bx
  1241.     push    dx
  1242.     xor    bx,bx            ; handle 0 is stdin
  1243.     xor    al,al            ; get device info
  1244.     mov    ah,ioctl
  1245.     int    dos
  1246.     rcl    dl,1            ; put ISDEV bit into the carry bit
  1247.     pop    dx            ; carry is set if device
  1248.     pop    bx
  1249.     pop    ax
  1250.     ret                ; carry set if device
  1251. ISDEV    ENDP
  1252.  
  1253. ISEOF    PROC    NEAR            ; Set carry if STDIN is at EOF
  1254.     push    ax            ;  but only if stdin is a non-device
  1255.     push    bx
  1256.     push    dx
  1257.     xor    bx,bx            ; handle 0 is stdin
  1258.     xor    al,al            ; get device info
  1259.     mov    ah,ioctl
  1260.     int    dos
  1261.     mov    ah,ioctl
  1262.     mov    al,6            ; get handle input status, set al
  1263.     test    dl,80h            ; bit set if handle is for a device
  1264.     jnz    iseof1            ; nz = device, always ready (al != 0)
  1265.     int    dos
  1266. iseof1:    or    al,al            ; EOF?
  1267.     pop    dx
  1268.     pop    bx
  1269.     pop    ax
  1270.     jnz    iseof2            ; nz = no
  1271.     stc                ; set carry for eof
  1272.     ret
  1273. iseof2:    clc                ; clear carry for not-eof
  1274.     ret
  1275. ISEOF    ENDP
  1276. ; Convert ascii characters in al and ah to lowercase. [jrd]
  1277. ; All registers are preserved except AX, of course.
  1278.  
  1279. TOLOWR PROC NEAR
  1280.     cmp    ah,'A'            ; less that cap A?
  1281.     jl    tolow1            ; l = yes. leave untouched
  1282.     cmp    ah,'Z'+1        ; more than cap Z?
  1283.     jns    tolow1            ; ns = yes
  1284.     or    ah,20H            ; convert to lowercase
  1285. tolow1:    cmp    al,'A'            ; less that cap A?
  1286.     jl    tolow2            ; l = yes. leave untouched
  1287.     cmp    al,'Z'+1        ; more than cap Z?
  1288.     jns    tolow2            ; ns = yes
  1289.     or    al,20H            ; convert to lowercase
  1290. tolow2:    ret
  1291. TOLOWR    endp
  1292.  
  1293. ; Parse control sequences and device control strings.
  1294. ; Expect CSI, Escape [, or DCS lead-in characters to have been read.
  1295. ; Puts numerical Parameters in array param (16 bits, count is nparam) and
  1296. ;  a single letter Parameter in lparam, (Parameters are all ASCII column 3)
  1297. ;  Intermediate characters in array inter (count is ninter), (ASCII column 2)
  1298. ;  Final character in AL (ASCII columns 4-7).
  1299. ; Invoke by setting state to offset atparse, set pardone to offset of
  1300. ; procedure to jump to after reading Final char (0 means do just ret)
  1301. ; and optionally setting parfail to address to jump to if parsing failure.
  1302. ; When the Final char has been accepted this routine jumps to label held in
  1303. ; pardone for final action. Before the Final char has been read successful
  1304. ; operations return carry clear.
  1305. ; Failure exits are carry set, and an optional jump through parfail (if 
  1306. ; non-zero) or a return.
  1307.  
  1308. atparse    proc    near
  1309.     mov    bx,parstate        ; get parsing state
  1310.     or    bx,bx            ; have any state?
  1311.     jnz    atpars1            ; nz = have a state
  1312.     call    atpclr            ; do initialization
  1313.     mov    parstate,offset atparm    ; next state is get Parameters
  1314.     mov    bx,parstate        ; get initial state
  1315. atpars1:call    bx            ; execute it
  1316.     jc    atpfail            ; c = failure
  1317.     cmp    parstate,offset atpdone    ; parsed final char?
  1318.     je    atpdone            ; e = yes
  1319.     ret                ; no, wait for another char
  1320.  
  1321.                 ; successful conclusion, final char is in AL
  1322. atpdone:mov    parstate,0        ; reset parsing state
  1323.     cmp    pardone,0        ; separate return address defined?
  1324.     jne    atpdon1            ; ne = yes
  1325.     clc
  1326.     ret                ; else just return
  1327. atpdon1:clc
  1328.     jmp    pardone            ; jmp to supplied action routine
  1329.  
  1330. atpfail:mov    parstate,0        ; failed, reset parser to normal state
  1331.     cmp    parfail,0        ; jump address specified?
  1332.     je    atpfail1        ; e = no
  1333.     jmp    parfail            ; yes, exit this way
  1334. atpfail1:stc
  1335.     ret
  1336.                     ; parsing workers
  1337. atparm:    cmp    ninter,0        ; Parameter, started intermediate yet?
  1338.     jne    atinter            ; ne = yes, no more parameters
  1339.     cmp    al,';'            ; argument separator?
  1340.     jne    atparm3            ; ne = no
  1341.     mov    ax,nparam        ; number of Parameters
  1342.     inc    ax            ; say a new one
  1343.     cmp    ax,maxparam        ; too many?
  1344.     jb    atparm2            ; b = no, continue
  1345.     stc                ; set carry to say failed
  1346.     ret                ; too many, ignore remainder
  1347. atparm2:mov    nparam,ax        ; say doing another Parameter
  1348.     clc
  1349.     ret
  1350.  
  1351. atparm3:mov    ah,al            ; copy char
  1352.     and    ah,not 0fh        ; ignore low nibble
  1353.     cmp    ah,30h            ; column 3, row 0? (30h='0')
  1354.     jne    atparm8            ; ne = no, check Intermediate/Final
  1355.     cmp    al,'9'            ; digit?
  1356.     ja    atparm5            ; a = no, check letter Parameters
  1357.     sub    al,'0'            ; ascii to binary
  1358.     mov    bx,nparam        ; current parameter number
  1359.     shl    bx,1            ; convert to word index
  1360.     mov    cx,param[bx]        ; current parameter value
  1361.     shl    cx,1            ; multiply by 10.  2 * cl
  1362.     push    bx
  1363.     mov    bx,cx            ; save 2 * cl
  1364.     shl    cx,1            ; 4 * cl
  1365.     shl    cx,1            ; 8 * cl
  1366.     add    cx,bx            ; 10 * cl
  1367.     pop    bx
  1368.     add    cl,al            ; add new digit
  1369.     adc    ch,0
  1370.     jnc    atparm4            ; nc = no carry out (65K or below)
  1371.     mov    cx,0ffffh        ; set to max value
  1372. atparm4:mov    param[bx],cx        ; current Parameter value
  1373.     clc
  1374.     ret
  1375.                     ; check non-numeric Parameters
  1376. atparm5:cmp    al,'?'            ; within column 3?
  1377.     ja    atfinal            ; a = no, check Final char
  1378.     mov    lparam,al        ; store non-numeric Parameter
  1379.     clc
  1380.     ret
  1381.  
  1382. atparm8:inc    nparam            ; non-column 3, end of Parameters
  1383.     cmp    al,3fh            ; column 4 and above?
  1384.     ja    atfinal            ; a = yes, check Final char
  1385.     
  1386.                     ; Intermediate chars, all in Column 2
  1387. atinter:mov    parstate,offset atinter    ; next state (intermediate)
  1388.     cmp    al,';'            ; argument separator?
  1389.     jne    atinte1            ; ne = no
  1390.     mov    ax,ninter        ; number of Intermediates 
  1391.     cmp    ax,maxinter        ; too many?
  1392.     jb    atinte2            ; b = no, continue
  1393.     stc                ; carry = failed
  1394.     ret                ; too many, ignore remainder
  1395. atinte1:test    al,not 2fh        ; column two = 20h - 2fh?
  1396.     jnz    atfinal            ; nz = not an Intermediate, try Final
  1397.     mov    bx,ninter        ; current Intermediate slot number
  1398.     mov    inter[bx],al        ; current Intermediate value
  1399. atinte2:inc    ninter            ; say doing another Intermediate
  1400.     clc
  1401.     ret
  1402.  
  1403. atfinal:cmp    al,40h            ; Final character, range is 40h to 7fh
  1404.     jb    atfina1            ; b = out of range
  1405.     cmp    al,7fh
  1406.     ja    atfina1            ; a = out of range
  1407.     mov    parstate,offset atpdone    ; next state is "done"
  1408.     clc                ; success, final char is in AL
  1409.     ret
  1410. atfina1:stc                ; c = failed
  1411.     ret
  1412. atparse    endp
  1413.  
  1414. ; Clear Parameter, Intermediate arrays in preparation for parsing
  1415. atpclr    proc    near
  1416.     push    ax
  1417.     push    cx
  1418.     push    di
  1419.     push    es
  1420.     mov    lparam,0        ; clear letter Parameter
  1421.     mov    nparam,0        ; clear Parameter count
  1422.     mov    cx,maxparam        ; number of Parameter slots
  1423.     mov    di,offset param        ; Parameter slots
  1424.     push    ds
  1425.     pop    es            ; use data segment for es:di below
  1426.     cld                ; set direction forward
  1427.     xor    ax,ax            ; get a null
  1428.     rep    stosw            ; clear the slots
  1429.     mov    ninter,ax        ; clear Intermediate count
  1430.     mov    cx,maxinter        ; number of Intermediate slots
  1431.     mov    di,offset inter        ; Intermediate slots
  1432.     rep    stosb            ; clear the slots
  1433.     pop    es
  1434.     pop    di
  1435.     pop    cx
  1436.     pop    ax
  1437.     ret
  1438. atpclr    endp
  1439.  
  1440. ; Dispatch table processor. Enter with BX pointing at table of {char count,
  1441. ; address of action routines, characters}. Jump to matching routine or return.
  1442. ; Enter with AL holding received Final char.
  1443. atdispat proc near
  1444.     mov    cl,[bx]            ; get table length from first byte
  1445.     xor    ch,ch
  1446.     mov    di,bx            ; main table
  1447.     add    di,3            ; point di at first char in table
  1448.     push    es
  1449.     push    ds
  1450.     pop    es            ; use data segment for es:di below
  1451.     cld                ; set direction forward
  1452.     repne    scasb            ; find matching character
  1453.     pop    es
  1454.     je    atdisp2            ; e = found a match, get action addr
  1455.     ret                ; ignore escape sequence
  1456. atdisp2:sub    di,bx            ; distance scanned in table
  1457.     sub    di,4            ; skip count byte, address word, inc
  1458.     shl    di,1            ; convert to word index
  1459.     inc    bx            ; point to address of action routines
  1460.     mov    bx,[bx]            ; get address of action table
  1461.     jmp    word ptr [bx+di]    ; dispatch to the routine
  1462. atdispat endp
  1463. code    ends
  1464.     end
  1465.