home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / archives / msr313src.zip / msscmd.asm < prev    next >
Assembly Source File  |  1993-07-12  |  78KB  |  2,483 lines

  1.     NAME    msscmd
  2. ; File MSSCMD.ASM
  3.     include mssdef.h
  4. ;  Copyright (C) 1985, 1993, Trustees of Columbia University in the 
  5. ;  City of New York.  Permission is granted to any individual or institution
  6. ;  to use this software as long as it is not sold for profit.  This copyright
  7. ;  notice must be retained.  This software may not be included in commercial
  8. ;  products without written permission of Columbia University.
  9. ;
  10. ; Edit history:
  11. ; 27 August 1992 version 3.13
  12. ; 6 Sept 1991 version 3.11
  13. ; 13 March 1991 version 3.10
  14. ; Last edit 21 Oct 1992
  15. ; 5 Jan 1991 Add \$(Environment variable) substitution.
  16. ; 9 Sept 1990 Add \v(named variable) substitution.
  17. ; 21 Nov 1988 Version 2.32
  18. ; 17 Oct 1988 Make command keyword search failure yield global kstatus of
  19. ;  failure status, to inform Take/Macros of a defective command.
  20. ; 4 Aug 1988 Fix keyword count initialization in help display.
  21. ; 1 July 1988 Version 2.31
  22. ; 7 June 1988 Add comand.impdo flag to permit a keyword search failure to
  23. ;  retry as a DO cmd (macro table). Used only by main Kermit command level.
  24. ; 27 May 1988 Allow "-<cr>" as line continuation pair and let "\-<cr>" 
  25. ;  stand for "-<end of line>". Add comand.cmblen to sense buffer overflow on
  26. ;  calls to cmtxt; if comand.cmblen is left at zero then use 128 byte limit
  27. ;  (comand.cmblen is cleared at command end).
  28. ; 15 May 1988 Make :label keywords no-ops, make query ordinary char in TAKEs.
  29. ; 6 May 1988 Show ambiguous keywords at parse time, do graceful interaction.
  30. ; 28 March 1988 Permit '\%x' (x = '0' or above) in commands as 'variables'
  31. ;  (substituted string of text). Words are obtained from Macro table mcctab.
  32. ;  DEF MAC and DO MAC define variables. DEF macro uses comand.cmper > 0 to
  33. ;  allow '\%x'to be stored as a literal. Variables work in any command except
  34. ;  DEF MAC. Major redesign of whole parser. [jrd]
  35. ; 4 March 1988 Rewrite keyword parsing to permit non-alphabetized tables
  36. ;  and 8-bit characters. Add byte comand.cmwhite which when non-zero permits
  37. ;  leading whitespace in cmline and cmword commands; it is reset at command
  38. ;  completion. Move procedure Prompt here from mssker. [jrd]
  39. ; 27 Feb 1988 Add capability of stdin being a file. [jrd]
  40. ; 1 Jan 1988 version 2.30
  41.  
  42.      public comnd, comand, isdev, iseof, prompt, tolowr, valbuf, valtab
  43.     public parstate, pardone, parfail, nparam, param, lparam, ninter
  44.     public inter, atparse, atpclr, atdispat, cspb, dspb, mprompt, nvaltoa
  45.     public savepoff, saveplen, keyboard, fwrtdir, cspb1
  46.  
  47. env    equ    2CH            ; environment address in psp
  48. braceop    equ    7bh            ; opening curly brace
  49. bracecl    equ    7dh            ; closing curly brace
  50.  
  51. data     segment
  52.     extrn    flags:byte, taklev:byte, takadr:word, mcctab:byte
  53.     extrn    kstatus:word, oldtak:byte, errlev:byte, psp:word
  54.     extrn    portval:word, bdtab:byte, tdfmt:byte, machnam:byte
  55.     extrn    verident:word, kstatus:word, errlev:byte, comptab:byte
  56.     extrn    termtb:byte, sescur:word, dosnum:word
  57.  
  58.                 ; Start Patch structure. Must be first.
  59.     even
  60. dspb    dw    code        ; segment values for patcher
  61.     dw    code1
  62.     dw    code2
  63.     dw    data
  64.     db    64 dup (0)    ; data segment patch buffer
  65.                 ;  with space for other material, if req'd
  66.                 ; end of Patch structure
  67. progm    db    'MS-DOS_KERMIT$'    ; for \v(program)
  68. system    db    'MS-DOS$'        ; for \v(system)
  69. keyboard dw    88            ; \v(keyboard) kind of keybd 88/101
  70.  
  71. comand    cmdinfo    <>
  72. cmer00  db      cr,lf,'?Program internal error, recovering$'
  73. cmer01    db    cr,lf,'?More parameters are needed$'
  74. cmer02    db    cr,lf,'?Word "$'
  75. cmer03    db    '" is not usable here$'
  76. cmer04    db    '" is ambiguous$'
  77. cmer07    db    cr,lf,'?Ignoring extra characters "$'
  78. cmer08    db    '"$'
  79. cmer09    db    cr,lf,'?Text exceeded available buffer capacity$'
  80. cmin00  db      ' Press ENTER to execute command$'
  81. cmin01    db    ' Use one of the following words in this position:',cr,lf,'$'
  82. stkmsg    db    cr,lf,bell,'?Exhausted work space! Circular definition?$'
  83. crlf    db      cr,lf,'$'
  84. ctcmsg    db    5eh,'C$'
  85. cmunk    db    'unknown'
  86. errflag    db    0            ; non-zero to suppress cmcfrm errors
  87. kwstat    db    0            ; get-keyword status
  88. prevch    db    0            ; previous char read by cmgetc
  89. noparse    db    0            ; semicolons not special, if non-zero
  90. subcnt    db    0            ; count of chars matched in '\%'
  91. subtype db    0            ; kind of sub (% or v)
  92. bracecnt db    0            ; curly brace counter
  93. cmsflg    db    0            ; Non-zero when last char was a space
  94. cmdbuf    db    cmdblen dup (0)        ; Buffer for command parsing
  95.     even
  96. cmdstk    dw    0            ; stack pointer at comand call time
  97. cmptab    dw    0            ; Address of present keyword table
  98. cmhlp    dw    0            ; Address of present help
  99. cmwptr    dw    0            ; Pointer for next char write
  100. cmrptr    dw    0            ; Pointer for next char read
  101. cmsiz    dw    0            ; Size info of user input
  102. cmsptr    dw    0            ; Place to save a pointer
  103. mcmprmp    dw    0            ; master prompt, string address
  104. mcmrprs    dd    0            ; master prompt, reparse address
  105. mcmostp    dw    0            ; master prompt, stack pointer
  106. temp    dw    0            ; temp (counts char/line so far)
  107.  
  108. valtab    db    1+19            ; table of values for \v(value)
  109.     mkeyw    'argc)',1
  110.     mkeyw    'count)',2
  111.     mkeyw    'date)',3
  112.     mkeyw    'ndate)',14
  113.     mkeyw    'directory)',5
  114.     mkeyw    'dosversion)',19
  115.     mkeyw    'errorlevel)',4
  116.     mkeyw    'keyboard)',10
  117.     mkeyw    'line)',15
  118.     mkeyw    'platform)',8
  119.     mkeyw    'port)',15
  120.     mkeyw    'program)',12
  121.     mkeyw    'session)',17
  122.     mkeyw    'speed)',11
  123.     mkeyw    'status)',13
  124.     mkeyw    'system)',9
  125.     mkeyw    'terminal)',16
  126.     mkeyw    'time)',6
  127.     mkeyw    'ntime)',18
  128.     mkeyw    'version)',7
  129.  
  130. envtab    db    1                     ; \$(..) Environment table
  131.     mkeyw    'An Environment variable)',0    ; reserve 0 in valtab
  132.  
  133. valtmp    dw    0
  134. valbuf    db    130 dup (0)        ; storage of variable definition
  135.  
  136.     even
  137. envadr    dd    0            ; seg:offset of a string in Environemt
  138. envlen    dw    0            ; length of envadr's string
  139.  
  140.     even                ; Control sequence storage area
  141. maxparam equ    16            ; number of ESC and DCS Parameters
  142. maxinter equ    16            ; number of ESC and DCS Intermediates
  143.  
  144. savepoff label    word            ; Start per session save area
  145. parstate dw    0            ; parser state, init to startup
  146. pardone dw    0            ; where to jmp after Final char seen
  147. parfail    dw    0            ; where to jmp if parser fails
  148. nparam    dw    0            ; number of received Parameters
  149. param    dw    maxparam dup (0)    ; Parameters for ESC
  150. lparam    db    0            ; a single letter Parameter for ESC
  151. ninter    dw    0            ; number of received Intermediates
  152. inter    db    maxinter dup (0),0    ; Intermediates for ESC, + guard 
  153. saveplen dw    ($-savepoff)
  154.  
  155. pcmcfrm    dd    cmcfrm             ; FAR pointers to main parser procs
  156. pcmkeyw    dd    cmkeyw             ; located in code segment code1
  157. pcmtext    dd    cmtext
  158. pcmfil0    dd    cmfil0
  159. pprserr    dd    prserr
  160. pcmexit    dd    cm6
  161. pfvaltoa dd    fvaltoa
  162. data    ends
  163.  
  164. code1    segment
  165. cspb1    equ    this byte
  166.     db    (256-($-cspb1)) dup (0)    ; code1 segment patch buffer
  167.                 ; end of Patch area
  168. code1    ends
  169.  
  170. code    segment
  171.     extrn    ctlu:near, cmblnk:near, locate:near, takrd:near, dec2di:near
  172.     extrn    takclos:near, docom:near, prtasz:near, getenv:near
  173.     extrn    getbaud:near, strlen:near, prtscr:near
  174.  
  175.     assume    cs:code, ds:data, es:nothing
  176.  
  177.                 ; Patch area. Must be first in MSK's Code Seg
  178. cspb    equ    this byte
  179.     dw    seg code1
  180.     dw    seg code2
  181.     dw    seg data
  182.     dw    seg data1
  183.     db    (256-($-cspb)) dup (0)    ; code segment patch buffer
  184.                 ; end of Patch area
  185.  
  186. fctlu    proc    far        ; FAR callable versions of items in seg code
  187.     call    ctlu        ;  for calling from code segment code1 below
  188.     ret
  189. fctlu    endp
  190. fcmblnk    proc    far
  191.     call    cmblnk
  192.     ret
  193. fcmblnk    endp
  194. fgetbaud proc    far
  195.     call    getbaud
  196.     ret
  197. fgetbaud endp
  198. fgetenv    proc    far
  199.     call    getenv
  200.     ret
  201. fgetenv    endp
  202. flocate    proc    far
  203.     call    locate
  204.     ret
  205. flocate    endp
  206. ftakrd    proc    far
  207.     call    takrd
  208.     ret
  209. ftakrd    endp
  210. fdec2di    proc    far
  211.     call    dec2di
  212.     ret
  213. fdec2di    endp
  214. fstrlen    proc    far
  215.     call    strlen
  216.     ret
  217. fstrlen    endp
  218. ftakclos proc    far
  219.     call    takclos
  220.     ret
  221. ftakclos endp
  222. fprtasz    proc    far
  223.     call    prtasz
  224.     ret
  225. fprtasz    endp
  226. fprtscr    proc    far
  227.     call    prtscr
  228.     ret
  229. fprtscr    endp
  230. fisdev    proc    far
  231.     call    isdev
  232.     ret
  233. fisdev    endp
  234. fiseof    proc    far
  235.     call    iseof
  236.     ret
  237. fiseof    endp
  238. ftolowr    proc    far
  239.     call    tolowr
  240.     ret
  241. ftolowr    endp
  242. nvaltoa    proc    near
  243.     call    dword ptr pfvaltoa
  244.     ret
  245. nvaltoa    endp
  246.  
  247. ;       This routine parses the specified function in AH. Any additional
  248. ;       information is in DX and BX.
  249. ;       Returns carry clear on success and carry set on failure
  250.  
  251. COMND    PROC NEAR
  252.     mov    cmdstk,sp        ; save stack ptr for longjmp exit
  253.     mov    noparse,0          ; recognize semicolons in Take files
  254.     mov    bracecnt,0        ; curly brace counter
  255.     mov    al,taklev        ; Take level now
  256.     mov    oldtak,al        ; remember past internal takclos call
  257.     cmp    ah,cmeol        ; Parse a confirm?
  258.     jne    cm2            ; nz = no
  259.     call    dword ptr pcmcfrm    ; get a Carriage Return end of line
  260.     ret
  261. cm2:     cmp    ah,cmkey        ; Parse a keyword?
  262.     jne    cm3
  263.     call    dword ptr pcmkeyw    ; try and get one
  264.     ret
  265. cm3:    cmp    ah,cmline        ; parse line of text
  266.     jne    cm4
  267.     call    dword ptr pcmtext
  268.     ret
  269. cm4:    cmp    ah,cmword        ; parse arbitrary word
  270.     jne    cm5
  271.     call    dword ptr pcmfil0
  272.     ret
  273. cm5:    mov    ah,prstr        ; else give error
  274.     mov    dx,offset cmer00    ; "?Program internal error"
  275.     int    dos
  276.     jmp    dword ptr pprserr    ; reparse
  277.                     ; Control-C exit path (far to near)
  278. cm6:    mov    sp,cmdstk        ; restore command entry stack pointer
  279.     stc
  280.     ret                ;  and fail immediately (a longjmp)
  281.  
  282. COMND    ENDP
  283. code    ends
  284.  
  285. code1    segment
  286.     assume    cs:code1
  287.  
  288. ; This routine parses a keyword from the table pointed at by DX, help text
  289. ; point to by BX. Format of the table is as follows (use macro mkeyw):
  290. ;    addr:    db    N      ; Where N is the # of entries in the table
  291. ;        dw    M      ; M is the size of the keyword
  292. ;        db    'string'  ; String is the keyword
  293. ;        dw    value      ; Value is data to be returned
  294. ; Keywords may be in any order and in mixed case.
  295. ; Return is rskp for success and ret for failure.
  296.  
  297. ; cmptab: pointer to keyword table (supplied by caller)
  298. ; cmhlp: pointer to help message (supplied by caller)
  299. ; cmsptr: pointer to current user word text
  300. ; cmsiz: length of user text, excluding terminator
  301. ; comand.cmcr: 0 = empty lines not allowed, 1 = empty lines allowed
  302. ; comand.cmwhite: non-zero allows leading whitespace for cmline and cmword,
  303. ;                 reset automatically at end of call
  304. ; cmwptr: buffer write pointer to next free byte
  305. ; cmrptr: buffer read pointer for next free byte
  306. ; comand.cmper:    0 to do \%x substitution. Set to 0 at end of call
  307. ; comand.impdo: non-zero permits keyword failure to retry as DO command, reset
  308. ;         automatically at time of failure.
  309. cmkeyw    proc    far
  310.     mov    cmsiz,0            ; user word length
  311.     mov    ax,cmrptr        ; get command reading pointer
  312.     mov    cmsptr,ax        ; set pointer for start of user word
  313.     mov    cmhlp,bx        ; save the help pointer
  314.         mov    cmptab,dx        ; save the beginning of keyword table
  315.     mov    bx,dx
  316.     cmp    byte ptr[bx],0        ; get number of entries in table
  317.     jne    cmky1
  318.     jmp    cmky7            ; e = no keywords to check, error
  319. cmky1:    mov    cmsflg,0ffh        ; skip leading spaces/tabs
  320.     call    cmgtch            ; get char from the user into ah
  321.     jc    cmky3            ; c = terminator
  322.     mov    dx,cmrptr        ; next byte to read
  323.     dec    dx            ; where we just read a char
  324.     mov    cmsptr,dx        ; remember start of keyword
  325.     inc    cmsiz            ; start counting user chars
  326. cmky2:    call    cmgtch            ; read until terminator
  327.     jc    cmky3            ; c = terminator
  328.     inc    cmsiz            ; count user chars
  329.     jmp    short cmky2        ; no terminator yet
  330.  
  331. cmky3:    cmp    ah,'?'                  ; need help?
  332.     jne    cmky4            ; ne = no
  333.     jmp    cmkyhlp            ; do help and exit
  334. cmky4:    cmp    ah,escape        ; escape?
  335.     jne    cmky6            ; ne = no
  336.     call    cmkyesc            ; process escape
  337.     jc    cmky5            ; c = failure (no unique keyword yet)
  338.     mov    comand.cmper,0        ; reset to variable recognition
  339.     mov    comand.cmkeep,0
  340.     mov    comand.impdo,0        ; clear flag to prevent loops
  341.     mov    comand.cmquiet,0    ; permit echoing again
  342.     clc
  343.     ret                ; return successfully to user
  344.  
  345. cmky5:    cmp    cmsiz,0            ; started a word yet?
  346.     je    cmky1            ; e = no, ignore escape, keep looking
  347.     jmp    cmkyhlp            ; ne = yes, show type of error
  348.  
  349. cmky6:    cmp    cmsiz,0            ; length of user's text, empty?
  350.     je    cmky7            ; e = yes, parse error
  351.     push    bx
  352.     mov    bx,cmsptr        ; point at first user character
  353.     cmp    byte ptr[bx],':'    ; start of a label?
  354.     pop    bx
  355.     jne    cmky6a            ; ne = no, return success
  356.     mov    cmsiz,1            ; say just one byte
  357. cmky6a:    call    getkw            ; get unique kw, point to it with bx
  358.     jc    cmky8            ; c = not found
  359.     add    bx,[bx]            ; add length of keyword text (CNT)
  360.     add    bx,2            ; point at value field
  361.     mov    bx,[bx]            ; bx = return value following keyword
  362.     xor    ax,ax
  363.     mov    comand.cmper,al        ; reset to variable recognition
  364.     mov    comand.cmkeep,al
  365.     mov    comand.impdo,al        ; clear flag to prevent loops
  366.     mov    comand.cmquiet,al    ; permit echoing again
  367.     mov    errflag,al
  368.     clc
  369.     ret                ; return successfully
  370.                     ; all other terminators come here
  371. cmky7:    cmp    cmsiz,0            ; empty table or empty user's text?
  372.     jne    cmky8            ; ne = no
  373.     cmp    comand.cmcr,0        ; empty lines allowed?
  374.     jne    cmky10            ; ne = yes, do not complain
  375.     push    dx
  376.     mov    ah,prstr
  377.     mov    dx,offset cmer01    ; command word expected
  378.     int    dos
  379.     pop    dx
  380.     xor    al,al
  381.     mov    comand.cmquiet,al    ; permit echoing again
  382.     mov    comand.impdo,al        ; clear flag to prevent loops
  383.     stc                ; failure
  384.     ret
  385.  
  386. cmky8:    cmp    comand.impdo,0        ; failed here, ok to try Macro table?
  387.     je    cmky8a            ; e = no, use regular exit path
  388.     mov    comand.impdo,0        ; yes, but clear flag to prevent loops
  389.     mov    cmrptr,offset cmdbuf    ; reinit read pointer
  390.     mov    comand.cmquiet,1    ; suppress echoing of same keyword
  391.     mov    bx,offset docom        ; return DO as "found" keyword
  392.     clc
  393.     ret                ; return success to invoke DO
  394.  
  395. cmky8a:    mov    errflag,1        ; say already doing error recovery
  396.     or    kstatus,ksgen        ; global command status, failure
  397.     mov    comand.cmquiet,0    ; permit echoing again
  398.     call    fisdev            ; reading pretyped lines?
  399.     jnc    cmky9            ; nc = yes, consume rest of line
  400.     cmp    taklev,0        ; in a Take file?
  401.     jne    cmky9            ; ne = yes
  402.     call    cmskw            ; display offending keyword
  403.     dec    cmrptr            ; interactive, backup to terminator
  404.     mov    bx,cmrptr        ; look at it
  405.     cmp    byte ptr [bx],' '    ; got here on space terminator?
  406.     jne    cmky10            ; ne = no, (cr,lf,ff) exit failure
  407.     mov    ah,prstr        ; start a fresh line
  408.     mov    dx,offset crlf
  409.     int    dos
  410.     call    bufreset        ; cut back buffer to just before term
  411.     jmp    repars            ; reparse interactive lines
  412.  
  413. cmky9:    call    dword ptr pcmcfrm    ; get formal end of command line
  414.                     ;  to maintain illusion of typeahead
  415.                     ;  and let user backspace to correct
  416.                     ;  mistakes (we reparse everything)
  417.     call    cmskw            ; display offending keyword
  418. cmky10:    mov    comand.cmquiet,0    ; permit echoing again
  419.     stc                ; say failure
  420.     ret
  421. cmkeyw    endp
  422.  
  423. ;;;;;; start support routines for keyword parsing.
  424.  
  425. cmkyesc    proc    near            ; deal with escape terminator
  426.     call    bufreset        ; reset buffer to end just before ESC
  427.     cmp    cmsiz,0         ; user word length, empty?
  428.     jne    cmkye2            ; ne = have user text, else complain
  429. cmkye1:    call    esceoc            ; do normal escape end-of-command
  430.     stc                ; say failure to fill out word
  431.     ret
  432.                     ; add unique keyword to buffer
  433. cmkye2:    call    getkw            ; is there a matching keyword?
  434.     jc    cmkye1            ; c = ambiguous or not found
  435.     push    bx            ; unique, bx points to structure
  436.     push    si
  437.     mov    cx,[bx]            ; length of keyword
  438.     add    bx,2            ; point to first letter
  439.     mov    si,cmwptr        ; where next char goes
  440.     mov    dx,cmsiz        ; length of user word
  441.     add    bx,dx            ; add chars known so far
  442.     sub    cx,dx            ; calculate number yet to add
  443.     add    cmsiz,cx
  444.     jcxz    cmkye4            ; z = none
  445.     mov    ah,conout        ; display new char
  446. cmkye3:    mov    al,[bx]            ; get a keyword letter
  447.     inc    bx
  448.     call    ftolowr            ; lowercase
  449.     mov    [si],al            ; store it
  450.     inc    si
  451.     mov    dl,al
  452.     int    dos            ; display it
  453.     loop    cmkye3            ; do all new chars
  454. cmkye4:    mov    byte ptr[si],' '    ; insert space terminator in buffer
  455.     mov    dl,' '            ; display it
  456.     mov    ah,conout
  457.     int    dos
  458.     inc    si
  459.     mov    cmrptr,si        ; move token pointer after the space
  460.     mov    cmwptr,si        ; next free slot is after the space
  461.     mov    cmsflg,0ffh        ; set space-seen flag
  462.     pop    si
  463.     pop    bx            ; bx = keyword structure
  464.     add    bx,[bx]            ; add length of keyword text
  465.     add    bx,2            ; point at value field
  466.     mov    bx,[bx]            ; bx = return value following keyword
  467.     clc                ; carry clear for success
  468.     ret
  469. cmkyesc    endp
  470.  
  471. esceoc    proc    near            ; do normal escape end-of-command
  472.     push    ax
  473.     push    dx
  474.     mov    ah,conout        ; ring the bell
  475.     mov    dl,bell
  476.     int    dos
  477.     pop    dx
  478.     pop    ax
  479.     call    bufreset        ; reset buffer
  480.     stc                ; say error condition
  481.     ret
  482. esceoc    endp
  483.  
  484. ; Help. Question mark entered by user.  Display all the keywords that match
  485. ; user text. If text is null then use external help if available; otherwise,
  486. ; display all keywords in the table. Removes question mark from buffer and
  487. ; invokes reparse of command line to-date. User word starts at cmsptr and
  488. ; is cmsiz bytes long.
  489. cmkyhlp    proc    near
  490.     xor    cx,cx            ; clear number of keyword (none yet)
  491.     cmp    cmsiz,0            ; user text given?
  492.     jne    cmkyh1            ; ne = yes, use matching keywords
  493.     cmp    cmhlp,0            ; external help given?
  494.     jne    cmkyh6            ; yes, use it instead of full table
  495. cmkyh1:    mov    temp,0            ; count # chars printed on this line
  496.     mov    bx,cmptab        ; beginning of kw table
  497.     mov    ch,[bx]            ; length of table
  498.     xor    cl,cl            ; no keywords or help displayed yet
  499.     inc    bx            ; point at CNT field
  500. cmkyh2:    cmp    cmsiz,0            ; length of user word
  501.     je    cmkyh3            ; e = null, use full table
  502.     call    cmpwrd            ; compare keyword with user word
  503.     jc    cmkyh5            ; c = no match, get another keyword
  504. cmkyh3:    mov    ax,[bx]            ; length of table keyword
  505.     add    byte ptr temp,al    ; count chars printed so far
  506.     cmp    temp,76            ; will this take us beyond column 78?
  507.     jbe    cmkyh4            ; be = no, line has more room
  508.     mov    byte ptr temp,al    ; reset the count
  509.     mov    ah,prstr
  510.     mov    dx,offset crlf        ; break the line
  511.     int    dos
  512. cmkyh4:    or    cl,cl            ; any keywords found yet?
  513.     jnz    cmkyh4a            ; nz = yes
  514.     mov    dx,offset cmin01    ; start with One of the following: msg
  515.     mov    ah,prstr
  516.     int    dos
  517.     inc    cl            ; say one keyword has been found
  518. cmkyh4a:mov    dl,spc            ; put two spaces before each keyword
  519.     mov    ah,conout
  520.     int    dos
  521.     int    dos
  522.     add    temp,2            ; count output chars
  523.     mov    di,bx            ; get current keyword structure
  524.     add    di,2            ; text part
  525.     push    cx
  526.     mov    cx,[bx]            ; string length to cx, offset to di
  527.     call    fprtscr            ; display counted string
  528.     pop    cx
  529. cmkyh5:    dec    ch            ; are we at end of table?
  530.     jle    cmkyh7            ; le = yes, quit now
  531.     add    bx,[bx]            ; next keyword, add CNT chars to bx
  532.     add    bx,4            ; skip CNT and 16 bit value
  533.     jmp    cmkyh2            ; go examine this keyword
  534.  
  535. cmkyh6:    mov    dx,cmhlp        ; external help text
  536.     mov    ah,prstr
  537.     int    dos
  538.     inc    cl            ; say gave help already
  539. cmkyh7:    or    cl,cl            ; found any keywords?
  540.     jnz    cmkyh9            ; nz = yes
  541.     mov    cx,cmsiz        ; length of word
  542.     or    cx,cx
  543.     jg    cmkyh8            ; g = something to show
  544.     push    dx
  545.     mov    ah,prstr
  546.     mov    dx,offset cmer01    ; command word expected
  547.     int    dos
  548.     pop    dx
  549.     jmp    prserr
  550. cmkyh8:    mov    kwstat,0        ; set keyword not-found status
  551.     call    cmskw            ; display offending keyword
  552. cmkyh9:    mov    ah,prstr        ; start a fresh line
  553.     mov    dx,offset crlf
  554.     int    dos
  555.     call    bufreset        ; cut back buffer to just before '?'
  556.         jmp    repars
  557. cmkyhlp    endp
  558.  
  559. ; See if keyword is ambiguous or not from what the user has typed in.
  560. ; Return carry set if word is ambiguous or not found, carry clear otherwise.
  561. ; Uses table pointed at by cmptab, user text pointed at by cmsptr and length
  562. ; in cmsiz.
  563. cmambg    proc    near
  564.     push    bx
  565.     push    cx
  566.     push    dx
  567.     xor    dl,dl            ; count keyword matches so far
  568.     mov    bx,cmptab        ; look at start of keyword table
  569.     mov    cl,[bx]            ; get number of entries in table
  570.     xor    ch,ch            ; use cx as a counter
  571.     jcxz    cmamb8            ; z = no table so always ambiguous
  572.     inc    bx            ; look at CNT byte of keyword
  573. cmamb4:    call    cmpwrd            ; user vs table words, same?
  574.     jc    cmamb6            ; c = no match
  575.     inc    dl            ; count this as a match
  576.     cmp    dl,1            ; more than one match?
  577.     ja    cmamb8            ; a = yes, quit now
  578. cmamb6:    add    bx,[bx]            ; add CNT chars to bx
  579.     add    bx,4            ; skip CNT and 16 bit value
  580.     loop    cmamb4            ; do rest of keyword table
  581.     cmp    dl,1            ; how many matches were found?
  582.     jne    cmamb8            ; ne = none or more than 1: ambiguous
  583.     pop    dx            ; restore main registers
  584.     pop    cx
  585.     pop    bx
  586.     clc
  587.     ret                ; ret = not ambiguous
  588. cmamb8:    pop    dx            ; restore main registers
  589.     pop    cx
  590.     pop    bx
  591.     stc
  592.     ret                ; return ambiguous or not found
  593. cmambg    endp
  594.  
  595. ; Compare user text with keyword, abbreviations are considered a match.
  596. ; Enter with bx pointing at keyword table CNT field for a keyword.
  597. ; Return carry clear if they match, set if they do not match. User text
  598. ; pointed at by cmsptr and length is in cmsiz.
  599. ; Registers preserved.
  600.  
  601. cmpwrd    proc    near
  602.     push    cx
  603.     mov    cx,cmsiz        ; length of user's text
  604.     jcxz    cmpwrd2            ; z: null user word matches no keyword
  605.     cmp    cx,[bx]            ; user's text longer than keyword?
  606.     ja    cmpwrd2            ; a = yes, no match
  607.     push    ax
  608.     push    bx
  609.     push    si
  610.     add    bx,2                ; point at table's keyword text
  611.     mov    si,cmsptr        ; buffer ptr to user input
  612.     cld
  613. cmpwrd1:lodsb                ; user text
  614.     mov    ah,[bx]            ; keyword text
  615.     inc    bx            ; next keyword letter
  616.     call    ftolowr            ; force lower case on both chars
  617.     cmp    ah,al            ; same?
  618.     loope    cmpwrd1            ; e = same so far
  619.     pop    si
  620.     pop    bx
  621.     pop    ax
  622.     jne    cmpwrd2            ; ne = mismatch
  623.     pop    cx            ; recover keyword counter
  624.     clc                ; they match
  625.     ret
  626. cmpwrd2:pop    cx            ; recover keyword counter
  627.     stc                ; they do not match
  628.     ret
  629. cmpwrd    endp
  630.  
  631. ; Get pointer to keyword structure using user text. Uses keyword table
  632. ; pointed at by cmptab and cmsiz holding length of user's keyword (cmpwrd
  633. ; needs comand.cmsptr pointing at user's keyword and length of cmsiz).
  634. ; Structure pointer returned in BX.
  635. ; Return carry clear for success and carry set for failure. Modifies BX.
  636. getkw    proc    near
  637.     push    cx
  638.     mov    kwstat,0        ; keyword status, set to not-found
  639.     cmp    cmsiz,0            ; length of user word, empty?
  640.     je    getkw3            ; e = yes, fail
  641.     mov    bx,cmptab        ; table of keywords
  642.     mov    cl,[bx]            ; number of keywords in table
  643.     xor    ch,ch
  644.     jcxz    getkw3            ; z = none, fail
  645.     inc    bx            ; point to first
  646. getkw1:    call    cmpwrd            ; compare user vs table words
  647.     jc    getkw2            ; c = failed to match word, try next
  648.     mov    kwstat,1        ; say found one keyword, maybe more
  649.     push    dx
  650.     mov    dx,cmsiz        ; users word length
  651.     cmp    [bx],dx            ; same length (end of keyword)?
  652.     pop    dx
  653.     je    getkw4            ; e = yes, exact match. Done
  654.     call    cmambg            ; ambiguous?
  655.     jnc    getkw4            ; nc = unique, done, return with bx
  656.     mov    kwstat,2        ; say more than one such keyword
  657. getkw2:    add    bx,[bx]            ; next keyword, add CNT chars to bx
  658.     add    bx,4            ; skip CNT and 16 bit value
  659.     loop    getkw1            ; do all, exhaustion = failure
  660. getkw3:    pop    cx
  661.     stc                ; return failure
  662.     ret
  663. getkw4:    pop    cx
  664.     clc                ; return success
  665.     ret
  666. getkw    endp
  667.  
  668. ; show offending keyword message. Cmsptr points to user word,
  669. ; cmsiz has length. Modifies AX, CX, and DX.
  670. cmskw    proc    near
  671.     cmp    comand.cmquiet,0    ; Quiet mode?
  672.     je    cmskw0            ; e = no, regular mode
  673.     ret                ; else say nothing
  674. cmskw0:    mov    ah,prstr        ; not one of the above terminators
  675.     mov    dx,offset cmer02    ; '?Word "'
  676.     int    dos
  677.     mov    ah,conout
  678.     mov    cx,cmsiz        ; length of word
  679.     jcxz    cmskw3            ; z = null
  680.     mov    ah,conout
  681.     push    si
  682.     mov    si,cmsptr        ; point to word
  683.     cld
  684. cmskw1:    lodsb
  685.     cmp    al,' '            ; control code?
  686.     jae    cmskw2            ; ae = no
  687.     push    ax
  688.     mov    dl,5eh            ; caret
  689.     int    dos
  690.     pop    ax
  691.     add    al,'A'-1        ; plus ascii bias
  692. cmskw2:    mov    dl,al            ; display chars in word
  693.     int    dos
  694.     loop    cmskw1
  695.     pop    si
  696. cmskw3:    mov    dx,offset cmer03    ; '" not usable here.'
  697.     cmp    kwstat,1        ; kywd status from getkw, not found?
  698.     jb    cmskw4            ; b = not found, a = ambiguous
  699.     mov    dx,offset cmer04    ; '" ambiguous'
  700. cmskw4:    mov    ah,prstr
  701.         int    dos
  702.     ret
  703. cmskw    endp
  704. ;;;;;;;;;; end of support routines for keyword parsing.
  705.  
  706. ; Parse    arbitrary text up to a CR. Enter with BX = pointer to output buffer,
  707. ; DX pointing to help text. Produces asciiz string. Return updated pointer in
  708. ; BX and output size in AX. Leading spaces are omitted unless comand.cmwhite
  709. ; is non-zero (cleared upon exit). It does not need to be followed by the
  710. ; usual call to confirm the line. Byte comand.cmblen can be used to specify
  711. ; the length of the caller's buffer; cleared to zero by this command to
  712. ; imply a length of 127 bytes (default) and if zero at startup use 127 bytes.
  713. cmtext    proc    far
  714.     mov    cmptab,bx        ; save pointer to data buffer
  715.     mov    cmhlp,dx        ; save the help message
  716.     xor    cx,cx            ; init the char count
  717.     cmp    comand.cmblen,0        ; length of user's buffer given?
  718.     jne    cmtxt0            ; ne = yes
  719.     mov    comand.cmblen,127    ; else set 127 byte limit plus null
  720. cmtxt0:    mov    cmsflg,0ffh        ; skip initial spaces
  721.     cmp    comand.cmwhite,0    ; allow leading whitespace?
  722.     je    cmtxt1a            ; e = no
  723. cmtxt1:    mov    cmsflg,0        ; get all spaces
  724. cmtxt1a:call    cmgtch            ; get a char
  725.     jnc    cmtxt5            ; nc = non-terminator, put in buffer
  726.     cmp    ah,' '            ; space?
  727.     je    cmtxt5            ; e = yes, record it
  728.     cmp    ah,escape        ; escape?
  729.     jne    cmtxt2            ; ne = no
  730.     call    esceoc            ; do normal escape end-of-command
  731.     jmp    short cmtxt0        ; try again
  732.  
  733. cmtxt2:    cmp    ah,'?'            ; asking a question?
  734.     je    cmtxt3            ; e = yes
  735.     cmp    ah,cr            ; formal carriage return?
  736.     je    cmtxt2a            ; e = yes
  737.     inc    cmrptr            ; accept char into buffer
  738.     jmp    short cmtxt5        ;  and record the char
  739. cmtxt2a:mov    bx,cmptab        ; return updated pointer
  740.     xor    ax,ax
  741.     mov    byte ptr[bx],al        ; put terminator into the buffer
  742.     mov    comand.cmwhite,al    ; clear leading whitespace flag
  743.     mov    comand.cmper,al        ; reset to variable recognition
  744.     mov    comand.cmblen,ax    ; set user buffer length to unknown
  745.     mov    comand.cmkeep,al
  746.     mov    ax,cx            ; return count in AX
  747.     call    rprompt            ; restore master prompt level
  748.     clc
  749.     ret
  750.                     ; Help processor
  751. cmtxt3:    inc    cmrptr            ; count the ?
  752.     or    cx,cx            ; is "?" the first char?
  753.     jnz    cmtxt5            ; nz = no, just add to buffer
  754.     dec    cmrptr
  755.     mov    cmsiz,0            ; no keyword for help
  756.     mov    comand.cmwhite,0    ; clear leading whitespace flag
  757.     cmp    cmhlp,0            ; external help given?
  758.     jne    cmtxt3a            ; ne = yes
  759.     mov    cmhlp,offset cmin00    ; confirm with c/r msg
  760.     mov    comand.cmblen,0        ; set user buf length to unknown
  761. cmtxt3a:jmp    cmkyhlp            ; do help process
  762.  
  763. cmtxt5:    inc    cx            ; increment the count
  764.     mov    bx,cmptab        ; pointer into destination array
  765.     mov    [bx],ah            ; put char into the buffer
  766.     inc    bx
  767.     xor    al,al
  768.     mov    [bx],al            ; insert null terminator
  769.     mov    cmptab,bx
  770.     cmp    cx,comand.cmblen    ; buffer filled?
  771.     ja    cmtxt6            ; a = yes, declare error
  772.     jb    cmtxt5a            ; a = not filled yet
  773.     mov    ah,conout        ; notify user that the buffer is full
  774.     mov    dl,bell
  775.     int    dos
  776. cmtxt5a:jmp    cmtxt1
  777. cmtxt6:    mov    ah,prstr
  778.     mov    dx,offset cmer09
  779.     int    dos
  780.     jmp    prserr            ; declare parse error
  781. cmtext    endp
  782.  
  783.  
  784. ; Parse arbitrary text up to whitespace.  Enter with DX pointing to output
  785. ; buffer and BX pointing to help text. Produces asciiz string. Return updated
  786. ; pointer in DX and input size in AH. Skips leading whitespace unless 
  787. ; comand.cmwhite is non-zero (cleared upon exit). Does a return skip exit.
  788.  
  789. cmfil0    proc    far
  790.     mov    cmptab,dx        ; save pointer to data buffer
  791.     mov    cmhlp,bx        ; save the help message
  792.     mov    cmsiz,0            ; init the char count
  793.     cmp    comand.cmblen,0        ; length of user's buffer given?
  794.     jne    cmfil0a            ; ne = yes
  795.     mov    comand.cmblen,127    ; else set 127 byte limit plus null
  796. cmfil0a:cmp    comand.cmwhite,0    ; allow leading whitespace?
  797.     jne    cmfil1            ; ne = yes
  798.     mov    cmsflg,0ffh        ; omit leading space
  799. cmfil1:    call    cmgtch            ; get a char
  800.     jc    cmfi1a            ; c = terminator 
  801.     jmp    short cmfil5        ; put char into the buffer
  802. cmfi1a:    cmp    ah,escape        ; escape?
  803.     je    cmfi1b            ; e = yes
  804.     jmp    short cmfil2        ; process other terminators
  805. cmfi1b:    call    esceoc            ; do normal escape end-of-command
  806.     jmp    short cmfil0a        ; try again
  807.  
  808. cmfil2:    cmp    ah,'?'            ; asking a question?
  809.     je    cmfil3            ; e = yes
  810.     xchg    dx,bx            ; re-interchange bx and dx
  811.     xor    ax,ax
  812.     mov    comand.cmwhite,al    ; clear whitespace flag
  813.     mov    comand.cmper,al        ; reset to variable recognition
  814.     mov    comand.cmkeep,al    ; do not keep Take file open
  815.     mov    comand.cmblen,ax    ; set user buffer length to unknown
  816.     mov    bx,cmptab        ; pointer into destination array
  817.     mov    byte ptr[bx],al        ; put null terminator into the buffer
  818.     inc    bx
  819.     mov    ax,cmsiz        ; return count in AX
  820.     mov    dx,cmptab        ; return updated pointer
  821.     xchg    dx,bx            ; re-interchange bx and dx
  822.     call    rprompt            ; restore master prompt level
  823.     clc
  824.     ret                ; return success
  825.  
  826. cmfil3:    inc    cmrptr            ; count the ?
  827.     cmp    cmsiz,0            ; Is "?" first char?
  828.     jne    cmfil5            ; ne = no, just add to buffer
  829.     dec    cmrptr
  830.     mov    cmsiz,0
  831.     cmp    cmhlp,0            ; external help given?
  832.     jne    cmfil3a            ; ne = yes
  833.     mov    cmhlp,offset cmin00    ; confirm with c/r msg
  834. cmfil3a:jmp    cmkyhlp            ; do help process
  835.  
  836. cmfil5:    inc    cmsiz            ; increment the count
  837.     mov    bx,cmptab        ; pointer into destination array
  838.     mov    [bx],ah            ; put char into the buffer
  839.     inc    bx
  840.     mov    cmptab,bx
  841.     mov    cx,cmsiz        ; length of command so far
  842.     cmp    cx,comand.cmblen    ; buffer filled?
  843.     ja    cmfil7            ; a = yes, declare error
  844.     jb    cmfil6            ; a = not filled yet
  845.     mov    ah,conout        ; notify user that the buffer is full
  846.     mov    dl,bell
  847.     int    dos
  848. cmfil6:    jmp    cmfil1
  849.  
  850. cmfil7:    mov    ah,prstr
  851.     mov    dx,offset cmer09
  852.     int    dos
  853.     jmp    prserr            ; declare parse error
  854. cmfil0    endp
  855.  
  856. ; This routine gets a confirm (CR) and displays any extra non-blank text.
  857. ; errflag non-zero means suppress "extra text" display in this routine
  858. ; because another routine is handling errors.
  859. cmcfrm    proc    far
  860.     mov    comand.cmper,1        ; do not react to \%x substitutions
  861. cmcfr1:    mov    cmsflg,0ffh        ; set space-seen flag (skip spaces)
  862.     call    cmgtch            ; get a char
  863.     push    cmrptr
  864.     pop    temp            ; remember first non-space position
  865.     jc    cmcfr4            ; c = terminator
  866.     dec    temp            ; backup to text char
  867. cmcfr3:    mov    cmsflg,0ffh        ; set space-seen flag (skip spaces)
  868.     call    cmgtch
  869.     jnc    cmcfr3            ; read until terminator
  870. cmcfr4:    cmp    ah,' '
  871.     je    cmcfr3            ; ignore ending on space
  872.     cmp    ah,escape        ; escape?
  873.     jne    cmcfr5            ; ne = no
  874.     call    esceoc            ; do standard end of cmd on escape
  875.     mov    ax,cmrptr
  876.     cmp    ax,temp            ; started text yet?
  877.     je    cmcfr1            ; e = no
  878.     jmp    short cmcfr3        ; try again
  879. cmcfr5: cmp    ah,'?'            ; curious?
  880.         jne    cmcfr6            ; ne = no
  881.     mov    cmhlp,offset cmin00    ; msg Confirm with c/r
  882.     mov    cmsiz,0            ; no keyword
  883.     mov    errflag,0
  884.     jmp    cmkyhlp            ; do help
  885. cmcfr6:    cmp    ah,cr            ; the confirmation char?
  886.     jne    cmcfr3            ; ne = no
  887.     cmp    errflag,0        ; already doing one error?
  888.     jne    cmcfr7            ; ne = yes, skip this one
  889.     mov    cx,cmrptr        ; pointer to terminator
  890.     mov    dx,temp            ; starting place
  891.     sub    cx,dx            ; end minus starting point = length
  892.     jle    cmcfr7            ; le = nothing to display
  893.     push    dx            ; save source pointer
  894.     mov    ah,prstr
  895.     mov    dx,offset cmer07    ; ?Ignoring extras
  896.     int    dos
  897.     pop    dx
  898.     mov    bx,1            ; stdout handle, cx=count, dx=src ptr
  899.     mov    ah,write2        ; allow embedded dollar signs
  900.     int    dos
  901.     mov    ah,prstr
  902.     mov    dx,offset cmer08    ; trailer msg
  903.     int    dos
  904. cmcfr7:    mov    errflag,0
  905.     mov    comand.cmper,0        ; reset to variable recognition
  906.     mov    comand.cmkeep,0
  907.     call    rprompt            ; restore master prompt level
  908.     clc                ; return confirmed
  909.     ret
  910. cmcfrm    endp
  911.  
  912. ;;; Routines to get and edit incoming text.
  913.  
  914. ; Detect '\%x' (x = '0' or above) and substitute the matching Macro string
  915. ; in place of the '\%x' phrase in the user's buffer. If comand.cmper != 0
  916. ; then treat '\%' as literal characters. If no matching parameter exists
  917. ; just remove '\%x'. Ditto for \v(variable). Returns carry clear if nothing
  918. ; done, else carry set and new text already placed in user's buffer.
  919. ; Includes \v(variable) and \$(Environment variable) and \m(macro name).
  920. ; Uses depth-first recursion algorithm. All registers preserved.
  921. subst    proc    near
  922.     cmp    comand.cmper,0        ; recognize '\%','\v(','\$(','\m(' ?
  923.     jne    subst2            ; ne = no, treat as literals
  924.     cmp    ah,'\'            ; is it the first char of the pattern?
  925.     jne    subst1            ; ne = no, try next
  926.     mov    subcnt,1        ; say first is matched
  927.     mov    subtype,0
  928.     jmp    short subst2        ; exit successfully
  929. subst1:    cmp    subcnt,1        ; first char matched already?
  930.     ja    subst3            ; a = first two have been matched
  931.     jb    subst2            ; b = none yet
  932.     inc    subcnt            ; assume a match follows
  933.     mov    subtype,ah        ; remember kind of substitution
  934.     or    subtype,20h        ; convert to lower case
  935.     cmp    ah,'%'            ; second match char, same?
  936.     je    subst2            ; e = yes
  937.     cmp    subtype,'v'        ; \v(...)?
  938.     je    subst2            ; e = yes
  939.     cmp    subtype,'$'        ; \$(..)?
  940.     je    subst2
  941.     cmp    subtype,'m'        ; \m(..)?
  942.     je    subst2
  943. subst1a:mov    subcnt,0        ; mismatch, clear match counter
  944.     mov    subtype,0        ; clear substitution kind
  945. subst2:    clc                ; carry clear = no substitution done
  946.     ret
  947. subst3:    cmp    subtype,'v'        ; doing \v(..)?
  948.     je    subst3a            ; e = yes
  949.     cmp    subtype,'$'        ; doing \$(..)?
  950.     je    subst3a            ; e = yes
  951.     cmp    subtype,'m'        ; doing \m(..)?
  952.     jne    subst3b            ; ne = no, do \%<char>
  953. subst3a:cmp    ah,'('            ; have leading parenthesis?
  954.     jne    subst1a            ; ne = no, mismatch, exit
  955.     jmp    subst10            ; process \v(..), \$(..), \m(..)
  956. subst3b:mov    subcnt,0        ; do \%<char>, clear match counter
  957.     cmp    ah,'0'            ; third char is '0' or above?
  958.     jb    subst1a            ; b = out of range, no match
  959.     push    bx            ; save working regs
  960.     push    cx
  961.     push    es
  962.     sub    cmrptr,3        ; reread commands where backslash was
  963.     call    bufreset        ; reset buffer to this point
  964.     push    cmptab            ; save current keyword parsing parms
  965.     push    cmsptr
  966.     push    cmsiz
  967.     mov    bx,cmrptr        ; points at backslash
  968.     mov    cmsptr,bx        ; direct keyword routine to it
  969.     mov    cmsiz,3            ; three bytes (\%x) of user text
  970.     mov    cmptab,offset mcctab     ; use Macro table for new text
  971.     call    getkw            ; get ptr, bx, to matching keyword
  972.     pop    cmsiz            ; restore borrowed keyword parameters
  973.     pop    cmsptr
  974.     pop    cmptab
  975.     jc    substx            ; c = not found, keep after pops
  976.     cmp    taklev,maxtak        ; room in take level?
  977.     jae    subst6            ; ae = no
  978.     mov    cx,[bx]            ; length of found word
  979.     add    cx,2            ; plus count field
  980.     add    bx,cx            ; point to 16 bit value (string ptr)
  981.     mov    es,[bx]            ; point to string structure segment
  982.     xor    bx,bx
  983.     mov    cx,es:[bx]        ; length of string
  984.     add    takadr,size takinfo    ; pointer to new Take structure
  985.     inc    taklev
  986.     mov    bx,takadr        ; pointer to new Take structure
  987.     mov    [bx].takbuf,es        ; segment of Take buffer
  988.     mov    [bx].takcnt,cx        ; number of unread bytes
  989.     mov    [bx].taktyp,0fdh    ; flag as an internal macro
  990.     mov    [bx].takptr,2        ; init pointer to definition itself
  991. substx:    pop    es
  992.     pop    cx
  993.     pop    bx
  994.     stc                ; set carry to say have stored chars
  995.     ret
  996.  
  997. subst6:    mov    ah,prstr
  998.     mov    dx,offset stkmsg    ; out of work space msg
  999.     int    dos
  1000.     jmp    prserr            ; and declare parse error
  1001.  
  1002.                     ; \m(..), \v(..), \$(..)
  1003. subst10:push    bx            ; save working regs
  1004.     push    cx
  1005.     push    es
  1006.     push    cmptab            ; save current keyword parsing parms
  1007.     push    cmsptr
  1008.     push    cmsiz
  1009.     push    cmhlp
  1010.     push    dx
  1011.     mov    subcnt,0        ; clear match counter
  1012.     mov    cmhlp,0
  1013.     mov    ax,cmrptr
  1014.     mov    valtmp,ax        ; remember current read pointer
  1015.     mov    cmsptr,ax        ; start of word
  1016.     mov    cmptab,offset valtab    ; table of keywords for \v(..)
  1017.     cmp    subtype,'v'        ; \v(..)?
  1018.     je    subst10a        ; e = yes
  1019.     mov    cmptab,offset envtab    ; Environment variable table \$(..)
  1020.     cmp    subtype,'$'        ; \$(..)?
  1021.     je    subst10a        ; e = yes
  1022.     mov    cmptab,offset mcctab    ; main Macro table for \m(..)
  1023. subst10a:mov    cmsflg,0        ; see leading spaces/tabs
  1024.     mov    cmsiz,0            ; word size
  1025. subst11:call    cmgtch            ; read a character into ah
  1026.     jnc    subst13            ; nc = non-terminator
  1027.     cmp    ah,'?'            ; need help?
  1028.     jne    subst11a        ; ne = no
  1029.     jmp    cmkyhlp            ; do help and exit
  1030. subst11a:cmp    ah,escape        ; escape?
  1031.     jne    subst12            ; ne = no
  1032.     mov    bx,cmsptr        ; where word started
  1033.     mov    cmrptr,bx        ; omit word to date
  1034.     mov    cmwptr,bx
  1035.     call    cmkyesc            ; process escape
  1036.     mov    dx,1            ; signal valtoa to add trailing space
  1037.     jnc    subst14            ; nc = success (found the keyword)
  1038.                     ;
  1039. subst12:mov    bx,cmsptr        ; where word started
  1040.     sub    bx,3            ; back over "\v(" pr "\$(" part
  1041.     mov    cmrptr,bx        ; omit "\v(..." or "\$(..." to date
  1042.     mov    cmwptr,bx        ;  and "\m(..." too
  1043.     jmp    repars            ; reparse command without \v(...
  1044.                     ;
  1045. subst13:inc    cmsiz            ; count user chars
  1046.     cmp    ah,')'            ; end bracket?
  1047.     jne    subst11            ; ne = no, keep looking
  1048.     dec    cmsiz            ; omit user's ')' from tests
  1049.     cmp    subtype,'$'        ; \$(..)?
  1050.     je    subst13a        ; e = yes, no keyword in table
  1051.     call    getkw            ; \m(..) and \v(..) test for keyword
  1052.     jc    subst12            ; c = failure
  1053.     jmp    short subst13b        ; success
  1054.  
  1055. subst13a:call    envvar            ; search Environment for the word
  1056.     jc    subst12            ; c = failure
  1057.     mov    bx,offset envtab+1    ; Environment data structure
  1058.  
  1059. subst13b:mov    ax,valtmp        ; where word started
  1060.     mov    cmrptr,ax
  1061.     sub    ax,3            ; backup to "\v(" or "\$("
  1062.     mov    cmrptr,ax        ; write output where backslash was
  1063.     mov    cmwptr,ax
  1064.     mov    cx,[bx]            ; bx = structure pointer, cx=keyw len
  1065.     add    cx,2            ; skip count byte
  1066.     add    bx,cx            ; point at 16 bit value field
  1067.     mov    bx,[bx]            ; get value to bx for valtoa
  1068.     xor    dx,dx            ; signal valtoa to not add trailing sp
  1069. subst14:
  1070.     cmp    taklev,maxtak        ; room in take level?
  1071.     jb    subst15            ; b = yes
  1072.     mov    dx,offset stkmsg    ; out of work space msg
  1073.     mov    ah,prstr        ; display error message
  1074.     int    dos
  1075.     jmp    short subst17
  1076. subst15:push    di
  1077.     call    valtoa            ; make text be an internal macro
  1078.     jc    subst16            ; c = failed
  1079.     add    takadr,size takinfo    ; pointer to new Take structure
  1080.     inc    taklev            ; next Take level
  1081.     push    bx
  1082.     mov    bx,takadr        ; address of take structure
  1083.     mov    ax,ds
  1084.     mov    [bx].takbuf,ax        ; segment of Take buffer
  1085.     mov    [bx].takptr,offset valbuf+2 ; offset of beginning of def text
  1086.     mov    [bx].takcnt,di        ; # of chars in buffer
  1087.     mov    [bx].taktyp,0fdh    ; flag as an internal macro
  1088.     pop    bx
  1089. subst16:pop    di
  1090. subst17:pop    dx
  1091.     pop    cmhlp
  1092.     pop    cmsiz            ; restore borrowed keyword parameters
  1093.     pop    cmsptr
  1094.     pop    cmptab
  1095.     jmp    repars            ; reparse cmd line with new material
  1096. subst    endp
  1097.  
  1098. ; Make an internal macro defined as the text for one of the value variables.
  1099. ; Use incoming DX as trailing space suppression flag, if null.
  1100. valtoa    proc    near
  1101.     push    dx            ; save trailing space flag
  1102.     mov    di,offset valbuf+2    ; start text here
  1103.     mov    word ptr [di],0        ; fill buffer with sweet nothings
  1104.                     ; BX has index of variable
  1105.     cmp    bx,0            ; Environment?
  1106.     jne    valtoa1            ; ne = no
  1107.     mov    cx,envlen        ; string length
  1108.     jcxz    valtoa0            ; z = empty
  1109.     push    es
  1110.     push    ds
  1111.     mov    ax,ds
  1112.     mov    es,ax            ; destination is es:di
  1113.     lds    si,envadr        ; ds:si is source from Environment
  1114.     cld
  1115.     rep    movsb            ; copy string
  1116.     pop    ds            ; recover ds
  1117.     pop    es
  1118. valtoa0:jmp    valtoa30
  1119.     
  1120. valtoa1:cmp    bx,1            ; ARGC?
  1121.     jne    valtoa2            ; ne = no
  1122.     call    wrtargc            ; write argc
  1123.     jmp    valtoa30
  1124. valtoa2:cmp    bx,2            ; COUNT?
  1125.     jne    valtoa3            ; ne = no
  1126.     call    wrtcnt            ; write it
  1127.     jmp    valtoa30
  1128. valtoa3:cmp    bx,3            ; DATE?
  1129.     jne    valtoa4
  1130.     call    wrtdate
  1131.     jmp    valtoa30
  1132. valtoa4:cmp    bx,4            ; ERRORLEVEL?
  1133.     jne    valtoa5            ; ne = no
  1134.     call    wrterr
  1135.     jmp    valtoa30
  1136. valtoa5:cmp    bx,5            ; DIR?
  1137.     jne    valtoa6
  1138.     call    wrtdir
  1139.     jmp    valtoa30
  1140. valtoa6:cmp    bx,6            ; TIME?
  1141.     jne    valtoa7
  1142.     call    wrttime
  1143.     jmp    valtoa30
  1144. valtoa7:cmp    bx,7            ; VERSION?
  1145.     jne    valtoa8            ; ne = no
  1146.     mov    ax,version        ; get version such as 300
  1147.     call    fdec2di            ; convert binary to asciiz
  1148.     mov    word ptr [di],0020h    ; space, null
  1149.     inc    di
  1150.     jmp    valtoa30
  1151. valtoa8:cmp    bx,8            ; PLATFORM?
  1152.     jne    valtoa9            ; ne = no
  1153.     call    wrtplat            ; get machine name, e.g. "IBM-PC"
  1154.     jmp    valtoa30
  1155. valtoa9:cmp    bx,9            ; SYSTEM?
  1156.     jne    valtoa10        ; ne = no
  1157.     call    wrtsystem        ; get "MS-DOS" string
  1158.     jmp    valtoa30
  1159. valtoa10:cmp    bx,10            ; KEYBOARD?
  1160.     jne    valtoa11        ; ne = no
  1161.     call    wrtkbd            ; 88 or 101 value
  1162.     jmp    valtoa30
  1163. valtoa11:cmp    bx,11            ; SPEED?
  1164.     jne    valtoa12        ; ne = no
  1165.     push    di
  1166.     call    fgetbaud        ; read baud rate from hardware
  1167.     pop    di
  1168.     mov    bx,portval
  1169.     mov    ax,[bx].baud
  1170.     cmp    al,byte ptr bdtab    ; index versus number of table entries
  1171.     jb    valtoa11a        ; b = index is in the table
  1172.     mov    si,offset cmunk-2    ; unrecognized value, say "unknown"
  1173.     mov    bx,7            ; length of string
  1174.     jmp    short valtoa11c
  1175. valtoa11a:mov    si,offset bdtab        ; ascii rate table
  1176.     mov    cl,[si]            ; number of entries
  1177.     inc    si            ; point to an entry
  1178. valtoa11b:
  1179.     mov    bx,[si]            ; length of text string
  1180.     cmp    ax,[si+bx+2]        ; our index vs table entry index
  1181.     je    valtoa11c        ; e = match
  1182.     add    si,bx            ; skip text
  1183.     add    si,4            ; skip count and index word
  1184.     loop    valtoa11b        ; look again
  1185.     mov    si,offset cmunk-2    ; unrecognized value, say "unknown"
  1186.     mov    bx,7            ; length of string
  1187. valtoa11c:mov    cx,bx            ; length of string
  1188.     add    si,2            ; point at string
  1189.     rep    movsb            ; copy string
  1190.     jmp    valtoa30
  1191.  
  1192. valtoa12:cmp    bx,12            ; PROGRAM?
  1193.     jne    valtoa13        ; ne = no
  1194.     call    wrtprog            ; get "MS-DOS_KERMIT" string
  1195.     jmp    valtoa30
  1196. valtoa13:cmp    bx,13            ; STATUS?
  1197.     jne    valtoa14        ; ne = no
  1198.     call    wrtstat            ; compose status string
  1199.     jmp    valtoa30
  1200. valtoa14:cmp    bx,14            ; NDATE?
  1201.     jne    valtoa15        ; ne = no
  1202.     call    wrtndate
  1203.     jmp    valtoa30
  1204. valtoa15:cmp    bx,15            ; LINE, PORT?
  1205.     jne    valtoa16        ; ne = no
  1206.     call    wrtport
  1207.     jmp    valtoa30
  1208. valtoa16:cmp    bx,16            ; TERMINAL?
  1209.     jne    valtoa17        ; ne = no
  1210.     call    wrtterm
  1211.     jmp    valtoa30
  1212. valtoa17:cmp    bx,17            ; SESSION (internal Telnet)?
  1213.     jne    valtoa18        ; ne = no
  1214.     mov    ax,sescur        ; get internal Telnet session ident
  1215.     inc    ax            ; count from 1 for users (0 == none)
  1216.     call    fdec2di            ; convert binary to asciiz
  1217.     mov    word ptr [di],0020h    ; space, null
  1218.     inc    di
  1219.     jmp    valtoa30
  1220. valtoa18:cmp    bx,18            ; \v(ntime) (seconds in day)?
  1221.     jne    valtoa19        ; ne = no
  1222.     mov    ah,gettim        ; get DOS time of day
  1223.     int    dos            ; ch=hh, cl=mm, dh=ss, dl=0.01 sec
  1224.     mov    bx,60
  1225.     mov    al,ch            ; hours
  1226.     mul    bl            ; to minutes
  1227.     xor    ch,ch
  1228.     add    ax,cx            ; plus minutes
  1229.     mov    cl,dh            ; preserve seconds
  1230.     mul    bx            ; need carry out to DX
  1231.     add    ax,cx            ; add seconds
  1232.     adc    dx,0
  1233.     mov    cx,10            ; down to 16 bit size
  1234.     div    cx
  1235.     call    fdec2di            ; convert binary to asciiz
  1236.     xchg    ax,dx
  1237.     call    fdec2di            ; convert binary to asciiz
  1238.     mov    word ptr [di],0020h    ; space, null
  1239.     inc    di
  1240.     jmp    short valtoa30
  1241. valtoa19:cmp    bx,19            ; \v(dosversion)?
  1242.     jne    valtoa29        ; ne = no
  1243.     mov    ax,dosnum        ; DOS verson, major high, minor low
  1244.     push    ax
  1245.     xchg    ah,al
  1246.     xor    ah,ah
  1247.     call    fdec2di            ; write major
  1248.     pop    ax
  1249.     xor    ah,ah
  1250.     cmp    al,10            ; less than 10?
  1251.     ja    valtoa19a        ; a = no
  1252.     mov    byte ptr [di],'0'    ; use two digits for minor
  1253.     inc    di
  1254. valtoa19a:call    fdec2di            ; write minor
  1255.     mov    word ptr [di],0020h    ; space, null
  1256.     inc    di
  1257.     jmp    short valtoa30
  1258. valtoa29:pop    dx            ; assume an internal Macro
  1259.     add    takadr,size takinfo    ; pointer to new Take structure
  1260.     inc    taklev            ; next Take level
  1261.     push    bx
  1262.     mov    ax,bx            ; save seg of string here
  1263.     mov    bx,takadr        ; address of take structure
  1264.     mov    [bx].takbuf,ax        ; segment of Take buffer
  1265.     mov    [bx].takptr,2         ; offset of beginning of def text
  1266.     push    es
  1267.     mov    es,ax            ; segment of string definition
  1268.     mov    di,es:[0]        ; get length of string text (count)
  1269.     pop    es
  1270.     mov    [bx].takcnt,di        ; # of chars in buffer
  1271.     mov    [bx].taktyp,0fdh    ; flag as an internal macro
  1272.     pop    bx
  1273.     jmp    short valtoax        ; exit early with CARRY SET(!)
  1274.  
  1275. valtoa30:pop    dx            ; trailing space flag
  1276.     or    dx,dx            ; leave the spaces?
  1277.     jnz    valtoa31        ; nz = yes
  1278.     cmp    word ptr [di-1],0020h    ; trailing space?
  1279.     jne    valtoa31        ; ne = no
  1280.     dec    di            ; remove space
  1281. valtoa31:sub    di,offset valbuf+2    ; di = length of the buffer contents
  1282.     clc
  1283.     ret
  1284. valtoax:stc
  1285.     ret
  1286. valtoa    endp
  1287.  
  1288. ; Far callable version
  1289. fvaltoa    proc    far
  1290.     call    valtoa
  1291.     ret
  1292. fvaltoa    endp
  1293. ; Set envadr to the string following the <variable=> keyword in the
  1294. ; Environment and set envlen to its length after removing leading and
  1295. ; trailing whitespace.
  1296. ; <variable> starts at ds:<valtmp>, of length cmsiz-1, and can be mixed case.
  1297. ; Return carry set if can't find the <variable=> line in the Environment.
  1298. envvar    proc    near
  1299.     push    es
  1300.     mov    bx,valtmp        ; start of variable name
  1301.     mov    cx,cmsiz        ; length of variable name, w/o ')'
  1302.     or    cx,cx            ; empty?
  1303.     jle    envvar3            ; le = nothing to look for, fail
  1304.     push    bx
  1305.     push    cx
  1306. envvar1:mov    al,byte ptr [bx]    ; scan variable name in our buffer
  1307.     cmp    al,'a'            ; lower case
  1308.     jb    envvar2            ; b = no
  1309.     cmp    al,'z'            ; still in lower case range?
  1310.     ja    envvar2            ; a = no
  1311.     and    al,not 20h        ; convert to DOS's upper case
  1312.     mov    byte ptr [bx],al    ; replace char
  1313. envvar2:inc    bx
  1314.     loop    envvar1
  1315.     pop    cx
  1316.     pop    bx            ; find "<variable>=" in Environment
  1317.     call    fgetenv            ; dx = offset in Environment of "="
  1318.     jnc    envvar4            ; nc = success
  1319. envvar3:pop    es            ; no such variable
  1320.     stc                ; c = failure
  1321.     ret
  1322. ; dx has offset in Environment of char "="
  1323. ; ds:valtmp is start of variable name, cmsiz is length + 1 of the name.
  1324. ; Return seg:offset and length variables so we can copy from there in valtoa
  1325. envvar4:push    di
  1326.     push    si
  1327.     xor    ax,ax
  1328.     mov    word ptr envadr,ax    ; offset in env of variable's string
  1329.     mov    word ptr envadr+2,ax    ; seg of same
  1330.     mov    envlen,ax        ; length of string
  1331.     mov    es,psp            ; our Prog Seg Prefix segment
  1332.     mov    ax,es:word ptr[env]    ; pick up Environment address
  1333.     mov    es,ax            ; set es: to Environment segment
  1334.     mov    di,dx            ; line scan pointer
  1335.     cmp    byte ptr es:[di],'='    ; did we stop on this?
  1336.     jne    envvar5            ; ne = no
  1337.     inc    di            ; skip the "=" char
  1338. envvar5:mov    al,es:[di]        ; scan over leading white space
  1339.     inc    di
  1340.     or    al,al            ; end of line terminator?
  1341.     jz    envvarf            ; z = yes, fail
  1342.     cmp    al,TAB            ; HT?
  1343.     jne    envvar6            ; ne = no
  1344.     mov    al,' '            ; HT becomes a space
  1345. envvar6:cmp    al,' '            ; white space?
  1346.     je    envvar5            ; scan off white space
  1347.     dec    di            ; backup to non-white char
  1348.     mov    word ptr envadr,di    ; offset of string in Environment
  1349.     mov    word ptr envadr+2,es    ; seg of string in Environment
  1350.     mov    si,di            ; remember starting offset here
  1351.                     ; remove trailing spaces from string
  1352.     xor    al,al            ; a null
  1353.     mov    cx,127            ; max length to search
  1354.     cld
  1355.     repne    scasb            ; skip over non-nulls
  1356.     dec    di            ; backup to null
  1357.     dec    di            ; backup to last string char
  1358.     mov    cx,di            ; ending offset
  1359.     inc    cx            ; count the char
  1360.     sub    cx,si            ; minus starting offset yields length
  1361.     jcxz    envvar9            ; z = empty string
  1362. envvar7:mov    al,es:[di]        ; last char
  1363.     dec    di            ; backup one char
  1364.     cmp    al,' '            ; space?
  1365.     je    envvar8            ; e = yes
  1366.     cmp    al,TAB            ; HT?
  1367.     jne    envvar9            ; ne = no, end of white space
  1368. envvar8:loop    envvar7            ; keep looking
  1369. envvar9:mov    envlen,cx        ; store the length
  1370.     clc                ; say success
  1371.     jmp    short envvarx
  1372. envvarf:stc                ; say failure
  1373. envvarx:pop    si
  1374.     pop    di
  1375.     pop    es
  1376.     ret
  1377. envvar    endp
  1378.  
  1379. ; Read chars from Take file, keyboard, or redirected stdin. Edit and remove
  1380. ; BS & DEL, Tab becomes space, act on Control-C, pass Control-U and Control-W.
  1381. ; Do echoing unless comand.cmquiet is non-zero. Do semicolon comments in Take
  1382. ; and indirect stdin files (\; means literal semicolon). Return char in AL.
  1383. CMGETC    proc    near            ; Basic raw character reader
  1384. cmget01:cmp    prevch,0        ; left over char yet to be exported?
  1385.     je    cmget02            ; e = no
  1386.     mov    al,prevch        ; get old char
  1387.     mov    prevch,0        ; clear storage
  1388.     jmp    cmget6            ; analyze it
  1389. cmget02:cmp    taklev,0        ; in a Take file?
  1390.     jne    cmget1            ; ne = yes, do Take reader section
  1391.     call    fisdev            ; is stdin a device or a file?
  1392.     jnc    cmget20            ; nc = file (redirection of stdin)
  1393.     jmp    cmget10            ; c = device, do separately
  1394.  
  1395. cmget20:call    fiseof            ; see if file is empty
  1396.     jc    cmget21            ; c = EOF on disk file
  1397.     mov    ah,coninq        ; read the char from file, not device
  1398.     int    dos
  1399.     cmp    al,cr            ; is it a cr?
  1400.     je    cmget01            ; yes, ignore and read next char
  1401.     cmp    al,ctlz            ; Control-Z?
  1402.     je    cmget21            ; e = yes, same as EOF here
  1403.     cmp    al,lf            ; LF's end lines from disk files
  1404.     jne    cmget8            ; ne = not LF, pass along as is
  1405.     mov    al,cr            ; make LF a CR for this parser
  1406.     call    fiseof            ; see if this is the last char in file
  1407.     jnc    cmget8            ; nc = not EOF, process new CR
  1408. cmget21:mov    flags.extflg,1        ; EOF on disk file, set exit flag
  1409.     jmp    short cmget6        ; do echoing and return
  1410.  
  1411. cmget1:    push    bx            ; read from Take file
  1412.     mov    bx,takadr        ; offset of this Take structure
  1413.     cmp    [bx].takcnt,0        ; bytes remaining in Take buffer
  1414.     jne    cmget4            ; ne = not empty
  1415.     cmp    [bx].taktyp,0feh    ; type of Take (file?)
  1416.     jne    cmget3            ; ne = no (macro)
  1417.     call    ftakrd            ; read another buffer
  1418.     cmp    [bx].takcnt,0        ; anything in the buffer?
  1419.     jne    cmget4            ; ne = yes
  1420. cmget3:    pop    bx            ; clear stack
  1421.     jmp    short cmget5        ; close take file
  1422.  
  1423. cmget4:    push    si
  1424.     push    es
  1425.     mov    es,[bx].takbuf        ; segment of Take buffer
  1426.     mov    si,[bx].takptr        ; current offset in Take buffer
  1427.     mov    al,es:[si]        ; read a char from Take buffer
  1428.     inc    si
  1429.     mov    [bx].takptr,si        ; move buffer pointer
  1430.     dec    [bx].takcnt        ; decrease number of bytes remaining
  1431.     pop    es
  1432.     pop    si
  1433.     pop    bx
  1434.     cmp    al,ctlz            ; Control-Z?
  1435.     jne    cmget6            ; ne = no
  1436. cmget5:    push    bx
  1437.     mov    bx,takadr        ; offset of this Take structure
  1438.     mov    ah,[bx].taktyp        ; save kind of Take/macro
  1439.     pop    bx
  1440.     cmp    ah,0fdh            ; internal macro?
  1441.     je    cmget5b            ; e = yes, close but no auto-CR
  1442.     cmp    comand.cmkeep,0        ; keep Take/macro open after eof?
  1443.     jne    cmget5a            ; ne = yes
  1444. cmget5b:push    ax
  1445.     call    ftakclos        ; close take file
  1446.     pop    ax
  1447.     cmp    ah,0fdh            ; internal macro?
  1448.     jne    cmget5a            ; ne = no, file or regular macro
  1449.     jmp    cmgetc            ; internal macros have no auto CR
  1450. cmget5a:mov    al,cr            ; report cr as last char
  1451.     mov    noparse,0        ; and say end of comment
  1452.                     ; start common code
  1453. cmget6:
  1454.     cmp    al,lf            ; line feed?
  1455.     jne    cmget8            ; ne = no
  1456.     jmp    cmget01            ; yes, ignore and read another char
  1457.                     ; handle comments (echo but not parse)
  1458. cmget8:    cmp    noparse,0        ; parsing?
  1459.     jne    cmget9            ; ne = yes, do echo and no parse
  1460.     cmp    prevch,0        ; have previous char to analyze?
  1461.     jne    cmget8c            ; ne = yes
  1462.     cmp    taklev,0        ; in a Take file or Macro?
  1463.     je    cmget8e            ; e = no, use ";" as a literal
  1464.     push    bx            ; treat ";" as literal in int macros
  1465.     mov    bx,takadr        ; offset of this Take structure
  1466.     cmp    [bx].taktyp,0fdh    ; internal macro?
  1467.     pop    bx
  1468.     je    cmget8e            ; e = yes, semicolons are literals
  1469.     cmp    al,'\'            ; start of '\;'?
  1470.     jne    cmget8a            ; ne = no
  1471.     mov    prevch,al        ; yes, maybe. save '\' til later
  1472.     jmp    cmget02            ; read next char
  1473. cmget8a:cmp    al,';'            ; possible start of comment?
  1474.     jne    cmget8e            ; no, export al
  1475.     mov    al,' '            ; replace ';' with space for comment
  1476.     jmp    short cmget9        ; go start a comment
  1477.  
  1478. cmget8c:cmp    al,';'            ; end of '\;'?
  1479.     je    cmget8d            ; e = yes, omit leading backslash
  1480.     xchg    prevch,al        ; no, save new, recover old '\'
  1481.     jmp    short cmget8e        ; export '\'
  1482. cmget8d:mov    prevch,0        ; clear old '\'
  1483. cmget8e:jmp    short cmget12        ; do parsing
  1484.  
  1485.                     ; echo comment
  1486. cmget9:    cmp    flags.takflg,0        ; echoing take files?
  1487.     je    cmget9a            ; e = no
  1488.     push    ax
  1489.     push    dx
  1490.     mov    ah,conout        ; echo current char
  1491.     mov    dl,al
  1492.     int    dos
  1493.     pop    dx
  1494.     pop    ax
  1495. cmget9a:mov    noparse,0        ; cr ends comment
  1496.     cmp    al,cr            ; end of comment?
  1497.     je    cmget13            ; e = yes, export cr
  1498.     mov    noparse,1        ; still in comment
  1499.     jmp    cmget01            ; read more chars
  1500.  
  1501.                     ; read from tty device
  1502. cmget10:mov    ah,coninq        ; Get a char from device, not file
  1503.     int    dos            ;  with no echoing
  1504.     or    al,al
  1505.     jnz    cmget12            ; ignore null bytes of special keys
  1506.     int    dos            ; read and discard scan code byte
  1507.     jmp    short cmget10        ; try again
  1508.  
  1509. cmget12:cmp    al,'C'and 1Fh        ; Control-C?
  1510.     je    cmget14            ; e = yes
  1511.     cmp    al,TAB            ; tab is replaced by space
  1512.     jne    cmget13            ; ne = not tab
  1513.     mov    al,' '
  1514.     ret
  1515. cmget13:cmp    al,LF            ; LF becomes CR interactively
  1516.     jne    cmget13a
  1517.     mov    al,CR
  1518. cmget13a:ret                ; normal exit, char is in AL
  1519.  
  1520. cmget14:mov    ah,prstr        ; Control-C handler
  1521.     push    dx
  1522.     mov    dx,offset ctcmsg    ; show Control-C
  1523.     int    dos
  1524.     pop    dx
  1525.     mov    prevch,0
  1526.     mov    flags.cxzflg,'C'    ; tell others the news
  1527.     jmp    dword ptr pcmexit    ; fail immediately via longjmp
  1528. cmgetc    endp
  1529.  
  1530. ; Read chars from user (cmgetc). Detect terminators. Reads from buffer
  1531. ; cmbuf. Set read pointer cmrptr to next free buffer byte if
  1532. ; char is not a terminator: chars CR, LF, FF, '?' (returns carry set for
  1533. ; terminators). Do ^U, ^W editing, convert FF to CR plus clear screen.
  1534. ; Edit "-<cr>" as line continuation, "\-<cr>" as "-<end of line>".
  1535. ; Return char in AH.
  1536. CMINBF    proc    near            ; Buffer reader, final editor
  1537.     cmp    cmwptr,offset cmdbuf+size cmdbuf-3 ; max buffer size - 3
  1538.     jb    cminb1            ; b = not full for writing
  1539.     mov    ah,conout        ; almost full, notify user
  1540.     push    dx
  1541.     mov    dl,bell
  1542.     int    dos
  1543.     pop    dx
  1544.     cmp    cmrptr,offset cmdbuf+size cmdbuf ; reading beyond buffer?
  1545.     jb    cminb1            ; b = no
  1546.     mov    ah,prstr
  1547.     mov    dx,offset cmer09    ; command too long
  1548.     int    dos
  1549.     jmp    prserr            ; overflow = parse error
  1550.  
  1551. cminb1:    push    bx
  1552.     mov    bx,cmrptr        ; read pointer
  1553.     mov    ah,[bx]            ; get current command char while here
  1554.     cmp    bx,cmwptr        ; do we need to read more?
  1555.     pop    bx            ; no if cmrptr < cmwptr
  1556.     jb    cminb2            ; b: cmrptr < cmwptr (have extra here)
  1557.     call    cmgetc            ; no readahead, read another into al
  1558.     mov    ah,al            ; keep char in 'ah'
  1559.     push    bx
  1560.     mov    bx,cmwptr        ; get the pointer into the buffer
  1561.     mov    [bx],ah            ; put it in the buffer
  1562.     inc    bx
  1563.     mov    cmwptr,bx        ; inc write pointer
  1564.     pop    bx
  1565.     jmp    short cminb1        ; call cmgetc until cmwptr >= cmrptr
  1566.                     ; Char to be delivered is in ah
  1567. cminb2:    cmp    ah,'W' and 1fh        ; is it a ^W?
  1568.     jne    cminb3
  1569.     call    cntrlw            ; kill the previous word
  1570.     jnc    cminbf            ; nc = no change, get another char
  1571.     jmp    repars            ; need a new command scan (cleans stk)
  1572.  
  1573. cminb3:    cmp    ah,'U' and 1fh        ; is it a ^U?
  1574.     jne    cminb3a            ; ne = no
  1575.     mov    cmwptr,offset cmdbuf    ; reset buffer write pointer
  1576.     jmp    repars            ; go start over (cleans stack)
  1577.                     ; BS and DEL
  1578. cminb3a:cmp    ah,DEL            ; delete code?
  1579.     je    cminb3b            ; e = yes
  1580.     cmp    ah,BS            ; Backspace (a delete operator)?
  1581.     jne    cminb4            ; ne = no
  1582. cminb3b:call    bufdel            ; delete char from buffer
  1583.     jc    cminb3c            ; c = did erasure
  1584.     jmp    cminbf            ; no erasure, ignore BS, get more
  1585. cminb3c:jmp    repars            ; could have deleted previous token
  1586.  
  1587. cminb4:    push    bx            ; look for hyphen or \hyphen
  1588.     cmp    ah,cr            ; check for hyphen line continuation
  1589.     jne    cminb4b            ; ne = not end of line
  1590.     mov    bx,cmwptr        ; get the pointer into the buffer
  1591.     cmp    bx,offset cmdbuf-2    ; do we have a previous char?
  1592.     jb    cminb4b            ; b = no
  1593.     cmp    byte ptr[bx-2],'-'    ; previous char was a hyphen?
  1594.     jne    cminb4b            ; ne = no
  1595.     pop    bx
  1596.     call    bufdel            ; delete the hyphen
  1597.     jmp    repars
  1598. cminb4b:pop    bx
  1599.                     ; Echoing done here
  1600.     cmp    comand.cmquiet,0    ; quiet mode?
  1601.     jne    cminb5            ; yes, skip echoing
  1602.     cmp    taklev,0        ; in a take file?
  1603.     je    cminb4a            ; e = no
  1604.     cmp    flags.takflg,0        ; echo take file?
  1605.     je    cminb5            ; e = no
  1606. cminb4a:push    ax            ; save the char
  1607.     cmp    ah,' '            ; printable?
  1608.     jae    cminb4c            ; yes, no translation needed
  1609.     cmp    ah,cr            ; this is printable
  1610.     je    cminb4c
  1611.     cmp    ah,lf
  1612.     je    cminb4c
  1613.     cmp    ah,escape        ; escape?
  1614.     je    cminb4d            ; do not echo this character
  1615.     push    ax            ; show controls as caret char
  1616.     push    dx
  1617.     mov    dl,5eh            ; caret
  1618.     mov    ah,conout
  1619.     int    dos
  1620.     pop    dx
  1621.     pop    ax
  1622.     add    ah,'A'-1        ; make control code printable
  1623. cminb4c:push    dx
  1624.     mov    dl,ah
  1625.     mov    ah,conout
  1626.     int    dos            ; echo it ourselves
  1627.     pop    dx
  1628. cminb4d:pop    ax            ; and return char in ah
  1629.  
  1630. cminb5:    cmp    ah,cr            ; carriage return?
  1631.     je    cminb6
  1632.     cmp    ah,lf            ; line feed?
  1633.     je    cminb6
  1634.     cmp    ah,ff            ; formfeed?
  1635.     jne    cminb7            ; none of the above, report bare char
  1636.     call    fcmblnk            ; FF: clear the screen and
  1637.     push    bx
  1638.     push    cx
  1639.     push    dx
  1640.     call    flocate            ; Home the cursor
  1641.     mov    bx,cmwptr        ; make the FF parse like a cr
  1642.     mov    byte ptr [bx-1],cr    ; pretend a carriage return were typed
  1643.     pop    dx
  1644.     pop    cx
  1645.     pop    bx
  1646. cminb6: cmp    cmwptr,offset cmdbuf    ; parsed any chars yet?
  1647.     jne    cminb7            ; ne = yes
  1648.     cmp    comand.cmcr,0        ; bare cr's allowed?
  1649.     jne    cminb7            ; ne = yes
  1650.     jmp    prserr            ; If not, just start over
  1651. cminb7:    clc
  1652.     ret
  1653. cminbf    endp
  1654.  
  1655. ; Read chars from cminbf. Cmrptr points to next char to be read.
  1656. ; Compresses repeated spaces if cmsflg is non-zero. Exit with cmrptr pointing
  1657. ; at a terminator or otherwise at next free slot.
  1658. ; Non-space then space acts as a terminator but cmrptr is incremented.
  1659. ; Substitution variables, '\%x', are detected and expanded. Return char in AH.
  1660.  
  1661. CMGTCH    proc    near            ; return char in AH, from rescan buf
  1662.     call    cminbf            ; get char from buffer or user
  1663.     push    bx
  1664.     mov    bx,cmrptr        ; get read pointer into the buffer
  1665.     mov    ah,[bx]            ; read the next char
  1666.     inc    bx
  1667.     mov    cmrptr,bx        ; where to read next time
  1668.     pop    bx
  1669.     call    subst            ; examine for text substitution
  1670.     jnc    cmgtc1            ; nc = no substitutions done
  1671.     jmp    repars            ; reparse line with new material
  1672.  
  1673. cmgtc1:    cmp    ah,' '            ; space?
  1674.     jne    cmgtc3            ; ne = no
  1675.     cmp    bracecnt,0        ; are we within braces?
  1676.     jne    cmgtc3            ; ne = yes, treat space as literal
  1677.     cmp    cmsflg,0        ; space flag, was last char a space?
  1678.     jne    cmgtch            ; ne = yes, get another char
  1679.     mov    cmsflg,0FFH        ; Set the space(s)-seen flag
  1680.     mov    ah,' '            ; character for caller
  1681.     stc                ; set carry for terminator
  1682.     ret                ; return space as a terminator
  1683. cmgtc3: mov    cmsflg,0        ; clear the space-seen flag
  1684.     cmp    ah,braceop        ; opening brace?
  1685.     jne    cmgtc3b            ; ne = no
  1686.     inc    bracecnt        ; count it
  1687.     jmp    short cmgtc3c
  1688. cmgtc3b:cmp    ah,bracecl        ; closing brace?
  1689.     jne    cmgtc3c            ; ne = no
  1690.     sub    bracecnt,1        ; count down and get a sign bit
  1691.     jns    cmgtc3c            ; ns = no underflow
  1692.     mov    bracecnt,0        ; catch underflows
  1693. cmgtc3c:cmp    ah,escape        ; terminators remain in buffer but
  1694.     je    cmgtc4            ;  are ready to be overwritten
  1695.     cmp    ah,'?'            ; is the user curious?
  1696.     jne    cmgtc3a            ; ne = no
  1697.     cmp    taklev,0        ; in a Take file?
  1698.     jne    cmgtc3d            ; ne = yes, make query ordinary char
  1699.     je    cmgtc4
  1700. cmgtc3a:cmp    ah,cr
  1701.     je    cmgtc4
  1702.     cmp    ah,lf
  1703.     je    cmgtc4
  1704.     cmp    ah,ff
  1705.     je    cmgtc4
  1706. cmgtc3d:clc                ; carry clear for non-terminator
  1707.     ret
  1708. cmgtc4: dec    cmrptr            ; point at terminating char
  1709.     stc                ; set carry to say it is a terminator
  1710.     ret
  1711. cmgtch    endp
  1712.  
  1713. ; Reset comand.cmdbuf write pointer (cmwptr) to where the read pointer
  1714. ; (cmrptr) is now. Discards material not yet read.
  1715. bufreset proc    near
  1716.     push    cmrptr            ; where next visible char is read
  1717.     push    ax            ; count removed curly braces
  1718.     push    si
  1719.     mov    si,cmrptr        ; where to look
  1720.     mov    cx,cmwptr        ; last place being removed
  1721.     sub    cx,si            ; length to examine
  1722.     cld
  1723. bufres1:lodsb
  1724.     cmp    al,braceop        ; opening brace, counted already?
  1725.     jne    bufres2            ; ne = no
  1726.     dec    bracecnt        ; uncount it
  1727.     jmp    short bufres3
  1728. bufres2:cmp    al,bracecl        ; closing brace, counted already?
  1729.     jne    bufres3            ; jne = no
  1730.     inc    bracecnt        ; uncount it
  1731. bufres3:loop    bufres1
  1732.     cmp    bracecnt,0        ; negative?
  1733.     jge    bufres4            ; ge = no
  1734.     mov    bracecnt,0
  1735. bufres4:pop    si
  1736.     pop    ax
  1737.     pop    cmwptr            ; where new char goes in buffer
  1738.     ret
  1739. bufreset endp
  1740.  
  1741. ; Delete character from screen and adjust buffer. Returns carry clear if
  1742. ; no erasure, carry set otherwise.
  1743. bufdel    proc    near
  1744.     push    ax
  1745.     push    si
  1746.     mov    si,cmrptr
  1747.     mov    al,[si]
  1748.     cmp    al,braceop        ; opening brace, counted already?
  1749.     jne    bufdel1            ; ne = no
  1750.     dec    bracecnt        ; uncount it
  1751.     jmp    short bufdel2
  1752. bufdel1:cmp    al,bracecl        ; closing brace?
  1753.     jne    bufdel2            ; ne = no
  1754.     inc    bracecnt        ; uncount it
  1755. bufdel2:cmp    bracecnt,0        ; negative?
  1756.     jge    bufdel3            ; ge = no
  1757.     mov    bracecnt,0
  1758. bufdel3:pop    si
  1759.     pop    ax
  1760.     dec    cmrptr            ; remove previous char from buffer
  1761.     cmp    cmrptr,offset cmdbuf    ; back too far?
  1762.     jae    bufde2            ; ae = no, material can be erased
  1763.     mov    cmrptr,offset cmdbuf    ; set to start of buffer
  1764.     call    bufreset        ; reset buffer
  1765.     mov    bracecnt,0        ; ensure this is now cleared
  1766.     clc                ; say no erasure
  1767.     ret
  1768. bufde2:    call    bufreset        ; reset buffer
  1769.     stc                ; say did erasure
  1770.     ret
  1771. bufdel    endp
  1772.  
  1773. ; Come here is user types ^W when during input. Remove word from buffer.
  1774. cntrlw    proc    near
  1775.     push    ax
  1776.     push    cx
  1777.     push    dx
  1778.     call    bufreset        ; truncate buffer at cmrptr
  1779.     mov    cx,cmrptr        ; read pointer
  1780.     sub    cx,offset cmdbuf    ; compute chars in buffer
  1781.     clc                ; say have not yet modified line
  1782.     jcxz    ctlw2            ; z = nothing to do, exit no-carry
  1783.     push    es
  1784.     std                ; scan backward
  1785.     mov    ax,ds
  1786.     mov    es,ax            ; point to the data are
  1787.     mov    di,cmwptr        ; looking from here
  1788.     dec    di
  1789.     mov    al,' '
  1790.     repe    scasb            ; look for non-space
  1791.     je    ctlw1            ; all spaces, nothing to do
  1792.     inc    di            ; move back to non-space
  1793.     inc    cx
  1794.     repne    scasb            ; look for a space
  1795.     jne    ctlw1            ; no space, leave ptrs alone
  1796.     inc    di
  1797.     inc    cx            ; skip back over space
  1798. ctlw1:    inc    di
  1799.     pop    es
  1800.     cld                ; reset    direction flag
  1801.     mov    cmwptr,di        ; update pointer
  1802.     stc                ; set carry to say modified line
  1803. ctlw2:    pop    dx
  1804.     pop    cx
  1805.     pop    ax
  1806.     ret
  1807. cntrlw    endp
  1808.  
  1809. ; Jump to REPARS to do a rescan of the existing buffer.
  1810. ; Jump to PRSERR on a parsing error (quits command, clears old read material)
  1811.  
  1812. PRSERR    PROC NEAR
  1813.     mov    cmwptr,offset cmdbuf    ; initialize write pointer
  1814.     mov    ah,prstr
  1815.     mov    dx,offset crlf        ; leave old line, start a new one
  1816.     int    dos
  1817.     call    rprompt            ; restore master prompt level
  1818.                     ; reparse current line
  1819. REPARS:    mov    cmrptr,offset cmdbuf    ; reinit read pointer
  1820.     mov    comand.cmper,0        ; reset to variable recognition
  1821.     mov    cmsflg,0ffh        ; strip leading spaces
  1822.     mov    subcnt,0        ; clear substitution state variables
  1823.     mov    subtype,0
  1824.     mov    bracecnt,0
  1825.     cmp    taklev,0        ; in Take cmd?
  1826.     je    prser2            ; e = no
  1827.     cmp    flags.takflg,0        ; echo contents of Take file?
  1828.     je    prser3            ; e = no
  1829. prser2:    call    fctlu            ; clear display's line, reuse it
  1830.     mov    dx,comand.cmprmp    ; display the asciiz prompt
  1831.     call    fprtasz
  1832. prser3:    mov    bx,0ffffh        ; returned keyword value
  1833.     mov    sp,comand.cmostp    ; set new sp to old one
  1834.     jmp    dword ptr comand.cmrprs    ; jump to just before the prompt call
  1835. PRSERR    ENDP
  1836.  
  1837. ; Restore prompt material to that of the master prompt. This removes settings
  1838. ; of local PROMPT calls so we can reprompt at the main Kermit level.
  1839. RPROMPT    proc    near
  1840.     push    ax
  1841.     mov    ax,mcmprmp        ; address of prompt string
  1842.     or    ax,ax            ; any address given yet?
  1843.     jz    rprompt1        ; z = none, not inited yet
  1844.     mov    comand.cmprmp,ax    ; set current address ptr
  1845.     mov    ax,word ptr mcmrprs    ; offset of reparse address
  1846.     mov    word ptr comand.cmrprs,ax
  1847.     mov    ax,word ptr mcmrprs+2    ; segment of reparse address
  1848.     mov    word ptr comand.cmrprs+2,ax
  1849.     mov    ax,mcmostp        ; stack ptr at reparse time
  1850.     mov    comand.cmostp,ax
  1851. rprompt1:pop    ax
  1852.     ret
  1853. RPROMPT    endp
  1854.  
  1855.  
  1856.  
  1857. ; write \v(ARGC) contents to ds:di
  1858. wrtargc    proc    near
  1859.     xor    ax,ax
  1860.     cmp    taklev,0        ; in a Take/Macro?
  1861.     je    wrtarg1            ; e = no
  1862.     mov    bx,takadr        ; current Take structure
  1863.     mov    ax,[bx].takargc        ; get ARGC
  1864. wrtarg1:call    fdec2di            ; write as ascii
  1865.     mov    word ptr [di],0020h    ; space and null terminator
  1866.     inc    di
  1867.     ret
  1868. wrtargc    endp
  1869.  
  1870. ; write \v(COUNT) text to ds:di
  1871. wrtcnt    proc    near
  1872.     xor    ax,ax
  1873.     cmp    taklev,0        ; in a Take/Macro?
  1874.     je    wrtcnt1            ; e = no
  1875.     mov    bx,takadr        ; current Take structure
  1876.     mov    ax,[bx].takctr        ; get COUNT
  1877. wrtcnt1:call    fdec2di            ; write as ascii
  1878.     mov    word ptr [di],0020h    ; space and null terminator
  1879.     inc    di
  1880.     ret
  1881. wrtcnt    endp
  1882.  
  1883. ; write \v(DATE) text to ds:di
  1884. wrtdate    proc    near
  1885.     push    cx
  1886.     push    dx
  1887.     mov    ah,getdate        ; DOS date (cx= yyyy, dh= mm, dl= dd)
  1888.     int    dos
  1889.     xor    ah,ah
  1890.     cmp    tdfmt,0            ; USA standard mm/dd/yyyy?
  1891.     jne    wrtdat1            ; ne = no
  1892.     xor    ah,ah
  1893.     mov    al,dh            ; month
  1894.     call    wrtdat5            ; output
  1895.     mov    byte ptr [di],'/'    ; separate
  1896.     inc    di
  1897.     xor    ah,ah
  1898.     mov    al,dl            ; day
  1899.     call    wrtdat5
  1900.     mov    byte ptr [di],'/'
  1901.     inc    di
  1902.     mov    ax,cx
  1903.     jmp    short wrtdat3
  1904.  
  1905. wrtdat1:cmp    tdfmt,1            ; European standard dd/mm/yyyy?
  1906.     jne    wrtdat2            ; ne = no
  1907.     xor    ah,ah
  1908.     mov    al,dl            ; day
  1909.     call    wrtdat5
  1910.     mov    byte ptr [di],'/'
  1911.     inc    di
  1912.     xor    ah,ah
  1913.     mov    al,dh            ; month
  1914.     call    wrtdat5
  1915.     mov    byte ptr [di],'/'
  1916.     inc    di
  1917.     mov    ax,cx
  1918.     jmp    short wrtdat3
  1919.  
  1920. wrtdat2:mov    ax,cx            ; Japan yyyy:mm:dd,   year
  1921.     call    wrtdat5
  1922.     mov    byte ptr [di],':'
  1923.     inc    di
  1924.     xor    ah,ah
  1925.     mov    al,dh            ; month
  1926.     call    wrtdat5
  1927.     mov    byte ptr [di],':'
  1928.     inc    di
  1929.     xor    ah,ah
  1930.     mov    al,dl            ; day
  1931. wrtdat3:call    wrtdat5
  1932.     mov    word ptr [di],0020h    ; space and null terminator
  1933.     inc    di
  1934.     pop    dx
  1935.     pop    cx
  1936.     ret
  1937.     ret
  1938.  
  1939. wrtdat5:cmp    ax,10            ; leading tens digit present?
  1940.     jae    wrtdat6            ; ae = yes
  1941.     mov    byte ptr [di],'0'    ; insert leading 0
  1942.     inc    di
  1943. wrtdat6:call    fdec2di            ; write decimal asciiz to buffer
  1944.     ret
  1945.  
  1946. wrtdate    endp
  1947.  
  1948. ; write \v(ERRORLEVEL) text to ds:di
  1949. wrterr    proc    near
  1950.     mov    al,errlev        ; current Errorlevel
  1951.     xor    ah,ah
  1952.     call    fdec2di            ; write as ascii
  1953.     mov    word ptr [di],0020h    ; space and null terminator
  1954.     inc    di
  1955.     ret
  1956. wrterr    endp
  1957.  
  1958. ; write \v(KEYBOARD) text to ds:di
  1959. wrtkbd    proc    near
  1960.     mov    ax,keyboard        ; 88 or 101 keyboard keys
  1961.     call    fdec2di            ; write as ascii
  1962.     mov    word ptr [di],0020h    ; space and null terminator
  1963.     inc    di
  1964.     ret
  1965. wrtkbd    endp
  1966.  
  1967. ; write \v(NDATE) text to ds:di
  1968. ; where NDATE is YYYYMMDD
  1969. wrtndate proc    near
  1970.     mov    ah,getdate        ; DOS date (cx= yyyy, dh= mm, dl= dd)
  1971.     int    dos
  1972.     push    dx            ; save dx
  1973.     mov    ax,cx            ; year
  1974.     call    fdec2di            ; convert it
  1975.     pop    dx            ; get mm:dd
  1976.     push    dx
  1977.     mov    al,dh            ; months are next
  1978.     xor    ah,ah
  1979.     cmp    al,10            ; less than 10?
  1980.     jae    wrtndat1        ; ae = no
  1981.     mov    byte ptr [di],'0'    ; leading 0
  1982.     inc    di
  1983. wrtndat1:call    fdec2di
  1984.     pop    dx
  1985.     mov    al,dl            ; get days
  1986.     xor    ah,ah
  1987.     cmp    al,10            ; less than 10?
  1988.     jae    wrtndat2        ; ae = no
  1989.     mov    byte ptr [di],'0'    ; leading 0
  1990.     inc    di
  1991. wrtndat2:call    fdec2di
  1992.     mov    word ptr [di],0020h    ; space and null terminator
  1993.     inc    di
  1994.     ret
  1995. wrtndate endp
  1996.  
  1997. ; write \v(DIRECTORY) text to ds:di
  1998. wrtdir    proc    near
  1999.     push    si
  2000.     mov    ah,gcurdsk        ; get current disk
  2001.     int    dos
  2002.     add    al,'A'            ; make al = 0 == 'A'
  2003.     mov    [di],al
  2004.     mov    word ptr [di+1],'\:'
  2005.     mov    si,di            ; work buffer
  2006.     add    si,3            ; end with a colon and backslash
  2007.     mov    ah,gcd            ; get current directory
  2008.     xor    dl,dl            ; use current drive
  2009.     int    dos            ; get ds:si = asciiz path (no drive)
  2010.     mov    dx,di
  2011.     call    fstrlen
  2012.     add    di,cx
  2013.     cmp    cx,3            ; directory added?
  2014.     je    wrtdir1            ; e = no
  2015.     mov    byte ptr [di],'\'    ; add slash terminator so filenames
  2016.     inc    di            ;  can be appended easily
  2017. wrtdir1:mov    word ptr [di],0020h    ; space and null terminator
  2018.     inc    di
  2019.     pop    si
  2020.     ret
  2021. wrtdir    endp
  2022.  
  2023. fwrtdir    proc    far
  2024.     call    wrtdir
  2025.     ret
  2026. fwrtdir    endp
  2027.  
  2028. ; write \v(PLATFORM) text to ds:di
  2029. wrtplat    proc    near
  2030.     push    si
  2031.     mov    si,offset machnam    ; machine name in sys dep file
  2032.     cld
  2033. wrtplat1:lodsb                ; get a char
  2034.     cmp    al,'$'            ; terminator?
  2035.     je    wrtplat2        ; e = yes
  2036.     mov    [di],al            ; store char
  2037.     inc    di
  2038.     jmp    short wrtplat1        ; keep going
  2039. wrtplat2:mov    word ptr [di],0020h    ; space and null terminator
  2040.     inc    di
  2041.     mov    temp,di            ; place for additional text
  2042.     pop    si
  2043.     ret
  2044. wrtplat    endp
  2045.  
  2046. ; write \v(PORT) text to ds:di
  2047. wrtport    proc    near
  2048.     push    bx
  2049.     push    si
  2050.     mov    al,flags.comflg        ; get coms port indicator
  2051.     mov    bx,offset comptab    ; table of comms ports
  2052.     mov    cl,[bx]            ; number of entries
  2053.     xor    ch,ch
  2054.     inc    bx
  2055. wrtpor3:mov    dx,[bx]            ; length of this entry
  2056.     mov    si,bx
  2057.     add    si,2            ; points to entry text string
  2058.     add    si,dx            ; point to qualifier
  2059.     cmp    [si],al            ; our port?
  2060.     je    wrtpor4            ; e = yes
  2061.     add    bx,[bx]            ; add text length
  2062.     add    bx,4            ; plus count and qualifier
  2063.     loop    wrtpor3            ; next entry
  2064.     jmp    short wrtpor5        ; no match, curious
  2065. wrtpor4:mov    si,bx            ; point at entry
  2066.     add    si,2            ; point at string
  2067.     mov    cx,[bx]            ; length of string
  2068.     push    es
  2069.     mov    ax,ds
  2070.     mov    es,ax
  2071.     cld
  2072.     rep    movsb            ; copy to DS:DI
  2073.     pop    es
  2074. wrtpor5:mov    word ptr [di],0020h    ; space and null terminator
  2075.     inc    di
  2076.     pop    si
  2077.     pop    bx
  2078.     ret
  2079. wrtport    endp
  2080.  
  2081. ; write \v(PROGRAM) text to ds:si
  2082. wrtprog    proc    near
  2083.     push    si
  2084.     mov    si,offset progm        ; source string
  2085.     cld
  2086. wrtprg1:lodsb
  2087.     cmp    al,'$'            ; terminator?
  2088.     je    wrtprg2            ; e = yes
  2089.     mov    [di],al            ; store the char
  2090.     inc    di
  2091.     jmp    short wrtprg1
  2092. wrtprg2:mov    word ptr [di],0020h    ; space and null terminator
  2093.     inc    di
  2094.     mov    temp,di            ; place for additional text
  2095.     pop    si
  2096.     ret
  2097. wrtprog    endp
  2098.  
  2099. ; write \v(STATUS) text to ds:di
  2100. wrtstat    proc    near
  2101.     mov    ax,kstatus        ; Kermit status word
  2102.     call    fdec2di
  2103.     mov    word ptr [di],0020h    ; space and null terminator
  2104.     inc    di
  2105.     mov    temp,di            ; place for additional text
  2106.     ret
  2107. wrtstat    endp
  2108.  
  2109. ; write \v(SYSTEM) text to ds:di
  2110. wrtsystem proc    near
  2111.     push    si
  2112.     mov    si,offset system    ; system string "MS-DOS", dollar sign
  2113.     cld
  2114.     jmp    wrtplat1        ; use some common code
  2115. wrtsystem endp
  2116.  
  2117. ; write \v(TIME) text to ds:di
  2118. wrttime    proc    near
  2119.     mov    ah,gettim        ; DOS tod (ch=hh, cl=mm, dh=ss, dl=.s)
  2120.     int    dos
  2121.     push    dx            ; save dx
  2122.     xor    ah,ah
  2123.     mov    al,ch            ; Hours
  2124.     cmp    al,10            ; leading digit?
  2125.     jae    wrttim1            ; ae = yes
  2126.     mov    byte ptr [di],'0'    ; make our own
  2127.     inc    di
  2128. wrttim1:push    cx
  2129.     call    fdec2di            ; write decimal asciiz to buffer
  2130.     pop    cx
  2131.     mov    byte ptr [di],':'
  2132.     inc    di
  2133.     xor    ah,ah
  2134.     mov    al,cl            ; Minutes
  2135.     cmp    al,10            ; leading digit?
  2136.     jae    wrttim2            ; ae = yes
  2137.     mov    byte ptr [di],'0'    ; make our own
  2138.     inc    di
  2139. wrttim2:call    fdec2di            ; write decimal asciiz to buffer
  2140.     mov    byte ptr [di],':'
  2141.     inc    di
  2142.     pop    dx
  2143.     xor    ah,ah
  2144.     mov    al,dh            ; Seconds
  2145.     cmp    al,10            ; leading digit?
  2146.     jae    wrttim3            ; ae = yes
  2147.     mov    byte ptr [di],'0'    ; make our own
  2148.     inc    di
  2149. wrttim3:call    fdec2di            ; write decimal asciiz to buffer
  2150.     mov    word ptr [di],0020h    ; space and null terminator
  2151.     inc    di
  2152.     mov    temp,di            ; place for additional text
  2153.     ret
  2154. wrttime    endp
  2155.  
  2156. ; write \v(Version) text to ds:di
  2157. wrtver    proc    near
  2158.     mov    si,offset verident    ; MS Kermit version string in mssker
  2159.     cld
  2160. wrtver1:lodsb
  2161.     mov    [di],al
  2162.     inc    di
  2163.     cmp    al,'$'            ; end of string?
  2164.     jne    wrtver1            ; ne = no, continue copying
  2165.     dec    di
  2166.     mov    word ptr [di],0020h    ; space and null terminator
  2167.     inc    di
  2168.     mov    temp,di            ; place for additional text
  2169.     ret
  2170. wrtver    endp
  2171.  
  2172. ; write \v(TERMINAL) text to ds:di
  2173. wrtterm    proc    near
  2174.     mov    ax,flags.vtflg        ; current terminal type
  2175.     mov    bx,offset termtb    ; terminal type table msx...
  2176.     mov    cl,[bx]            ; number of entries in our table
  2177.     xor    ch,ch
  2178.     inc    bx            ; point to the data
  2179. wrtter1:mov    si,[bx]            ; length of keyword
  2180.     cmp    ax,[bx+si+2]        ; value fields match?
  2181.     je    wrtter2            ; e = yes
  2182.     add    bx,si            ; add word length
  2183.     add    bx,4            ; skip count and value fields
  2184.     loop    wrtter1            ; keep searching
  2185.     jmp    short wrtter4        ; no match, use just a space
  2186. wrtter2:mov    cx,[bx]            ; get length of counted string
  2187.     mov    si,bx
  2188.     add    si,2            ; look at text
  2189.     cld
  2190. wrtter3:lodsb
  2191.     mov    [di],al            ; from ds:si to ds:di
  2192.     inc    di
  2193.     loop    wrtter3
  2194. wrtter4:mov    word ptr [di],0020h    ; space and null terminator
  2195.     inc    di
  2196.     mov    temp,di            ; place for additional text
  2197.     ret
  2198. wrtterm    endp
  2199.  
  2200. code1    ends
  2201.  
  2202. code    segment
  2203.     assume    cs:code
  2204.  
  2205. ; Set master prompt level. Enter with DX = offset of prompt string
  2206. MPROMPT    proc    near
  2207.     mov    mcmprmp,dx        ; offset of prompt string
  2208.     pop    ax            ; get the return address
  2209.     mov    word ptr mcmrprs,ax     ; offset to go to on reparse
  2210.     mov    mcmostp,sp        ; stack pointer at reparse time
  2211.     push    ax            ; put it on the stack again
  2212.     mov    ax,cs            ; our current code segment
  2213.     mov    word ptr mcmrprs+2,ax     ; segment of reparse address
  2214.     jmp    short prompt        ; now set the active prompt material
  2215. MPROMPT    endp
  2216.  
  2217. ; This routine prints the prompt and specifies the reparse address.
  2218. ; Enter with pointer to prompt string in dx. 
  2219. PROMPT    PROC  NEAR
  2220.     mov    comand.cmprmp,dx    ; save the prompt
  2221.     pop    ax            ; get the return address
  2222.     mov    word ptr comand.cmrprs,ax ; offset to go to on reparse
  2223.     mov    comand.cmostp,sp    ; save for later restoration
  2224.     push    ax            ; put it on the stack again
  2225.     mov    ax,cs            ; our current code segment
  2226.     mov    word ptr comand.cmrprs+2,ax ; segment of reparse address
  2227.     mov    ax,offset cmdbuf
  2228.     mov    cmwptr,ax        ; reset buffer read/write pointers
  2229.     mov    cmrptr,ax
  2230.     xor    al,al
  2231.     mov    comand.cmper,al        ; allow substitutions
  2232.     mov    cmsflg,0ffh        ; remove leading spaces
  2233.     cmp    flags.takflg,al        ; look at Take flag, zero?
  2234.     jne    promp1            ; ne=supposed to echo, skip this check
  2235.     cmp    taklev,al        ; inside a take file?
  2236.     je    promp1            ; no, keep going
  2237.     ret                ; yes, return
  2238. promp1:    mov    ah,prstr
  2239.     mov    dx,offset crlf
  2240.     int    dos
  2241.     mov    dx,comand.cmprmp    ; prompt pointer
  2242.     call    prtasz            ; show asciiz prompt string
  2243.     clc
  2244.     ret
  2245. PROMPT    ENDP
  2246.  
  2247. ISDEV    PROC    NEAR            ; Set carry if STDIN is non-disk
  2248.     push    ax
  2249.     push    bx
  2250.     push    dx
  2251.     xor    bx,bx            ; handle 0 is stdin
  2252.     xor    al,al            ; get device info
  2253.     mov    ah,ioctl
  2254.     int    dos
  2255.     rcl    dl,1            ; put ISDEV bit into the carry bit
  2256.     pop    dx            ; carry is set if device
  2257.     pop    bx
  2258.     pop    ax
  2259.     ret                ; carry set if device
  2260. ISDEV    ENDP
  2261.  
  2262. ISEOF    PROC    NEAR            ; Set carry if STDIN is at EOF
  2263.     push    ax            ;  but only if stdin is a non-device
  2264.     push    bx
  2265.     push    dx
  2266.     xor    bx,bx            ; handle 0 is stdin
  2267.     xor    al,al            ; get device info
  2268.     mov    ah,ioctl
  2269.     int    dos
  2270.     mov    ah,ioctl
  2271.     mov    al,6            ; get handle input status, set al
  2272.     test    dl,80h            ; bit set if handle is for a device
  2273.     jnz    iseof1            ; nz = device, always ready (al != 0)
  2274.     int    dos
  2275. iseof1:    or    al,al            ; EOF?
  2276.     pop    dx
  2277.     pop    bx
  2278.     pop    ax
  2279.     jnz    iseof2            ; nz = no
  2280.     stc                ; set carry for eof
  2281.     ret
  2282. iseof2:    clc                ; clear carry for not-eof
  2283.     ret
  2284. ISEOF    ENDP
  2285.  
  2286. ; Convert ascii characters in al and ah to lowercase.
  2287. ; All registers are preserved except AX, of course.
  2288.  
  2289. TOLOWR PROC NEAR
  2290.     cmp    ah,'A'            ; less that cap A?
  2291.     jl    tolow1            ; l = yes. leave untouched
  2292.     cmp    ah,'Z'+1        ; more than cap Z?
  2293.     jns    tolow1            ; ns = yes
  2294.     or    ah,20H            ; convert to lowercase
  2295. tolow1:    cmp    al,'A'            ; less that cap A?
  2296.     jl    tolow2            ; l = yes. leave untouched
  2297.     cmp    al,'Z'+1        ; more than cap Z?
  2298.     jns    tolow2            ; ns = yes
  2299.     or    al,20H            ; convert to lowercase
  2300. tolow2:    ret
  2301. TOLOWR    endp
  2302. code    ends
  2303.  
  2304. code1    segment
  2305.     assume    cs:code1
  2306.  
  2307. ; Parse control sequences and device control strings.
  2308. ; Expect CSI, Escape [, or DCS lead-in characters to have been read.
  2309. ; Puts numerical Parameters in array param (16 bits, count is nparam) and
  2310. ;  a single letter Parameter in lparam, (Parameters are all ASCII column 3)
  2311. ;  Intermediate characters in array inter (count is ninter), (ASCII column 2)
  2312. ;  Final character in AL (ASCII columns 4-7).
  2313. ; Invoke by setting state to offset atparse, set pardone to offset of
  2314. ; procedure to jump to after reading Final char (0 means do just ret)
  2315. ; and optionally setting parfail to address to jump to if parsing failure.
  2316. ; When the Final char has been accepted this routine jumps to label held in
  2317. ; pardone for final action. Before the Final char has been read successful
  2318. ; operations return carry clear.
  2319. ; Failure exits are carry set, and an optional jump through parfail (if 
  2320. ; non-zero) or a return.
  2321.  
  2322. atparse    proc    near
  2323.     mov    bx,parstate        ; get parsing state
  2324.     or    bx,bx            ; have any state?
  2325.     jnz    atpars1            ; nz = have a state
  2326.     call    atpclr            ; do initialization
  2327.     mov    bx,parstate        ; get initial state
  2328. atpars1:call    bx            ; execute it
  2329.     jc    atpfail            ; c = failure
  2330.     cmp    parstate,offset atpdone    ; parsed final char?
  2331.     je    atpdone            ; e = yes
  2332.     ret                ; no, wait for another char
  2333.  
  2334.                 ; successful conclusion, final char is in AL
  2335. atpdone:mov    parstate,0        ; reset parsing state
  2336.     cmp    pardone,0        ; separate return address defined?
  2337.     jne    atpdon1            ; ne = yes
  2338.     clc
  2339.     ret                ; else just return
  2340. atpdon1:clc
  2341.     jmp    pardone            ; jmp to supplied action routine
  2342.  
  2343. atpfail:mov    parstate,0        ; failed, reset parser to normal state
  2344.     cmp    parfail,0        ; jump address specified?
  2345.     je    atpfail1        ; e = no
  2346.     jmp    parfail            ; yes, exit this way
  2347. atpfail1:stc
  2348.     ret
  2349.                     ; parsing workers
  2350. atparm:    cmp    ninter,0        ; Parameter, started intermediate yet?
  2351.     jne    atinter            ; ne = yes, no more parameters
  2352.     cmp    al,';'            ; argument separator?
  2353.     jne    atparm3            ; ne = no
  2354.     mov    ax,nparam        ; number of Parameters
  2355.     inc    ax            ; say a new one
  2356.     cmp    ax,maxparam        ; too many?
  2357.     jb    atparm2            ; b = no, continue
  2358.     stc                ; set carry to say failed
  2359.     ret                ; too many, ignore remainder
  2360. atparm2:mov    nparam,ax        ; say doing another Parameter
  2361.     clc
  2362.     ret
  2363.  
  2364. atparm3:mov    ah,al            ; copy char
  2365.     and    ah,not 0fh        ; ignore low nibble
  2366.     cmp    ah,30h            ; column 3, row 0? (30h='0')
  2367.     jne    atparm6            ; ne = no, check Intermediate/Final
  2368.     cmp    al,'9'            ; digit?
  2369.     ja    atparm5            ; a = no, check letter Parameters
  2370.     sub    al,'0'            ; ascii to binary
  2371.     mov    bx,nparam        ; current parameter number
  2372.     shl    bx,1            ; convert to word index
  2373.     mov    cx,param[bx]        ; current parameter value
  2374.     shl    cx,1            ; multiply by 10.  2 * cl
  2375.     push    bx
  2376.     mov    bx,cx            ; save 2 * cl
  2377.     shl    cx,1            ; 4 * cl
  2378.     shl    cx,1            ; 8 * cl
  2379.     add    cx,bx            ; 10 * cl
  2380.     pop    bx
  2381.     add    cl,al            ; add new digit
  2382.     adc    ch,0
  2383.     jnc    atparm4            ; nc = no carry out (65K or below)
  2384.     mov    cx,0ffffh        ; set to max value
  2385. atparm4:mov    param[bx],cx        ; current Parameter value
  2386.     clc
  2387.     ret
  2388.                     ; check non-numeric Parameters
  2389. atparm5:cmp    al,'?'            ; within column 3?
  2390.     ja    atfinal            ; a = no, check Final char
  2391.     mov    lparam,al        ; store non-numeric Parameter
  2392.     clc
  2393.     ret
  2394.  
  2395. atparm6:cmp    nparam,0        ; started a parameter yet?
  2396.     jne    atparm7            ; ne = yes
  2397.     cmp    param,0            ; got anything for param[0]?
  2398.     je    atinter            ; e = no
  2399. atparm7:inc    nparam            ; yes, say finished with another
  2400.  
  2401. atinter:mov    parstate,offset atinter    ; next state (intermediate)
  2402.     cmp    al,';'            ; argument separator?
  2403.     jne    atinte1            ; ne = no
  2404.     cmp    ninter,maxinter        ; too many intermediates?
  2405.     jb    atinte2            ; b = no, continue
  2406.     stc                ; carry = failed
  2407.     ret                ; too many, ignore remainder
  2408. atinte1:test    al,not 2fh        ; column two = 20h - 2fh?
  2409.     jnz    atfinal            ; nz = not an Intermediate, try Final
  2410.     cmp    ninter,maxinter        ; too many intermediates?
  2411.     jb    atinte1a        ; b = no, continue
  2412.     stc                ; carry = failed
  2413.     ret                ; too many, ignore remainder
  2414. atinte1a:mov    bx,ninter        ; current Intermediate slot number
  2415.     mov    inter[bx],al        ; current Intermediate value
  2416. atinte2:inc    ninter            ; say doing another Intermediate
  2417.     clc
  2418.     ret
  2419.  
  2420. atfinal:cmp    al,40h            ; Final character, range is 40h to 7fh
  2421.     jb    atfina1            ; b = out of range
  2422.     cmp    al,7fh
  2423.     ja    atfina1            ; a = out of range
  2424.     mov    parstate,offset atpdone    ; next state is "done"
  2425.     clc                ; success, final char is in AL
  2426.     ret
  2427. atfina1:stc                ; c = failed
  2428.     ret
  2429. atparse    endp
  2430.  
  2431. ; Clear Parameter, Intermediate arrays in preparation for parsing
  2432. atpclr    proc    near
  2433.     push    ax
  2434.     push    cx
  2435.     push    di
  2436.     push    es
  2437.     xor    ax,ax            ; get a null
  2438.     mov    parstate,offset atparm    ; init parser state
  2439.     mov    lparam,al        ; clear letter Parameter
  2440.     mov    nparam,ax        ; clear Parameter count
  2441.     mov    cx,maxparam        ; number of Parameter slots
  2442.     mov    di,offset param        ; Parameter slots
  2443.     push    ds
  2444.     pop    es            ; use data segment for es:di below
  2445.     cld                ; set direction forward
  2446.     rep    stosw            ; clear the slots
  2447.     mov    ninter,ax        ; clear Intermediate count
  2448.     mov    cx,maxinter        ; number of Intermediate slots
  2449.     mov    di,offset inter        ; Intermediate slots
  2450.     rep    stosb            ; clear the slots
  2451.     pop    es
  2452.     pop    di
  2453.     pop    cx
  2454.     pop    ax
  2455.     ret
  2456. atpclr    endp
  2457.  
  2458. ; Dispatch table processor. Enter with BX pointing at table of {char count,
  2459. ; address of action routines, characters}. Jump to matching routine or return.
  2460. ; Enter with AL holding received Final char.
  2461. atdispat proc near
  2462.     mov    cl,[bx]            ; get table length from first byte
  2463.     xor    ch,ch
  2464.     mov    di,bx            ; main table
  2465.     add    di,3            ; point di at first char in table
  2466.     push    es
  2467.     push    ds
  2468.     pop    es            ; use data segment for es:di below
  2469.     cld                ; set direction forward
  2470.     repne    scasb            ; find matching character
  2471.     pop    es
  2472.     je    atdisp2            ; e = found a match, get action addr
  2473.     ret                ; ignore escape sequence
  2474. atdisp2:sub    di,bx            ; distance scanned in table
  2475.     sub    di,4            ; skip count byte, address word, inc
  2476.     shl    di,1            ; convert to word index
  2477.     inc    bx            ; point to address of action routines
  2478.     mov    bx,[bx]            ; get address of action table
  2479.     jmp    word ptr [bx+di]    ; dispatch to the routine
  2480. atdispat endp
  2481. code1    ends
  2482.     end
  2483.