home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / archives / msk316src.zip / MSSCMD.ASM < prev    next >
Assembly Source File  |  1999-08-22  |  164KB  |  5,509 lines

  1.        NAME    msscmd
  2. ; File MSSCMD.ASM
  3.     include mssdef.h
  4. ;    Copyright (C) 1982, 1999, Trustees of Columbia University in the 
  5. ;    City of New York.  The MS-DOS Kermit software may not be, in whole 
  6. ;    or in part, licensed or sold for profit as a software product itself,
  7. ;    nor may it be included in or distributed with commercial products
  8. ;    or otherwise distributed by commercial concerns to their clients 
  9. ;    or customers without written permission of the Office of Kermit 
  10. ;    Development and Distribution, Columbia University.  This copyright 
  11. ;    notice must not be removed, altered, or obscured.
  12. ;
  13. ; Edit history
  14. ; 12 Jan 1995 version 3.14
  15. ; Last edit
  16. ; 12 Jan 1995
  17.  
  18.      public comnd, comand, isdev, iseof, prompt, tolowr, toupr, valtab
  19.     public parstate, pardone, parfail, nparam, param, lparam, ninter
  20.     public inter, atparse, atpclr, atdispat, cspb, dspb, mprompt, nvaltoa
  21.     public savepoff, saveplen, keyboard, fwrtdir, cspb1, filetdate
  22.     public    fqryenv, ifelse, oldifelse, retbuf, vfile
  23.     public    rfprep, rgetfile, findkind, rfileptr, rpathname, rfilename
  24.  
  25. env    equ    2CH            ; environment address in psp
  26. braceop    equ    7bh            ; opening curly brace
  27. bracecl    equ    7dh            ; closing curly brace
  28.  
  29. ; codes for various \fxxx(arg list) operations
  30. F_char        equ    1
  31. F_code        equ    2
  32. F_contents     equ    3
  33. F_definition    equ    4
  34. ;F_eval        equ    5
  35. F_exec        equ    6
  36. F_files        equ    7
  37. F_index        equ    8
  38. F_length    equ    9
  39. F_literal    equ    10
  40. F_lower        equ    11
  41. F_lpad        equ    12
  42. F_maximum     equ    13
  43. F_minimum     equ    14
  44. F_nextfile    equ    15
  45. F_repeat     equ    16
  46. F_reverse    equ    17
  47. F_right        equ    18
  48. F_rpad        equ    19
  49. F_substr    equ    20
  50. F_upper        equ    21
  51. F_date        equ    22
  52. F_size        equ    23
  53. F_replace    equ    24
  54. F_eval        equ    25
  55. F_rindex    equ    26
  56. F_verify    equ    27
  57. F_ipaddr    equ    28
  58. F_tod2secs    equ    29
  59. F_chksum    equ    30
  60. F_basename    equ    31
  61. F_directories    equ    32
  62. F_rfiles    equ    33
  63. F_rdirectories    equ    34
  64.  
  65. ; Constants for valtoa 
  66. V_environ    equ    0
  67. V_argc        equ    1
  68. V_carrier    equ    22
  69. V_charset    equ    33
  70. V_console    equ    27
  71. V_count        equ    2
  72. V_cmdlevel    equ    29
  73. V_cps        equ    24
  74. V_date        equ    3
  75. V_ndate        equ    14
  76. V_dir        equ    5
  77. V_dosver    equ    19
  78. V_errlev    equ    4
  79. V_kbd        equ    10
  80. V_line        equ    15
  81. V_monitor     equ    28
  82. V_parity     equ    21
  83. V_platform     equ    8
  84. V_port         equ    15
  85. V_program     equ    12
  86. V_prompt     equ    23
  87. V_query        equ    31
  88. V_session     equ    17
  89. V_tcpip        equ    20
  90. V_space     equ    25
  91. V_speed     equ    11
  92. V_startup     equ    26
  93. V_status     equ    13
  94. V_sysid        equ    32
  95. V_system     equ    9
  96. V_terminal     equ    16
  97. V_time        equ    6
  98. V_ntime     equ    18
  99. V_version     equ    7
  100. V_input        equ    30
  101. V_inpath    equ    34
  102. V_disk        equ    35
  103. V_cmdfile    equ    36
  104. V_inidir    equ    37
  105. V_instatus    equ    38
  106. V_minput    equ    39
  107. V_return    equ    40
  108. V_connection    equ    41
  109. V_filespec    equ    42
  110. V_fsize        equ    43
  111. V_crc16        equ    44
  112. V_day        equ    45
  113. V_nday        equ    46
  114.  
  115. ; Like main filest, but shorter for local usage.
  116. fileiost struc
  117. mydta    db 26 dup(0)    ; DOS, 21 resev'd bytes, file attr, 2 each date & time
  118. mysizelo dw 0        ; DOS, file size double word
  119. mysizehi dw 0
  120. filename db 13 dup(0)    ; DOS, filename, asciiz, with dot. End of DOS section
  121. fileiost ends
  122.  
  123. data     segment
  124.     extrn    flags:byte, taklev:byte, takadr:word, mcctab:byte
  125.     extrn    kstatus:word, errlev:byte, psp:word, fsta:word
  126.     extrn    portval:word, bdtab:byte, tdfmt:byte, machnam:byte
  127.     extrn    verident:word, kstatus:word, errlev:byte, comptab:byte
  128.     extrn    termtb:byte, dosnum:word, prmptr:word, startup:byte
  129.     extrn    crt_mode:byte, tmpbuf:byte, buff:byte, minpcnt:word
  130.     extrn    decbuf:byte, encbuf:byte, queryseg:word, inpath_seg:word
  131.     extrn    cmdfile:byte, inidir:byte, dos_bottom:byte, marray:word
  132.     extrn    input_status:word, domath_ptr:word, domath_cnt:word
  133.     extrn    domath_msg:word, atoibyte:byte, atoi_err:byte, atoi_cnt:word
  134.     extrn    crcword:word, lastfsize:dword
  135. ifndef    no_tcp
  136.     extrn    sescur:word, tcp_status:word
  137. endif    ; no_tcp
  138.  
  139.                 ; Start Patch structure. Must be first.
  140.     even
  141. dspb    dw    code        ; segment values for patcher
  142.     dw    code1
  143.     dw    code2
  144.     dw    data
  145.     dw    data1
  146.     dw    _TEXT
  147.     db    64 dup (0)    ; data segment patch buffer
  148.                 ;  with space for other material, if req'd
  149.                 ; end of Patch structure
  150. progm    db    'MS-DOS_KERMIT$'    ; for \v(program)
  151. system    db    'MS-DOS$'        ; for \v(system)
  152. keyboard dw    88            ; \v(keyboard) kind of keybd 88/101
  153. ten    db    10            ; for times ten
  154.  
  155. comand    cmdinfo    <>
  156. cmer00  db      cr,lf,'?Program internal error, recovering$'
  157. cmer01    db    cr,lf,'?More parameters are needed$'
  158. cmer02    db    cr,lf,'?Word "$'
  159. cmer03    db    '" is not usable here$'
  160. cmer04    db    '" is ambiguous$'
  161. cmer07    db    cr,lf,'?Ignoring extra characters "$'
  162. cmer08    db    '"$'
  163. cmer09    db    cr,lf,'?Text exceeded available buffer capacity$'
  164. cmin01    db    ' Use one of the following words in this position:',cr,lf,'$'
  165. stkmsg    db    cr,lf,bell,'?Exhausted work space! Circular definition?$'
  166. moremsg    db    '... more, press a key to continue ...$'
  167. crlf    db      cr,lf,'$'
  168. ctcmsg    db    5eh,'C$'
  169. cmunk    db    'unknown'
  170. errflag    db    0            ; non-zero to suppress cmcfrm errors
  171. kwstat    db    0            ; get-keyword status
  172. subcnt    db    0            ; count of chars matched in '\%'
  173. subtype db    0            ; kind of sub (% or v)
  174. evaltype dw    0            ; evaltoa kind
  175. bracecnt db    0            ; curly brace counter
  176. parencnt db    0            ; parenthesis counter
  177. report_binary db 0            ; subst reporting \number directly
  178. argptr    dw    3 dup (0)        ; \fxxx() argument pointers
  179. arglen    dw    3 dup (0)        ; \fxxx() argument lengths
  180. replay_cnt dw    0            ; report to app, no echo
  181. replay_skip db    0            ; non-zero to skip subst for replays
  182. retbuf    db    130 dup (0)        ; \v(return) buffer
  183. vfile    db    65 dup (0)        ; \v(filespec) buffer
  184. maxdepth equ    15            ; depth of directory search
  185. fileio  fileiost maxdepth dup(<>)    ; find first/next support
  186. rfileptr dw    0            ; pointer to search dta structure
  187. rpathname db    64+1 dup (0)        ; search path
  188. rfilename db    8+3+1 dup (0)        ; search filename
  189.  
  190. filecnt    dw    0            ; count of files found
  191. findkind db    0 ; find kind (1=file,2=dir,plus 4=recurse,0=nothing)
  192. cmsflg    db    0            ; Non-zero when last char was a space
  193. cmkind    db    0            ; command kind request (cmkey etc)
  194. in_showprompt db 0            ; Non-zero when in showprompt
  195. cmdbuf    db    cmdblen dup (0)        ; Buffer for command parsing
  196.     db    0            ; safety for overflow
  197. rawbuf    db    cmdblen dup (0)        ; input, what user provided
  198.     db    0            ; safety for overflow
  199. read_source db    0            ; channel whence we read
  200. endedbackslash db 0
  201. in_reparse db    0
  202.     even
  203. cmdstk    dw    0            ; stack pointer at comand call time
  204. cmptab    dw    0            ; Address of present keyword table
  205. cmhlp    dw    0            ; Address of present help
  206. cmwptr    dw    0            ; Pointer for next char write
  207. cmrptr    dw    0            ; Pointer for next char read
  208. cmsiz    dw    0            ; Size info of user input
  209. cmsptr    dw    0            ; Place to save a pointer
  210. mcmprmp    dw    0            ; master prompt, string address
  211. mcmrprs    dd    0            ; master prompt, reparse address
  212. mcmostp    dw    0            ; master prompt, stack pointer
  213. temp    dw    0            ; temp (counts char/line so far)
  214. cmrawptr dw    0            ; non-substitution level, write ptr
  215.  
  216. ifndef    no_tcp
  217. valtab    db    1+46            ; table of values for \v(value)
  218. else
  219. valtab    db    1+46 - 2
  220. endif    ; no_tcp
  221.     mkeyw    'argc)',V_argc
  222.     mkeyw    'carrier)',V_carrier
  223.     mkeyw    'charset)',V_charset
  224.     mkeyw    'cmdfile)',V_cmdfile
  225.     mkeyw    'connection)',V_connection
  226.     mkeyw    'console)',V_console
  227.     mkeyw    'count)',V_count
  228.     mkeyw    'cmdlevel)',V_cmdlevel
  229.     mkeyw    'cps)',V_cps
  230.     mkeyw    'crc16)',V_crc16
  231.     mkeyw    'date)',V_date
  232.     mkeyw    'ndate)',V_ndate
  233.     mkeyw    'day)',V_day
  234.     mkeyw    'nday)',V_nday
  235.     mkeyw    'directory)',V_dir
  236.     mkeyw    'dosversion)',V_dosver
  237.     mkeyw    'disk)',V_disk
  238.     mkeyw    'errorlevel)',V_errlev
  239.     mkeyw    'filespec)',V_filespec
  240.     mkeyw    'fsize)',V_fsize
  241.     mkeyw    'inidir)',V_inidir
  242.     mkeyw    'inpath)',V_inpath
  243.     mkeyw    'input)',V_input
  244.     mkeyw    'instatus)',V_instatus
  245.     mkeyw    'keyboard)',V_kbd
  246.     mkeyw    'line)',V_port
  247.     mkeyw    'minput)',V_minput
  248.     mkeyw    'monitor)',V_monitor
  249.     mkeyw    'parity)',V_parity
  250.     mkeyw    'platform)',V_platform
  251.     mkeyw    'port)',V_port
  252.     mkeyw    'program)',V_program
  253.     mkeyw    'prompt)',V_prompt
  254.     mkeyw    'query)',V_query
  255.     mkeyw    'return)',V_return
  256. ifndef    no_tcp
  257.     mkeyw    'session)',V_session
  258.     mkeyw    'tcpip_status)',V_tcpip
  259. endif    ; no_tcp
  260.     mkeyw    'space)',V_space
  261.     mkeyw    'speed)',V_speed
  262.     mkeyw    'startup)',V_startup
  263.     mkeyw    'status)',V_status
  264.     mkeyw    'sysid)',V_sysid
  265.     mkeyw    'system)',V_system
  266.     mkeyw    'terminal)',V_terminal
  267.     mkeyw    'time)',V_time
  268.     mkeyw    'ntime)',V_ntime
  269.     mkeyw    'version)',V_version
  270.  
  271.  
  272. evaltab    db    32            ; \fverb(args)
  273.     mkeyw    'basename(',F_basename
  274.     mkeyw    'character(',F_char
  275.     mkeyw    'checksum(',F_chksum
  276.     mkeyw    'code(',F_code
  277.     mkeyw    'contents(',F_contents
  278.     mkeyw    'date(',F_date
  279.     mkeyw    'definition(',F_definition
  280.     mkeyw    'directories(',F_directories
  281.     mkeyw    'eval(',F_eval
  282.     mkeyw    'files(',F_files
  283.     mkeyw    'index(',F_index
  284.     mkeyw    'ipaddr(',F_ipaddr
  285.     mkeyw    'length(',F_length
  286.     mkeyw    'literal(',F_literal
  287.     mkeyw    'lower(',F_lower
  288.     mkeyw    'lpad(',F_lpad
  289.     mkeyw    'maximum(',F_maximum
  290.     mkeyw    'minimum(',F_minimum
  291.     mkeyw    'nextfile(',F_nextfile
  292.     mkeyw    'rindex(',F_rindex
  293.     mkeyw    'repeat(',F_repeat
  294.     mkeyw    'replace(',F_replace
  295.     mkeyw    'reverse(',F_reverse
  296.     mkeyw    'rdirectories(',F_rdirectories
  297.     mkeyw    'rfiles(',F_rfiles
  298.     mkeyw    'right(',F_right
  299.     mkeyw    'rpad(',F_rpad
  300.     mkeyw    'size(',F_size
  301.     mkeyw    'substr(',F_substr
  302.     mkeyw    'tod2secs(',F_tod2secs
  303.     mkeyw    'verify(',F_verify
  304.     mkeyw    'upper(',F_upper
  305. ; not implemented in MS-DOS Kermit
  306. ;    mkeyw    'execute(',F_exec
  307.  
  308. envtab    db    1                     ; \$(..) Environment table
  309.     mkeyw    'An Environment variable)',0    ; reserve 0 in valtab
  310.  
  311. numtab  db      1                               ; \number help table
  312.         mkeyw   'a number such as \123',0
  313.  
  314.                         ; table of Parity strings
  315. parmsgtab dw    parevnmsg,parmrkmsg,parnonmsg,paroddmsg,parspcmsg
  316. parevnmsg db    'EVEN',0
  317. parmrkmsg db    'MARK',0
  318. parnonmsg db    'NONE',0
  319. paroddmsg db    'ODD',0
  320. parspcmsg db    'SPACE',0
  321.  
  322. onoffmsgtab dw    offmsg,onmsg        ; on/off table, asciiz
  323. offmsg    db    'OFF',0
  324. onmsg    db    'ON',0
  325.  
  326. ansiword db    'ANSI',0        ; \v(console) ASCIIZ strings
  327. noneword db    'NONE',0
  328. colormsg db    'COLOR',0        ; \v(monitor) ASCIIZ strings
  329. monomsg    db    'MONO',0
  330. connmsg    db    'local',0        ; \v(connection) state
  331.  
  332. valtmp    dw    0
  333. numtmp    dw    0
  334.  
  335.     even
  336. envadr    dd    0            ; seg:offset of a string in Environemt
  337. envlen    dw    0            ; length of envadr's string
  338.  
  339.     even                ; Control sequence storage area
  340. maxparam equ    16            ; number of ESC and DCS Parameters
  341. maxinter equ    16            ; number of ESC and DCS Intermediates
  342.  
  343. savepoff label    word            ; Start per session save area
  344. parstate dw    0            ; parser state, init to startup
  345. pardone dw    0            ; where to jmp after Final char seen
  346. parfail    dw    0            ; where to jmp if parser fails
  347. nparam    dw    0            ; number of received Parameters
  348. param    dw    maxparam dup (0)    ; Parameters for ESC
  349. lparam    db    0            ; a single letter Parameter for ESC
  350. ninter    dw    0            ; number of received Intermediates
  351. inter    db    maxinter dup (0),0    ; Intermediates for ESC, + guard 
  352. saveplen dw    ($-savepoff)
  353. ifelse    db    0        ; non-zero if last IF statement failed
  354. oldifelse db    0        ; copy of ifelse from previous command
  355. month    db    'Jan ','Feb ','Mar ','Apr ','May ','Jun '
  356.     db    'Jul ','Aug ','Sep ','Oct ','Nov ','Dec '
  357. day    db    'Sun','Mon','Tue','Wed','Thu','Fri','Sat'
  358. data    ends
  359.  
  360. data1    segment
  361.     db    0            ; so error msg has non-zero offset
  362. cmin00  db      ' Press ENTER to execute command$'
  363. f1hlp    db    ' number$'
  364. f2hlp    db    ' char$'
  365. f3hlp    db    ' variable or macro name$'
  366. f8hlp    db    ' pattern-text, string, start-position$'
  367. f9hlp    db    ' text$'
  368. f12hlp    db    ' text, pad-length, pad-char$'
  369. f15hlp    db    ' no argument$'
  370. f16hlp    db    ' repeat-text, repeat-count$'
  371. f18hlp    db    ' text, right-most count$'
  372. f20hlp    db    ' text, start-position, substring len$'
  373. f22hlp    db    ' filename$'
  374. f24hlp    db    ' source, pattern, replacement$'
  375. f25hlp    db    ' arithmetic expression$'
  376. f30hlp    db    ' string$'
  377. n1hlp    db    ' digits of a number whose value fits into one byte$'
  378. data1    ends
  379.  
  380. ifndef    no_terminal
  381. ifndef    no_tcp
  382. _TEXT    segment
  383.     extrn    cpatch:far
  384. _TEXT    ends
  385. endif    ; no_tcp
  386. endif    ; no_terminal
  387.  
  388. code1    segment
  389.     assume     cs:code1
  390.     extrn    shovarcps:near, dskspace:far, fparse:far
  391.     extrn    strlen:far, prtscr:far, strcpy:far, prtasz:far, strcat:far
  392.     extrn    dec2di:far, decout:far, malloc:far, domath:far
  393.         extrn   atoi:far, takrd:far, buflog:far, tod2secs:far
  394.  
  395. cspb1    equ    this byte
  396.     db    (256-($-cspb1)) dup (0)    ; code1 segment patch buffer
  397.                 ; end of Patch area
  398. code1    ends
  399.  
  400. code    segment
  401.     extrn    ctlu:near, cmblnk:near, locate:near
  402.     extrn    takclos:far, docom:near, getenv:near
  403.     extrn    getbaud:near, lnout:near, takopen_sub:far
  404.     extrn    takopen_macro:far
  405.  
  406.     assume    cs:code, ds:data, es:nothing
  407.  
  408.                 ; Patch area. Must be first in MSK's Code Seg
  409. cspb    equ    this byte
  410.     dw    seg code1
  411.     dw    seg code2
  412.     dw    seg data
  413.     dw    seg data1
  414.     dw    seg _TEXT
  415.     dw    seg _DATA
  416.  
  417. ifndef    no_terminal
  418. ifndef    no_tcp
  419.     dw    offset cpatch
  420. endif    ; no_tcp
  421. endif    ; no_terminal
  422.     db    (256-($-cspb)) dup (0)    ; code segment patch buffer
  423.                 ; end of Patch area
  424.  
  425. fctlu    proc    far        ; FAR callable versions of items in seg code
  426.     call    ctlu        ;  for calling from code segment code1 below
  427.     ret
  428. fctlu    endp
  429. fcmblnk    proc    far
  430.     call    cmblnk
  431.     ret
  432. fcmblnk    endp
  433. fgetbaud proc    far
  434.     call    getbaud
  435.     ret
  436. fgetbaud endp
  437. fgetenv    proc    far
  438.     call    getenv
  439.     ret
  440. fgetenv    endp
  441. flocate    proc    far
  442.     call    locate
  443.     ret
  444. flocate    endp
  445.  
  446. fdec2di    proc    far
  447.     push    cx
  448.     push    ds
  449.     mov    cx,es
  450.     mov    ds,cx
  451.     call    dec2di
  452.     pop    ds
  453.     pop    cx
  454.     ret
  455. fdec2di    endp
  456. ftakclos proc    far
  457.     call    takclos
  458.     ret
  459. ftakclos endp
  460.  
  461. nvaltoa    proc    near
  462.     push    es
  463.     mov    ax,ds
  464.     mov    es,ax
  465.     call    fvaltoa
  466.     pop    es
  467.     ret
  468. nvaltoa    endp
  469. flnout    proc    far
  470.     call    lnout
  471.     ret
  472. flnout    endp
  473. fdskspace proc    far
  474.     call    dskspace
  475.     ret
  476. fdskspace endp
  477.  
  478. ;       This routine parses the specified function in AH. Any additional
  479. ;       information is in DX and BX.
  480. ;       Returns carry clear on success and carry set on failure
  481.  
  482. COMND    PROC NEAR
  483.     mov    cmdstk,sp        ; save stack ptr for longjmp exit
  484.     mov    bracecnt,0        ; curly brace counter
  485.     cmp    ah,cmeol        ; Parse a confirm?
  486.     jne    cm2            ; nz = no
  487.     call    cmcfrm            ; get a Carriage Return end of line
  488.     ret
  489. cm2:    mov    cmkind,ah        ; remember for {} line continuation
  490.     cmp    ah,cmkey        ; Parse a keyword?
  491.     jne    cm3            ; ne = no
  492.     xor    al,al            ; get a zero/clear
  493.     xchg    al,ifelse        ; get current ifelse state
  494.     mov    oldifelse,al        ; remember here (one cmd delay)
  495.     call    cmkeyw            ; get keyword
  496.     ret
  497. cm3:    cmp    ah,cmline        ; parse line of text
  498.     jne    cm4
  499.     call    cmtxt
  500.     ret
  501. cm4:    cmp    ah,cmword        ; parse arbitrary word
  502.     jne    cm5
  503.     call    cmtxt
  504.     ret
  505. cm5:    mov    ah,prstr        ; else give error
  506.     mov    dx,offset cmer00    ; "?Program internal error"
  507.     int    dos
  508.     jmp    prserr            ; reparse
  509.                     ; Control-C exit path (far to near)
  510. cmexit    label    far
  511.     mov    sp,cmdstk        ; restore command entry stack pointer
  512.     stc
  513.     retn                ;  and fail immediately (a longjmp)
  514.  
  515. COMND    ENDP
  516. code    ends
  517.  
  518. code1    segment
  519.     assume    cs:code1
  520.  
  521. ; This routine parses a keyword from the table pointed at by DX, help text
  522. ; point to by BX. Format of the table is as follows (use macro mkeyw):
  523. ;    addr:    db    N      ; Where N is the # of entries in the table
  524. ;        dw    M      ; M is the size of the keyword
  525. ;        db    'string'  ; String is the keyword
  526. ;        dw    value      ; Value is data to be returned
  527. ; Keywords may be in any order and in mixed case.
  528. ; Return is carry clear for success and carry set for failure.
  529.  
  530. ; cmptab: pointer to keyword table (supplied by caller)
  531. ; cmhlp: pointer to help message (supplied by caller)
  532. ; cmsptr: pointer to current user word text
  533. ; cmsiz: length of user text, excluding terminator
  534. ; comand.cmcr: 0 = empty lines not allowed, 1 = empty lines allowed
  535. ; comand.cmwhite: non-zero allows leading whitespace for cmline and cmword,
  536. ;                 reset automatically at end of call
  537. ; cmwptr: buffer write pointer to next free byte
  538. ; cmrptr: buffer read pointer for next free byte
  539. ; comand.cmper:    0 to do \%x substitution. Set to 0 at end of call
  540. ; comand.impdo: non-zero permits keyword failure to retry as DO command, reset
  541. ;         automatically at time of failure.
  542. cmkeyw    proc    far
  543.     mov    cmsiz,0            ; user word length
  544.     mov    ax,cmrptr        ; get command reading pointer
  545.     mov    cmsptr,ax        ; set pointer for start of user word
  546.     mov    cmhlp,bx        ; save the help pointer
  547.         mov    cmptab,dx        ; save the beginning of keyword table
  548.     mov    bx,dx
  549.     cmp    byte ptr[bx],0        ; get number of entries in table
  550.     jne    cmky1
  551.     jmp    cmky7            ; e = no keywords to check, error
  552. cmky1:    mov    cmsflg,0ffh        ; skip leading spaces/tabs
  553.     call    cmgtch            ; get char from the user into ah
  554.     jc    cmky3            ; c = terminator
  555.     mov    dx,cmrptr        ; next byte to read
  556.     dec    dx            ; where we just read a char
  557.     mov    cmsptr,dx        ; remember start of keyword
  558.     inc    cmsiz            ; start counting user chars
  559. cmky2:    call    cmgtch            ; read until terminator
  560.     jc    cmky3            ; c = terminator
  561.     inc    cmsiz            ; count user chars
  562.     jmp    short cmky2        ; no terminator yet
  563.  
  564. cmky3:    cmp    ah,'?'                  ; need help?
  565.     jne    cmky4            ; ne = no
  566.     call    cmkyhlp            ; display help
  567.     jmp    repars
  568. cmky4:    cmp    ah,escape        ; escape?
  569.     jne    cmky6            ; ne = no
  570.     call    cmkyesc            ; process escape
  571.     jc    cmky5            ; c = failure (no unique keyword yet)
  572.     mov    comand.cmper,0        ; reset to variable recognition
  573.     mov    comand.cmkeep,0
  574.     mov    comand.impdo,0        ; clear flag to prevent loops
  575.     mov    comand.cmquiet,0    ; permit echoing again
  576.     mov    comand.cmcnvkind,cmcnv_none ; default is no conversion
  577.     clc
  578.     ret                ; return successfully to user
  579.  
  580. cmky5:    cmp    cmsiz,0            ; started a word yet?
  581.     je    cmky1            ; e = no, ignore escape, keep looking
  582.     call    cmkyhlp            ; display help
  583.     jmp    repars
  584.  
  585. cmky6:    cmp    cmsiz,0            ; length of user's text, empty?
  586.     je    cmky7            ; e = yes, parse error
  587.     push    bx
  588.     mov    bx,cmsptr        ; point at first user character
  589.     cmp    byte ptr[bx],':'    ; start of a label?
  590.     pop    bx
  591.     jne    cmky6a            ; ne = no, return success
  592.     mov    cmsiz,1            ; say just one byte
  593. cmky6a:    call    getkw            ; get unique kw, point to it with bx
  594.     jc    cmky8            ; c = not found
  595.     add    bx,[bx]            ; add length of keyword text (CNT)
  596.     add    bx,2            ; point at value field
  597.     mov    bx,[bx]            ; bx = return value following keyword
  598.     call    optionclr        ; clear parser options
  599.     mov    errflag,0
  600.     clc
  601.     ret                ; return successfully
  602.                     ; all other terminators come here
  603. cmky7:    cmp    cmsiz,0            ; empty table or empty user's text?
  604.     jne    cmky8            ; ne = no
  605.     cmp    comand.cmcr,0        ; empty lines allowed?
  606.     jne    cmky10            ; ne = yes, do not complain
  607.     push    dx
  608.     mov    ah,prstr
  609.     mov    dx,offset cmer01    ; command word expected
  610.     int    dos
  611.     pop    dx
  612.     xor    al,al
  613.     mov    comand.cmquiet,al    ; permit echoing again
  614.     mov    comand.impdo,al        ; clear flag to prevent loops
  615.     stc                ; failure
  616.     ret
  617.  
  618. cmky8:    cmp    comand.impdo,0        ; failed here, ok to try Macro table?
  619.     je    cmky8a            ; e = no, use regular exit path
  620.     mov    comand.impdo,0        ; yes, but clear flag to prevent loops
  621.     mov    cmrptr,offset cmdbuf    ; reinit read pointer
  622.     mov    comand.cmquiet,1    ; suppress echoing of same keyword
  623.     mov    bx,offset docom        ; return DO as "found" keyword
  624.     clc
  625.     ret                ; return success to invoke DO
  626.  
  627. cmky8a:    cmp    comand.cmswitch,0    ; looking for switch keyword?
  628.     je    cmky8b            ; e = no
  629.     mov    comand.cmswitch,0
  630.     mov    comand.impdo,0        ; yes, but clear flag to prevent loops
  631.     mov    ax,cmsptr        ; where keyword started in buffer
  632.     mov    cmrptr,ax        ; reread it again later
  633.     mov    comand.cmquiet,1    ; suppress echoing of same keyword
  634.     stc                ; return with no complaint
  635.     ret
  636.  
  637. cmky8b:    mov    errflag,1        ; say already doing error recovery
  638.     or    kstatus,ksgen        ; global command status, failure
  639.     mov    comand.cmquiet,0    ; permit echoing again
  640.     call    isdev            ; reading pretyped lines?
  641.     jnc    cmky9            ; nc = yes, consume rest of line
  642.     cmp    taklev,0        ; in a Take file?
  643.     jne    cmky9            ; ne = yes
  644.     call    cmskw            ; display offending keyword
  645.     mov    comand.impdo,0        ; clear flag to prevent loops
  646.     dec    cmrptr            ; interactive, backup to terminator
  647.     mov    bx,cmrptr        ; look at it
  648.     cmp    byte ptr [bx],' '    ; got here on space terminator?
  649.     jne    cmky10            ; ne = no, (cr,lf,ff) exit failure
  650.     mov    ah,prstr        ; start a fresh line
  651.     mov    dx,offset crlf
  652.     int    dos
  653.     call    bufdel            ; cut back buffer to last good char
  654.     jmp    repars            ; reparse interactive lines
  655.  
  656. cmky9:    call    cmcfrm            ; get formal end of command line
  657.                     ;  to maintain illusion of typeahead
  658.                     ;  and let user backspace to correct
  659.                     ;  mistakes (we reparse everything)
  660.     call    cmskw            ; display offending keyword
  661. cmky10:    mov    comand.cmquiet,0    ; permit echoing again
  662.     stc                ; say failure
  663.     ret
  664. cmkeyw    endp
  665.  
  666. ;;;;;; start support routines for keyword parsing.
  667.  
  668. cmkyesc    proc    near            ; deal with escape terminator
  669.     push    cmrptr            ; points at ESC
  670.     pop    cmwptr            ; pointed one byte beyond ESC
  671.     cmp    cmsiz,0         ; user word length, empty?
  672.     jne    cmkye2            ; ne = have user text, else complain
  673. cmkye1:    call    esceoc            ; do normal escape end-of-command
  674.     stc                ; say failure to fill out word
  675.     ret
  676.                     ; add unique keyword to buffer
  677. cmkye2:    call    getkw            ; is there a matching keyword?
  678.     jc    cmkye1            ; c = ambiguous or not found
  679.     push    bx            ; unique, bx points to structure
  680.     push    si
  681.     mov    cx,[bx]            ; length of keyword
  682.     add    bx,2            ; point to first letter
  683.     dec    cmrawptr        ; overwrite ESC
  684.     mov    si,cmrawptr        ; where raw writes go
  685.     mov    dx,cmsiz        ; length of user word
  686.     add    bx,dx            ; add chars known so far
  687.     sub    cx,dx            ; calculate number yet to add
  688.     jcxz    cmkye4            ; z = none
  689. cmkye3:    mov    al,[bx]            ; get a keyword letter
  690.     inc    bx
  691.     call    tolowr            ; lowercase
  692.     mov    [si],al            ; store it
  693.     inc    si
  694.     loop    cmkye3            ; do all new chars
  695. cmkye4:    mov    word ptr [si],'  '    ; add trailing space, clear old next
  696.     inc    si
  697.     mov    cmrawptr,si
  698.     pop    si
  699.     pop    bx            ; bx = keyword structure
  700.     inc    cmrawptr        ; bufdel backs up one char
  701.     jmp    bufdel
  702. cmkyesc    endp
  703.  
  704. esceoc    proc    near            ; do normal escape end-of-command
  705.     push    ax
  706.     push    dx
  707.     mov    ah,conout        ; ring the bell
  708.     mov    dl,bell
  709.     int    dos
  710.     pop    dx
  711.     pop    ax
  712.     call    bufreset        ; reset buffer
  713.     stc                ; say error condition
  714.     ret
  715. esceoc    endp
  716.  
  717. ; Help. Question mark entered by user.  Display all the keywords that match
  718. ; user text. If text is null then use external help if available; otherwise,
  719. ; display all keywords in the table. Removes question mark from buffer and
  720. ; invokes reparse of command line to-date. User word starts at cmsptr and
  721. ; is cmsiz bytes long.
  722. cmkyhlp    proc    near
  723.     xor    cx,cx            ; clear number of keyword (none yet)
  724.     cmp    cmsiz,0            ; user text given?
  725.     jne    cmkyh1            ; ne = yes, use matching keywords
  726.     cmp    cmhlp,0            ; external help given?
  727.     jne    cmkyh6            ; yes, use it instead of full table
  728. cmkyh1:    mov    temp,0            ; count # chars printed on this line
  729.     mov    bx,cmptab        ; beginning of kw table
  730.     mov    ch,[bx]            ; length of table
  731.     xor    cl,cl            ; no keywords or help displayed yet
  732.     inc    bx            ; point at CNT field
  733. cmkyh2:    cmp    cmsiz,0            ; length of user word
  734.     je    cmkyh3            ; e = null, use full table
  735.     call    cmpwrd            ; compare keyword with user word
  736.     jc    cmkyh5            ; c = no match, get another keyword
  737. cmkyh3:    mov    ax,[bx]            ; length of table keyword
  738.     add    byte ptr temp,al    ; count chars printed so far
  739.     cmp    temp,76            ; will this take us beyond column 78?
  740.     jbe    cmkyh4            ; be = no, line has more room
  741.     mov    byte ptr temp,al    ; reset the count
  742.     mov    ah,prstr
  743.     mov    dx,offset crlf        ; break the line
  744.     int    dos
  745. cmkyh4:    or    cl,cl            ; any keywords found yet?
  746.     jnz    cmkyh4a            ; nz = yes
  747.     mov    dx,offset cmin01    ; start with One of the following: msg
  748.     mov    ah,prstr
  749.     int    dos
  750.     inc    cl            ; say one keyword has been found
  751. cmkyh4a:mov    dl,spc            ; put two spaces before each keyword
  752.     mov    ah,conout
  753.     int    dos
  754.     int    dos
  755.     add    temp,2            ; count output chars
  756.     mov    di,bx            ; get current keyword structure
  757.     add    di,2            ; text part
  758.     push    cx
  759.     mov    cx,[bx]            ; string length to cx, offset to di
  760.     call    prtscr            ; display counted string
  761.     pop    cx
  762. cmkyh5:    dec    ch            ; are we at end of table?
  763.     jle    cmkyh7            ; le = yes, quit now
  764.     add    bx,[bx]            ; next keyword, add CNT chars to bx
  765.     add    bx,4            ; skip CNT and 16 bit value
  766.     jmp    cmkyh2            ; go examine this keyword
  767.  
  768. cmkyh6:    mov    si,cmhlp        ; external help text in seg data1
  769.     xor    bx,bx            ; line counter
  770.     push    es
  771.     mov    ax,seg data1        ; all help text is in data1
  772.     mov    es,ax
  773.     cld
  774. cmkyh10:mov    al,es:[si]        ; read a help msg byte
  775.     inc    si
  776.     cmp    al,'$'            ; end of message?
  777.     je    cmkyh14            ; e = yes, stop
  778. cmkyh11:mov    ah,conout
  779.     mov    dl,al
  780.     int    dos            ; display byte
  781.     cmp    dl,LF            ; line break?
  782.     jne    cmkyh10            ; ne = no
  783.     inc    bl            ; count line
  784.     cmp    bl,dos_bottom        ; (24) time for a more msg?
  785.     jbe    cmkyh10            ; be = not yet
  786.     xor    bl,bl            ; reset line count
  787.     call    iseof            ; are we at EOF, such as from disk?
  788.     jc    cmkyh10            ; c = yes, ignore more msg
  789.     mov    ah,prstr
  790.     mov    dx,offset moremsg    ; "... more..." msg
  791.     int    dos
  792. cmkyh13:mov    ah,coninq        ; read the char from file, not device
  793.     int    dos
  794.     cmp    al,3            ; a ^C?
  795.     je    short cmkyh14        ; e = yes, stop the display
  796.     push    bx            ; save line counter
  797.     push    es            ; and read pointer
  798.     push    si
  799.     call    fctlu            ; clear display's line, reuse it
  800.     pop    si
  801.     pop    es
  802.     pop    bx
  803.     jmp    short cmkyh10        ; continue
  804. cmkyh14:pop    es
  805.     inc    cl            ; say gave help already
  806.  
  807. cmkyh7:    or    cl,cl            ; found any keywords?
  808.     jnz    cmkyh9            ; nz = yes
  809.     mov    cx,cmsiz        ; length of word
  810.     or    cx,cx
  811.     jg    cmkyh8            ; g = something to show
  812.     push    dx
  813.     mov    ah,prstr
  814.     mov    dx,offset cmer01    ; command word expected
  815.     int    dos
  816.     pop    dx
  817.     jmp    prserr
  818. cmkyh8:    mov    kwstat,0        ; set keyword not-found status
  819.     call    cmskw            ; display offending keyword
  820. cmkyh9:    mov    ah,prstr        ; start a fresh line
  821.     mov    dx,offset crlf
  822.     int    dos
  823.     call    bufdel            ; unwrite the "?" (cmrptr is there)
  824.     ret
  825. cmkyhlp    endp
  826.  
  827. ; See if keyword is ambiguous or not from what the user has typed in.
  828. ; Return carry set if word is ambiguous or not found, carry clear otherwise.
  829. ; Uses table pointed at by cmptab, user text pointed at by cmsptr and length
  830. ; in cmsiz.
  831. cmambg    proc    near
  832.     push    bx
  833.     push    cx
  834.     push    dx
  835.     xor    dl,dl            ; count keyword matches so far
  836.     mov    bx,cmptab        ; look at start of keyword table
  837.     mov    cl,[bx]            ; get number of entries in table
  838.     xor    ch,ch            ; use cx as a counter
  839.     jcxz    cmamb8            ; z = no table so always ambiguous
  840.     inc    bx            ; look at CNT byte of keyword
  841. cmamb4:    call    cmpwrd            ; user vs table words, same?
  842.     jc    cmamb6            ; c = no match
  843.     inc    dl            ; count this as a match
  844.     cmp    dl,1            ; more than one match?
  845.     ja    cmamb8            ; a = yes, quit now
  846. cmamb6:    add    bx,[bx]            ; add CNT chars to bx
  847.     add    bx,4            ; skip CNT and 16 bit value
  848.     loop    cmamb4            ; do rest of keyword table
  849.     cmp    dl,1            ; how many matches were found?
  850.     jne    cmamb8            ; ne = none or more than 1: ambiguous
  851.     pop    dx            ; restore main registers
  852.     pop    cx
  853.     pop    bx
  854.     clc
  855.     ret                ; ret = not ambiguous
  856. cmamb8:    pop    dx            ; restore main registers
  857.     pop    cx
  858.     pop    bx
  859.     stc
  860.     ret                ; return ambiguous or not found
  861. cmambg    endp
  862.  
  863. ; Compare user text with keyword, abbreviations are considered a match.
  864. ; Enter with bx pointing at keyword table CNT field for a keyword.
  865. ; Return carry clear if they match, set if they do not match. User text
  866. ; pointed at by cmsptr and length is in cmsiz.
  867. ; Registers preserved.
  868.  
  869. cmpwrd    proc    near
  870.     push    cx
  871.     mov    cx,cmsiz        ; length of user's text
  872.     jcxz    cmpwrd2            ; z: null user word matches no keyword
  873.     cmp    cx,[bx]            ; user's text longer than keyword?
  874.     ja    cmpwrd2            ; a = yes, no match
  875.     push    ax
  876.     push    bx
  877.     push    si
  878.     add    bx,2                ; point at table's keyword text
  879.     mov    si,cmsptr        ; buffer ptr to user input
  880.     cld
  881. cmpwrd1:lodsb                ; user text
  882.     mov    ah,[bx]            ; keyword text
  883.     inc    bx            ; next keyword letter
  884.     call    tolowr            ; force lower case on both chars
  885.     cmp    ah,al            ; same?
  886.     loope    cmpwrd1            ; e = same so far
  887.     pop    si
  888.     pop    bx
  889.     pop    ax
  890.     jne    cmpwrd2            ; ne = mismatch
  891.     pop    cx            ; recover keyword counter
  892.     clc                ; they match
  893.     ret
  894. cmpwrd2:pop    cx            ; recover keyword counter
  895.     stc                ; they do not match
  896.     ret
  897. cmpwrd    endp
  898.  
  899. ; Get pointer to keyword structure using user text. Uses keyword table
  900. ; pointed at by cmptab and cmsiz holding length of user's keyword (cmpwrd
  901. ; needs cmsptr pointing at user's keyword and length of cmsiz).
  902. ; Structure pointer returned in BX.
  903. ; Return carry clear for success and carry set for failure. Modifies BX.
  904. getkw    proc    near
  905.     push    cx
  906.     mov    kwstat,0        ; keyword status, set to not-found
  907.     cmp    cmsiz,0            ; length of user word, empty?
  908.     je    getkw3            ; e = yes, fail
  909.     mov    bx,cmptab        ; table of keywords
  910.     mov    cl,[bx]            ; number of keywords in table
  911.     xor    ch,ch
  912.     jcxz    getkw3            ; z = none, fail
  913.     inc    bx            ; point to first
  914. getkw1:    call    cmpwrd            ; compare user vs table words
  915.     jc    getkw2            ; c = failed to match word, try next
  916.     mov    kwstat,1        ; say found one keyword, maybe more
  917.     push    dx
  918.     mov    dx,cmsiz        ; users word length
  919.     cmp    [bx],dx            ; same length (end of keyword)?
  920.     pop    dx
  921.     je    getkw4            ; e = yes, exact match. Done
  922.     call    cmambg            ; ambiguous?
  923.     jnc    getkw4            ; nc = unique, done, return with bx
  924.     mov    kwstat,2        ; say more than one such keyword
  925. getkw2:    add    bx,[bx]            ; next keyword, add CNT chars to bx
  926.     add    bx,4            ; skip CNT and 16 bit value
  927.     loop    getkw1            ; do all, exhaustion = failure
  928. getkw3:    pop    cx
  929.     stc                ; return failure
  930.     ret
  931. getkw4:    pop    cx
  932.     clc                ; return success
  933.     ret
  934. getkw    endp
  935.  
  936. ; show offending keyword message. Cmsptr points to user word,
  937. ; cmsiz has length. Modifies AX, CX, and DX.
  938. cmskw    proc    near
  939.     cmp    comand.cmquiet,0    ; Quiet mode?
  940.     je    cmskw0            ; e = no, regular mode
  941.     ret                ; else say nothing
  942. cmskw0:    mov    ah,prstr        ; not one of the above terminators
  943.     mov    dx,offset cmer02    ; '?Word "'
  944.     int    dos
  945.     mov    ah,conout
  946.     mov    cx,cmsiz        ; length of word
  947.     jcxz    cmskw3            ; z = null
  948.     mov    ah,conout
  949.     push    si
  950.     mov    si,cmsptr        ; point to word
  951.     cld
  952. cmskw1:    lodsb
  953.     cmp    al,' '            ; control code?
  954.     jae    cmskw2            ; ae = no
  955.     push    ax
  956.     mov    dl,5eh            ; caret
  957.     int    dos
  958.     pop    ax
  959.     add    al,'A'-1        ; plus ascii bias
  960. cmskw2:    mov    dl,al            ; display chars in word
  961.     int    dos
  962.     loop    cmskw1
  963.     pop    si
  964. cmskw3:    mov    dx,offset cmer03    ; '" not usable here.'
  965.     cmp    kwstat,1        ; kywd status from getkw, not found?
  966.     jb    cmskw4            ; b = not found, a = ambiguous
  967.     mov    dx,offset cmer04    ; '" ambiguous'
  968. cmskw4:    mov    ah,prstr
  969.         int    dos
  970.     ret
  971. cmskw    endp
  972. ;;;;;;;;;; end of support routines for keyword parsing.
  973.  
  974. ; CMLINE: Parse    arbitrary text up to a CR.
  975. ; CMWORD: Parse text up to first trailing space, or if starts with ()
  976. ;  then consume the line.
  977. ; Enter with BX = pointer to output buffer
  978. ; DX pointing to help text. Produces asciiz string. Return updated pointer in
  979. ; BX and output size in AX. Leading spaces are omitted unless comand.cmwhite
  980. ; is non-zero (cleared upon exit). It does not need to be followed by the
  981. ; usual call to confirm the line. Byte comand.cmblen can be used to specify
  982. ; the length of the caller's buffer; cleared to zero by this command to
  983. ; imply a length of 127 bytes (default) and if zero at startup use 127 bytes.
  984. ; If the line starts with an opening curly brace, then physical lines are
  985. ; automatically continued until the closing curly brace is obtained.
  986. ; Continuation breaks are a comma in the data stream and a CR/LF to the
  987. ; visual screen. Material after the closing brace is discarded.
  988. ;
  989. ; Lines and words starting on a curly brace and ending on a matching
  990. ; curly brace plus optional whitespace have the trailing whitespace
  991. ; omitted and both outer braces removed.
  992. cmtxt    proc    far
  993.     mov    cmptab,bx        ; save pointer to data buffer
  994.     xor    ax,ax
  995.     mov    word ptr [bx],ax    ; clear result buffer
  996.     mov    cmhlp,dx        ; save the help message
  997.     mov    cmsiz,ax        ; init the char count
  998.     mov    parencnt,al        ; clear count of parentheses
  999.     cmp    comand.cmblen,ax    ; length of user's buffer given?
  1000.     jne    cmtxt1            ; ne = yes
  1001.     mov    comand.cmblen,127    ; else set 127 byte limit plus null
  1002. cmtxt1:    cmp    comand.cmwhite,al    ; allow leading whitespace?
  1003.     jne    cmtxt2            ; ne = yes
  1004.     mov    cmsflg,0ffh        ; omit leading space
  1005. cmtxt2:    call    cmgtch            ; get a char
  1006.     jc    cmtxt3            ; c = terminator 
  1007.     jmp    cmtxt10            ; put char into the buffer
  1008.  
  1009. cmtxt3:    cmp    ah,' '            ; space terminator?
  1010.     jne    cmtxt4            ; ne = no
  1011.     cmp    cmkind,cmline        ; parsing lines?
  1012.     je    cmtxt10            ; e = yes, put space in the buffer
  1013.     mov    bx,cmptab        ; words, check on () delimiters
  1014.     sub    bx,cmsiz
  1015.     cmp    byte ptr [bx],'('    ; started word with paren?
  1016.     jne    cmtxt6            ; ne = no, it's a terminator
  1017.     cmp    parencnt,0        ; outside parens?
  1018.     jne    cmtxt10            ; ne = no, use as data inside (..)
  1019.     jmp    cmtxt6            ; space is terminator
  1020.  
  1021. cmtxt4:    cmp    ah,escape        ; escape?
  1022.     jne    cmtxt5            ; ne = no
  1023.     call    esceoc            ; do normal escape end-of-command
  1024.     jmp    short cmtxt1        ; try again
  1025.  
  1026. cmtxt5:    cmp    ah,'?'            ; asking a question?
  1027.     je    cmtxt8            ; e = yes
  1028.     cmp    ah,CR            ; bare CR?
  1029.     je    cmtxt6            ; e = yes, always a terminator
  1030.     cmp    cmkind,cmline        ; reading a line?
  1031.     je    cmtxt10            ; e = yes, other terms go into buffer
  1032.                     ; else terminators terminate words
  1033.  
  1034. cmtxt6:    mov    bx,cmptab        ; pointer into destination array
  1035.     mov    byte ptr[bx],0        ; put null terminator into the buffer
  1036.     xchg    ax,cmsiz        ; return count in AX
  1037.     or    ax,ax            ; empty?
  1038.     jz    cmtxt7a            ; z = yes
  1039.  
  1040. cmtxt7:    push    si            ; remove terminating curly braces
  1041.     mov    si,cmptab        ; where next output byte goes
  1042.     sub    si,ax            ; minus read, equals start of buffer
  1043.     mov    cx,ax            ; count to cx for unbrace
  1044.     call    unbrace            ; outer curly brace remover
  1045.     mov    ax,cx            ; returned length back to AX
  1046.     mov    bx,si
  1047.     add    bx,ax            ; bx points to null terminator
  1048.     pop    si
  1049. cmtxt7a:call    optionclr        ; clear parser options
  1050.     cmp    cmkind,cmline        ; lines?
  1051.     jne    cmtxt7f            ; ne = no, words
  1052.     call    rprompt            ; restore master prompt level
  1053. cmtxt7f:clc
  1054.     ret
  1055.  
  1056. cmtxt8:    inc    cmrptr            ; count the ?
  1057.     cmp    cmsiz,0            ; Is "?" first char?
  1058.     jne    cmtxt10            ; ne = no, just add to buffer
  1059.     dec    cmrptr
  1060.     cmp    cmhlp,0            ; external help given?
  1061.     jne    cmtxt9            ; ne = yes
  1062.     mov    cmhlp,offset cmin00    ; confirm with c/r msg
  1063. cmtxt9:    push    cmsiz
  1064.     mov    cmsiz,0            ; so we do not use keyword table
  1065.     call    cmkyhlp            ; use our help message
  1066.     pop    cmsiz
  1067.     jmp    cmtxt2
  1068.  
  1069. cmtxt10:inc    cmsiz            ; increment the output count
  1070.     mov    bx,cmptab        ; pointer into destination array
  1071.     mov    [bx],ah            ; put char into the buffer
  1072.     cmp    cmkind,cmword        ; word?
  1073.     jne    cmtxt12            ; ne = no, line
  1074.     cmp    ah,'('            ; opening paren?
  1075.     jne    cmtxt11            ; ne = no
  1076.     inc    parencnt        ; count up parens
  1077.     jmp    short cmtxt12
  1078. cmtxt11:cmp    ah,')'            ; closing paren?
  1079.     jne    cmtxt12            ; ne = no
  1080.     sub    parencnt,1
  1081.     jns    cmtxt12            ; ns = no underflow
  1082.     mov    parencnt,1        ; don't underflow
  1083. cmtxt12:inc    bx
  1084.     mov    cmptab,bx
  1085.     mov    cx,cmsiz        ; length of command so far
  1086.     cmp    cx,4            ; got four chars?
  1087.     jne    cmtxt12a        ; ne = no
  1088.     cmp    comand.cmarray,0    ; worry about \&<char> as destination?
  1089.     je    cmtxt12a        ; e = no
  1090.     cmp    word ptr [bx-4],'&\'    ; starts with array indicator?
  1091.     jne    cmtxt12a        ; ne = no
  1092.     mov    comand.cmper,0        ; allow substitution in [...]
  1093.     mov    comand.cmarray,0    ; say have done array destination test
  1094. cmtxt12a:
  1095.     cmp    cx,comand.cmblen    ; buffer filled?
  1096.     ja    cmtxt14            ; a = yes, declare error
  1097.     jb    cmtxt13            ; a = not filled yet
  1098.     mov    ah,conout        ; notify user that the buffer is full
  1099.     mov    dl,bell
  1100.     int    dos
  1101.     jmp    cmtxt6            ; quit
  1102.  
  1103. cmtxt13:jmp    cmtxt2
  1104.  
  1105. cmtxt14:mov    ah,prstr
  1106.     mov    dx,offset cmer09
  1107.     int    dos
  1108.     jmp    prserr            ; declare parse error
  1109. cmtxt    endp
  1110.  
  1111.  
  1112. ; This routine gets a confirm (CR) and displays any extra non-blank text.
  1113. ; errflag non-zero means suppress "extra text" display in this routine
  1114. ; because another routine is handling errors.
  1115. cmcfrm    proc    far
  1116.     mov    bracecnt,0
  1117.     mov    comand.cmper,1        ; do not react to \%x substitutions
  1118.     cmp    cmrptr,offset cmdbuf    ; empty buffer?
  1119.     je    cmcfr7            ; e = yes
  1120.     mov    bx,cmrptr        ; where to read next
  1121.     dec    bx            ; last read byte
  1122.     cmp    byte ptr [bx],CR    ; terminated already?
  1123.     je    cmcfr7            ; e = yes
  1124.  
  1125. cmcfr1:    mov    cmsflg,0ffh        ; set space-seen flag (skip spaces)
  1126.     call    cmgtch            ; get a char
  1127.     push    cmrptr
  1128.     pop    temp            ; remember first non-space position
  1129.     jc    cmcfr4            ; c = terminator
  1130.     dec    temp            ; backup to text char
  1131. cmcfr3:    mov    cmsflg,0ffh        ; set space-seen flag (skip spaces)
  1132.     call    cmgtch
  1133.     jnc    cmcfr3            ; read until terminator
  1134. cmcfr4:    cmp    ah,' '
  1135.     je    cmcfr3            ; ignore ending on space
  1136.     cmp    ah,escape        ; escape?
  1137.     jne    cmcfr5            ; ne = no
  1138.     call    esceoc            ; do standard end of cmd on escape
  1139.     mov    ax,cmrptr
  1140.     cmp    ax,temp            ; started text yet?
  1141.     je    cmcfr1            ; e = no
  1142.     jmp    short cmcfr3        ; try again
  1143. cmcfr5: cmp    ah,'?'            ; curious?
  1144.         jne    cmcfr6            ; ne = no
  1145.     mov    cmhlp,offset cmin00    ; msg Confirm with c/r
  1146.     mov    cmsiz,0            ; no keyword
  1147.     mov    errflag,0
  1148.     jmp    cmkyhlp            ; do help
  1149. cmcfr6:    cmp    ah,cr            ; the confirmation char?
  1150.     jne    cmcfr3            ; ne = no
  1151.     cmp    errflag,0        ; already doing one error?
  1152.     jne    cmcfr7            ; ne = yes, skip this one
  1153.     mov    cx,cmrptr        ; pointer to terminator
  1154.     mov    dx,temp            ; starting place
  1155.     sub    cx,dx            ; end minus starting point = length
  1156.     jle    cmcfr7            ; le = nothing to display
  1157.     push    dx            ; save source pointer
  1158.     mov    ah,prstr
  1159.     mov    dx,offset cmer07    ; ?Ignoring extras
  1160.     int    dos
  1161.     pop    dx
  1162.     mov    bx,1            ; stdout handle, cx=count, dx=src ptr
  1163.     mov    ah,write2        ; allow embedded dollar signs
  1164.     int    dos
  1165.     mov    ah,prstr
  1166.     mov    dx,offset cmer08    ; trailer msg
  1167.     int    dos
  1168. cmcfr7:    xor    ax,ax
  1169.     mov    errflag,al
  1170.     call    optionclr        ; clear parser options
  1171.     clc                ; return confirmed
  1172.     ret
  1173. cmcfrm    endp
  1174.  
  1175. ;;; Routines to get and edit incoming text.
  1176.  
  1177. ; Detect '\%x' (x = '0' or above) and substitute the matching Macro string
  1178. ; in place of the '\%x' phrase in the user's buffer. If comand.cmper != 0
  1179. ; then treat '\%' as literal characters. If no matching parameter exists
  1180. ; just remove '\%x'. Ditto for \v(variable). Returns carry clear if nothing
  1181. ; done, else carry set and new text already placed in user's buffer.
  1182. ; Includes \v(variable) and \$(Environment variable) and \m(macro name).
  1183. ; Uses depth-first recursion algorithm. All registers preserved.
  1184. subst    proc    near
  1185.     cmp    comand.cmper,0        ; recognize '\%','\v(','\$(','\m(' ?
  1186.     jne    subst0            ; ne = no, treat as literals
  1187.     cmp    taklev,0        ; in a Take file?
  1188.     je    subst0a            ; e = no
  1189.     push    bx
  1190.     mov    bx,takadr
  1191.     cmp    [bx].takper,0        ; expand macros?
  1192.     pop    bx
  1193.     je    subst0a            ; e = yes
  1194. subst0:    clc                ; report out to application
  1195.     ret
  1196. subst0a:cmp    subtype,'0'        ; doing \numbers already?
  1197.     jne    subst0b            ; ne = no
  1198.     jmp    subst30            ; continue to parse digits
  1199.  
  1200. subst0b:cmp    ah,'\'            ; is it the first char of the pattern?
  1201.     jne    subst1            ; ne = no, try next
  1202.     cmp    subcnt,1        ; \\?
  1203.     jne    subst0c            ; ne = no
  1204.     mov    subcnt,0        ; clear state and pass back one \
  1205.     clc
  1206.     ret
  1207. subst0c:cmp    endedbackslash,0    ; ended sub macro on backslash?
  1208.     jne    subst1a            ; ne = yes
  1209.     inc    subcnt            ; say first char (\) is matched
  1210.     and    subcnt,1        ; modulo 2
  1211.     mov    subtype,ah
  1212.     stc                ; do not pass to application
  1213.     ret
  1214.  
  1215. subst1:    cmp    subcnt,1        ; first char matched already?
  1216.     ja    subst3            ; a = first two have been matched
  1217.     jb    subst0            ; b = none yet
  1218.     mov    al,ah            ; test kind of substitution
  1219.     or    al,20h            ; convert to lower case
  1220.     cmp    ah,'%'            ; second match char, same?
  1221.     je    subst2            ; e = yes
  1222.     cmp    al,'v'            ; \v(...)?
  1223.     je    subst2            ; e = yes
  1224.     cmp    al,'$'            ; \$(..)?
  1225.     je    subst2            ; e = yes
  1226.     cmp    al,'m'            ; \m(..)?
  1227.     je    subst2            ; e = yes
  1228.     cmp    comand.cmarray,0    ; allow array recognition?
  1229.     jne    subst1d            ; ne = no
  1230.     cmp    al,'&'            ; \&<char>[..]?
  1231.     je    subst2            ; e = yes
  1232. subst1d:                ; start \number
  1233.     cmp    comand.cmdonum,0    ; convert \numbers allowed?
  1234.     je    subst1c            ; e = no
  1235.     cmp    ah,'{'            ; \{number}?
  1236.     je    subst1b            ; e = yes
  1237.     cmp    al,'d'            ; \Dnumber?
  1238.     je    subst1b            ; e = yes
  1239.     cmp    al,'o'            ; \Onumber?
  1240.     je    subst1b            ; e = yes
  1241.     cmp    al,'x'            ; \Xnumber?
  1242.     je    subst1b            ; e = yes
  1243.     cmp    ah,'0'            ; in range for numbers?
  1244.     jb    subst1c            ; b = no
  1245.     cmp    ah,'9'
  1246.     ja    subst1c            ; a = no
  1247. subst1b:mov    subtype,'0'        ; mark as numeric
  1248.     mov    subcnt,2        ; matched second introducer
  1249.     push    bx
  1250.     mov    bx,cmrptr
  1251.     sub    bx,2            ; point at \
  1252.     mov    numtmp,bx        ; where \number starts
  1253.     pop    bx
  1254.     stc                ; do not pass to application
  1255.     ret
  1256.                     ; end \number
  1257. subst1c:cmp    al,'f'            ; \fname(..)?
  1258.     jne    subst1a            ; ne = no
  1259.     mov    subtype,al        ; remember type
  1260.     inc    subcnt            ; count match
  1261.     jmp    subst10            ; read more bytes internally
  1262.  
  1263. subst1a:push    ax            ; replay bytes as literals
  1264.     mov    al,subcnt        ; previously matched bytes
  1265.     xor    ah,ah
  1266.     inc    ax            ; plus current byte
  1267.     sub    cmrptr,ax
  1268.     mov    replay_cnt,ax        ; trailer bytes, amount to replay
  1269.     pop    ax
  1270.     mov    subcnt,0        ; mismatch, clear match counter
  1271.     mov    subtype,0
  1272.     mov    endedbackslash,0
  1273.     stc                ; reread from input
  1274.     ret
  1275.  
  1276. subst2:    cmp    al,'%'            ; starting \%?
  1277.     jne    subst2b            ; ne = no
  1278.     cmp    subtype,'v'        ; doing \v(..)? (dirs of c:\%foobar)
  1279. ;;;    je    subst1a            ; e = yes, don't expand \% inside
  1280.  
  1281. subst2b:mov    subtype,al        ; remember kind of substitution
  1282.     inc    subcnt            ; count match
  1283.     cmp    al,'%'
  1284.     je    subst10
  1285.     cmp    al,'&'            ; doing \&<char>[..]?
  1286.     je    subst10            ; e = yes
  1287.     stc                ; do not pass to application
  1288.     ret
  1289.  
  1290. subst3:    cmp    subtype,'v'        ; doing \v(..)?
  1291.     je    subst3a            ; e = yes
  1292.     cmp    subtype,'$'        ; doing \$(..)?
  1293.     je    subst3a            ; e = yes
  1294.     cmp    subtype,'m'        ; doing \m(..)?
  1295.     je    subst3a            ; e = yes
  1296.     mov    subcnt,0        ; clear match counter
  1297.     jmp    subst1a            ; no match
  1298.  
  1299. subst3a:cmp    ah,'('            ; have leading parenthesis?
  1300.     jne    subst1a            ; ne = no, mismatch, exit
  1301.     jmp    subst10            ; process \v(..), \$(..), \m(..)
  1302.  
  1303.                     ; \fname(..), \m(..), \v(..), \$(..)
  1304. subst10:push    bx            ; save working regs
  1305.     push    cx
  1306.     push    es
  1307.     push    cmptab            ; save current keyword parsing parms
  1308.     push    cmsptr
  1309.     push    cmsiz
  1310.     push    valtmp
  1311.     push    dx
  1312.     mov    subcnt,0        ; clear match counter
  1313.     mov    cmhlp,0
  1314.     mov    ax,cmrptr
  1315.     mov    valtmp,ax        ; remember current read pointer
  1316.     mov    cmsptr,ax        ; start of word
  1317.     mov    cmptab,offset valtab    ; table of keywords for \v(..)
  1318.     cmp    subtype,'v'        ; \v(..)?
  1319.     je    subst10a        ; e = yes
  1320.     mov    cmptab,offset envtab    ; Environment variable table \$(..)
  1321.     cmp    subtype,'$'        ; \$(..)?
  1322.     je    subst10a        ; e = yes
  1323.     mov    cmptab,offset evaltab    ; evaluate (name) table
  1324.     cmp    subtype,'f'        ; \fname()?
  1325.     je    subst10a        ; e = yes
  1326.     mov    cmptab,offset mcctab    ; main Macro table for \m(..)
  1327. subst10a:mov    cmsflg,0        ; see leading spaces/tabs
  1328.     mov    cmsiz,0            ; word size
  1329. subst11:mov    ch,subtype        ; save \type
  1330.     push    cx
  1331.     call    cmgtch            ; read a character into ah
  1332.     rcl    al,1            ; put carry bit into low bit of al
  1333.     pop    cx
  1334.     xchg    ch,subtype        ; recover \type
  1335.     rcr    al,1            ; recover carry bit
  1336.     jnc    subst13            ; nc = non-terminator
  1337.  
  1338.     cmp    subtype,'%'        ; \%<char>?
  1339.     jne    subst11d        ; ne = no
  1340.     cmp    ah,' '            ; question or similar?
  1341.     jbe    subst13            ; be = no, a funny
  1342.     inc    cmrptr            ; accept the terminator as data
  1343.     jmp    subst13
  1344. subst11d:
  1345.     cmp    subtype,'m'        ; discard trailing spaces for these
  1346.     je    subst11b
  1347.     cmp    subtype,'v'
  1348.     je    subst11b
  1349.     cmp    subtype,'&'
  1350.     je    subst11b
  1351.     cmp    subtype,'$'
  1352.     jne    subst11c
  1353. subst11b:cmp    ah,' '            ; space terminator?
  1354.     je    subst11            ; e = yes, ignore it
  1355. subst11c:cmp    ah,'?'            ; need help?
  1356.     jne    subst11a        ; ne = no
  1357.     call    cmkyhlp            ; display help information
  1358.     jmp    short subst11
  1359.  
  1360. subst11a:cmp    ah,escape        ; escape?
  1361.     jne    subst12            ; ne = no
  1362.     cmp    in_showprompt,0        ; making new prompt?
  1363.     jne    subst12            ; ne = yes, include literal escape
  1364.     call    cmkyesc            ; process escape
  1365.     jmp    short subst11        ; failed, ignore esc, read more
  1366.                     ;
  1367. subst12:cmp    subtype,'f'        ; \fname(..)?
  1368.     je    subst12a        ; e = yes, else no need to replay
  1369.     jmp    subst17
  1370.  
  1371. subst12a:mov    bx,cmsptr        ; where \fname() word started
  1372.     sub    bx,2            ; back over "\f" part
  1373.     mov    cx,cmrptr        ; last read + 1
  1374.     sub    cx,bx            ; bytes in \foobar
  1375.     mov    cmrptr,bx        ; reread point is the \
  1376.     add    replay_cnt,cx        ; bytes to reread (+external \ fails)
  1377.     jmp    subst17
  1378.  
  1379. subst13:inc    cmsiz            ; count user chars
  1380.     cmp    subtype,'%'        ; \%<char>?
  1381.     jne    subst13j        ; ne = no
  1382.     cmp    ah,'0'            ; large enough?
  1383.     jb    subst17            ; b = no, fail
  1384.     mov    cmsiz,3+1        ; size is three bytes, plus dec below
  1385.     sub    cmsptr,2        ; backup to \
  1386.     jmp    short subst13b
  1387. subst13j:cmp    subtype,'f'        ; \fname(...)?
  1388.     jne    subst13a        ; ne = no
  1389.     cmp    ah,'('            ; look for "(" as terminator
  1390.     je    subst13b        ; e = found, lookup "name" as keyword
  1391.     cmp    ah,'0'            ; numeric?
  1392.     jb    subst12a        ; b = no, end of string
  1393.     cmp    ah,'9'
  1394.     jbe    subst11            ; be = yes, keep reading
  1395.     call    tolowr            ; to lower case
  1396.     cmp    ah,'z'
  1397.     ja    subst12a        ; a = non-alpha, end of string
  1398.     cmp    ah,'a'
  1399.     jae    subst11            ; ae = alpha, keep reading
  1400.     jmp    short subst12a        ; yes, stop here
  1401.  
  1402. subst13a:cmp    subtype,'&'        ; \&c[..]?
  1403.     jne    subst13i        ; ne = no
  1404.     cmp    ah,']'            ; end bracket?
  1405.     jne    subst11            ; ne = no, keep going
  1406.     jmp    short subst13b        ; have it
  1407. subst13i:cmp    ah,')'            ; end bracket?
  1408.     jne    subst11            ; ne = no, keep looking
  1409. subst13b:dec    cmsiz            ; omit user's ')' from tests
  1410.     cmp    subtype,'&'        ; \&char[..]?
  1411.     je    subst20            ; e = yes, have all bytes
  1412.     cmp    subtype,'$'        ; \$(..)?
  1413.     je    subst13c        ; e = yes, no keyword in table
  1414.     push    cmsptr            ; save pointer
  1415. subst13g:mov    bx,cmsptr
  1416.     cmp    byte ptr [bx],' '    ; leading spaces?
  1417.     jne    subst13h        ; ne = no
  1418.     inc    cmsptr            ; look at next char
  1419.     jmp    short subst13g
  1420. subst13h:call    getkw            ; \m(..) and \v(..) test for keyword
  1421.     pop    cmsptr
  1422.     jc    subst12            ; c = failure
  1423.     jmp    short subst13d        ; success
  1424.  
  1425. subst13c:call    envvar            ; search Environment for the word
  1426.     jc    subst12            ; c = failure
  1427.     mov    bx,V_environ        ; set bx to kind of value for valtoa
  1428.     jmp    short subst13f
  1429.  
  1430. subst13d:cmp    subtype,'f'        ; doing \fname(...)?
  1431.     jne    subst13f        ; ne = no
  1432.     mov    subcnt,0        ; clear \f match indicator
  1433.     add    bx,[bx]            ; add length of keyword text (CNT)
  1434.     add    bx,2            ; point at value field
  1435.     mov    bx,[bx]            ; get value
  1436. subst13e:mov    evaltype,bx        ; remember kind of operation
  1437.     call    evaltoa            ; do \fname argument evaluation
  1438.     jc    subst12a        ; error, reparse
  1439.     jmp    subst17
  1440.  
  1441. subst13f:mov    ax,valtmp        ; where word started
  1442.     sub    ax,3            ; backup to "\v(" or "\$("
  1443.     cmp    subtype,'%'        ; doing \%<char>?
  1444.     jne    subst13k        ; ne = no
  1445.     inc    ax            ; one less field byte than others
  1446. subst13k:mov    cmrptr,ax        ; write output where backslash was
  1447.     call    bufreset        ; resets cmwptr too
  1448.     xor    dx,dx            ; signal valtoa to not add trailing sp
  1449.     cmp    subtype,'$'        ; \$(..)?
  1450.     je    subst14            ; e = yes, no keyword structure
  1451.     mov    cx,[bx]            ; bx = structure pointer, cx=keyw len
  1452.     add    cx,2            ; skip count byte
  1453.     add    bx,cx            ; point at 16 bit value field
  1454.     mov    bx,[bx]            ; get value to bx for valtoa
  1455. subst14:cmp    taklev,maxtak        ; room in take level?
  1456.     jb    subst15            ; b = yes
  1457.     mov    dx,offset stkmsg    ; out of work space msg
  1458.     mov    ah,prstr        ; display error message
  1459.     int    dos
  1460.     jmp    subst17
  1461.  
  1462.                     ; \&char[..]
  1463. subst20:mov    ax,valtmp        ; where <char> started
  1464.     mov    bx,ax
  1465.     cmp    byte ptr [bx+1],'['
  1466.     jne    subst12a
  1467.  
  1468.     sub    ax,2            ; backup to "\&<char>["
  1469.     mov    cmrptr,ax        ; write output where backslash was
  1470.     add    cmsptr,2        ; point after "[" for index math
  1471.     call    bufreset        ; resets cmwptr too
  1472.     push    si
  1473.     mov    si,cmsptr        ; start of string after "["
  1474.     mov    cx,1024            ; cmsiz ignores leading spaces
  1475. subst20a:cmp    byte ptr [si],' '    ; remove leading spaces
  1476.     jne    subst20b
  1477.     inc    si
  1478.     loop    subst20a
  1479. subst20b:push    si            ; save starting point
  1480.     mov    cx,1024            ; assumed max string in [..]
  1481.     xor    bx,bx
  1482.     cld
  1483. subst20c:lodsb
  1484.     cmp    al,']'            ; terminator?
  1485.     je    subst20e        ; e = yes
  1486.     inc    bx            ; count string chars
  1487. subst20d:loop    subst20c
  1488. subst20e:pop    si            ; recover starting point
  1489.     push    si
  1490.     mov    domath_ptr,si        ; ptr to string
  1491.     mov    domath_cnt,bx        ; length of string
  1492.     call    domath            ; string to binary in dx:ax
  1493.     pop    si
  1494.     mov    si,cmsptr        ; points just after "\&<char>["
  1495.     mov    bl,[si-2]        ; back up to <char>
  1496.     cmp    bl,'_'            ; arg list \$_[list element]?
  1497.     jne    subst20g        ; ne = no
  1498.     pop    si            ; clean stack
  1499.     cmp    ax,9            ; too large?
  1500.     ja    subst17            ; a = yes, do nothing
  1501.     push    ax
  1502.     mov    ax,4+2            ; want four bytes plus count
  1503.     call    malloc            ; to seg in ax
  1504.     mov    es,ax
  1505.     pop    ax
  1506.     mov    word ptr es:[2],'%\'    ; compose string \%<digit>
  1507.     add    al,'0'            ; use index of 0..9
  1508.     xor    ah,ah
  1509.     mov    word ptr es:[4],ax    ; null terminate
  1510.     mov    cx,3
  1511.     mov    word ptr es:[0],cx    ; three bytes of text
  1512.     mov    ax,es            ; setup for below
  1513.     jmp    short subst20h        ; compose the macro
  1514.  
  1515. subst20g:and    bl,not 20h        ; upper case it
  1516.     cmp    bl,'Z'            ; last <char>
  1517.     ja    subst20f        ; a = out of range
  1518.     sub    bl,'@'            ; remove bias
  1519.     xor    bh,bh
  1520.     shl    bx,1            ; address words
  1521.     mov    si,marray[bx]        ; get segment of string storage
  1522.     or    si,si            ; any?
  1523.     jz    subst20f        ; z = none
  1524.     mov    es,si            ; look at segment of array
  1525.     cmp    es:[0],ax        ; number of elements vs index above
  1526.     jbe    subst20f        ; be = out of range, quit
  1527.     mov    si,ax
  1528.     shl    si,1            ; index words
  1529.     mov    ax,es:[si+2]        ; get definition string segment to ax
  1530.     pop    si
  1531.     or    ax,ax            ; any?
  1532.     jz    subst17            ; z = no, empty
  1533. subst20h:mov    es,ax            ; string seg
  1534.     mov    cx,es:[0]        ; length of definition
  1535.     jcxz    subst17            ; z = empty
  1536.     call    takopen_sub        ; open take as text substitution
  1537.     jc    subst17            ; c = cannot open
  1538.     mov    bx,takadr        ; pointer to new Take structure
  1539.     mov    [bx].takbuf,es        ; segment of Take buffer
  1540.     mov    [bx].takcnt,cx        ; number of unread bytes
  1541.     jmp    subst17
  1542.  
  1543. subst20f:pop    si            ; failure
  1544.     jmp    subst17
  1545.  
  1546. subst15:mov    subcnt,0        ; clear match indicator
  1547.     call    takopen_sub        ; open take as text substitution
  1548.     jc    subst17            ; c = failed
  1549.     mov    cx,bx            ; value command kind, save
  1550.     mov    ax,tbufsiz        ; bytes of buffer space wanted
  1551.     call    malloc
  1552.     jc    subst17            ; c = failed
  1553.     mov    bx,takadr        ; point to structure
  1554.     or    [bx].takattr,take_malloc ; remember to dispose via takclos
  1555.     mov    [bx].takbuf,ax        ; seg of buffer
  1556.     mov    es,ax            ; ES:DI will be buffer pointer
  1557.     mov    di,[bx].takptr        ; where to write next
  1558.     mov    [bx].takper,0        ; expand macros
  1559. cmp subtype,'v'    ; \v(..)?
  1560. je subst16b    ; e = yes, don't expand macros within it
  1561.     cmp    subtype,'m'        ; \m(..)?
  1562.     jne    subst16a        ; ne = no
  1563. subst16b:
  1564.     mov    [bx].takper,1        ; do not expand macros in \m(..)
  1565. subst16a:mov    bx,cx            ; value command
  1566.     call    valtoa            ; make text be an internal macro
  1567.     mov    bx,takadr
  1568.     mov    [bx].takcnt,di        ; length
  1569. subst17:
  1570.     pop    dx
  1571.     mov    subtype,0
  1572.     pop    valtmp
  1573.     pop    cmsiz            ; restore borrowed keyword parameters
  1574.     pop    cmsptr
  1575.     pop    cmptab
  1576.     pop    es
  1577.     pop    cx
  1578.     pop    bx
  1579.     stc                ; carry = signal reread source
  1580.     ret
  1581.                     ; convert \number
  1582. subst30:cmp    ah,'?'            ; asking for help?
  1583.     jne    subst30a        ; ne = no
  1584.     mov    cmsiz,0            ; no keyword to expand
  1585.     push    cmhlp            ; save existing help
  1586.     mov    cmhlp,offset n1hlp    ; our message
  1587.     call    cmkyhlp            ; display help message
  1588.     pop    cmhlp            ; restore old help
  1589.     stc                ; buffer has been cleaned by cmkyhlp
  1590.     ret
  1591. subst30a:push    si
  1592.     mov    si,numtmp        ; where \ starts
  1593.     mov    atoibyte,1        ; convert only one character
  1594.     mov    cx,cmrptr
  1595.     sub    cx,numtmp        ; byte count to examine
  1596.     mov    atoi_cnt,cx        ; tell atoi the count
  1597.     call    atoi            ; value to dx:ax
  1598.     mov    cmsflg,0        ; clear space-seen flag
  1599.     jnc    subst31            ; nc = converted a value
  1600.     cmp    atoi_err,4        ; insufficient bytes to resolve
  1601.     je    subst30b        ; e = yes, get more
  1602.     mov    cx,cmrptr        ; replay complete failure
  1603.     sub    cx,numtmp        ; where \ started
  1604.     add    replay_cnt,cx        ; count to replay
  1605.     mov    si,numtmp
  1606.     mov    cmrptr,si        ; replay from here
  1607.     mov    subcnt,0        ; mismatch, clear match counter
  1608.     mov    subtype,0        ; and substitution type 
  1609.     mov    endedbackslash,0    ; general principles
  1610. subst30b:pop    si
  1611.     stc                ; carry set to read more bytes
  1612.     ret
  1613. subst31:pop    si
  1614.     cmp    atoi_err,1        ; success and terminated?
  1615.     jne    subst31a        ; ne = no
  1616.     mov    ah,al            ; ah now has value for app
  1617.     mov    subtype,0
  1618.     mov    subcnt,0
  1619.     inc    report_binary        ; signal have number to report to app
  1620.     clc
  1621.     ret
  1622.  
  1623. subst31a:cmp    atoi_err,0        ; success, can accept more data?
  1624.     jne    subst32            ; ne = no
  1625.     stc                ; c = return to read more data
  1626.     ret
  1627. subst32:mov    ah,al            ; ah now has value for app
  1628.     mov    subtype,0
  1629.     mov    subcnt,0
  1630.     push    ax
  1631.     dec    cmrptr            ; reread break byte
  1632.     mov    replay_cnt,1        ; for non-\
  1633.     push    bx
  1634.     mov    bx,cmrptr        ; last read byte
  1635.     mov    ah,[bx]            ; get break byte
  1636.     pop    bx
  1637.     inc    report_binary        ; signal have number to report to app
  1638.     cmp    ah,'\'            ; substitution introducer?
  1639.     jne    subst34            ; ne = no, pass it to app
  1640.     inc    cmrptr            ; step over read byte
  1641.     mov    replay_cnt,0        ; no replay
  1642.     mov    subcnt,1        ; mark \ introducer as have been read
  1643. subst34:pop    ax
  1644.     clc
  1645.     ret
  1646. subst    endp
  1647.  
  1648. ; Make an internal macro defined as the text for one of the value variables.
  1649. ; Use incoming DX as trailing space suppression flag, if null.
  1650. valtoa    proc    near
  1651.     push    di            ; save starting di
  1652.     push    dx            ; save trailing space flag
  1653.     mov    word ptr es:[di],0    ; fill buffer with sweet nothings
  1654.                     ; BX has index of variable
  1655.     cmp    bx,V_environ        ; \$() Environment?
  1656.     jne    valtoa1            ; ne = no
  1657.     mov    cx,envlen        ; string length
  1658.     jcxz    valtoa0            ; z = empty
  1659.     cmp    cx,tbufsiz-2        ; greater than current buffer?
  1660.     jbe    valtoa0a        ; be = no
  1661.     push    cx
  1662.     mov    ax,[bx].takbuf        ; old buffer
  1663.     mov    es,ax            ; new ES from above
  1664.     mov    ah,freemem        ; free it
  1665.     int    dos
  1666.     mov    ax,cx            ; bytes wanted
  1667.     call    malloc            ; get more space
  1668.     mov    bx,takadr
  1669.     mov    [bx].takbuf,ax        ; seg of macro def
  1670.     mov    ES,ax            ; new ES
  1671.     or    [bx].takattr,take_malloc ; remember to dispose via takclos
  1672.     pop    cx
  1673. valtoa0a:
  1674.     push    si
  1675.     push    ds
  1676.     lds    si,envadr        ; ds:si is source from Environment
  1677.     cld
  1678.     rep    movsb            ; copy string
  1679.     pop    ds
  1680.     pop    si
  1681. valtoa0:jmp    valtoa90
  1682.     
  1683. valtoa1:cmp    bx,V_argc        ; \v(argc)?
  1684.     jne    valtoa2            ; ne = no
  1685.     call    wrtargc            ; write argc
  1686.     jmp    valtoa90
  1687. valtoa2:cmp    bx,V_count        ; \v(count)?
  1688.     jne    valtoa3            ; ne = no
  1689.     call    wrtcnt            ; write it
  1690.     jmp    valtoa90
  1691. valtoa3:cmp    bx,V_date        ; \v(date)?
  1692.     jne    valtoa4
  1693.     call    wrtdate
  1694.     jmp    valtoa90
  1695. valtoa4:cmp    bx,V_errlev        ; \v(errorlevel)?
  1696.     jne    valtoa5            ; ne = no
  1697.     call    wrterr
  1698.     jmp    valtoa90
  1699. valtoa5:cmp    bx,V_dir        ; \v(dir)?
  1700.     jne    valtoa6
  1701.     call    wrtdir
  1702.     jmp    valtoa90
  1703. valtoa6:cmp    bx,V_time        ; \v(time)?
  1704.     jne    valtoa7
  1705.     call    wrttime
  1706.     jmp    valtoa90
  1707. valtoa7:cmp    bx,V_version        ; \v(version)?
  1708.     jne    valtoa8            ; ne = no
  1709.     mov    ax,version        ; get version such as 300
  1710.     call    fdec2di            ; convert binary to asciiz
  1711.     jmp    valtoa90
  1712. valtoa8:cmp    bx,V_platform        ; \v(platform)?
  1713.     jne    valtoa9            ; ne = no
  1714.     call    wrtplat            ; get machine name, e.g. "IBM-PC"
  1715.     jmp    valtoa90
  1716. valtoa9:cmp    bx,V_system        ; \v(system)?
  1717.     jne    valtoa10        ; ne = no
  1718.     call    wrtsystem        ; get "MS-DOS" string
  1719.     jmp    valtoa90
  1720. valtoa10:cmp    bx,V_kbd        ; \v(keyboard)?
  1721.     jne    valtoa11        ; ne = no
  1722.     call    wrtkbd            ; 88 or 101 value
  1723.     jmp    valtoa90
  1724. valtoa11:cmp    bx,V_speed        ; \v(speed)?
  1725.     jne    valtoa12        ; ne = no
  1726.     push    di
  1727.     call    fgetbaud        ; read baud rate from hardware
  1728.     pop    di
  1729.     mov    bx,portval
  1730.     mov    ax,[bx].baud
  1731.     cmp    al,byte ptr bdtab    ; index versus number of table entries
  1732.     jb    valtoa11a        ; b = index is in the table
  1733.     mov    si,offset cmunk-2    ; unrecognized value, say "unknown"
  1734.     mov    bx,7            ; length of string
  1735.     jmp    short valtoa11c
  1736. valtoa11a:mov    si,offset bdtab        ; ascii rate table
  1737.     mov    cl,[si]            ; number of entries
  1738.     inc    si            ; point to an entry
  1739. valtoa11b:
  1740.     mov    bx,[si]            ; length of text string
  1741.     cmp    ax,[si+bx+2]        ; our index vs table entry index
  1742.     je    valtoa11c        ; e = match
  1743.     add    si,bx            ; skip text
  1744.     add    si,4            ; skip count and index word
  1745.     loop    valtoa11b        ; look again
  1746.     mov    si,offset cmunk-2    ; unrecognized value, say "unknown"
  1747.     mov    bx,7            ; length of string
  1748. valtoa11c:mov    cx,bx            ; length of string
  1749.     add    si,2            ; point at string
  1750.     rep    movsb            ; copy string
  1751.     jmp    valtoa90
  1752.  
  1753. valtoa12:cmp    bx,V_program        ; \v(program)?
  1754.     jne    valtoa13        ; ne = no
  1755.     call    wrtprog            ; get "MS-DOS_KERMIT" string
  1756.     jmp    valtoa90
  1757. valtoa13:cmp    bx,V_status        ; \v(status)?
  1758.     jne    valtoa14        ; ne = no
  1759.     call    wrtstat            ; compose status string
  1760.     jmp    valtoa90
  1761. valtoa14:cmp    bx,V_ndate        ; \v(ndate)?
  1762.     jne    valtoa15        ; ne = no
  1763.     call    wrtndate
  1764.     jmp    valtoa90
  1765. valtoa15:cmp    bx,V_port        ; \v(port)? or \v(line)?
  1766.     jne    valtoa16        ; ne = no
  1767.     call    wrtport
  1768.     jmp    valtoa90
  1769. valtoa16:cmp    bx,V_terminal        ; \v(terminal)?
  1770.     jne    valtoa17        ; ne = no
  1771.     call    wrtterm
  1772.     jmp    valtoa90
  1773. valtoa17:
  1774. ifndef    no_tcp
  1775.     cmp    bx,V_session        ; \v(session) (internal Telnet)?
  1776.     jne    valtoa18        ; ne = no
  1777.  
  1778.     mov    ax,sescur        ; get internal Telnet session ident
  1779.     inc    ax            ; count from 1 for users (0 == none)
  1780.     call    fdec2di            ; convert binary to asciiz
  1781.     jmp    valtoa90
  1782. endif    ; no_tcp
  1783. valtoa18:cmp    bx,V_ntime        ; \v(ntime) (seconds in day)?
  1784.     jne    valtoa19        ; ne = no
  1785.     mov    ah,gettim        ; get DOS time of day
  1786.     int    dos            ; ch=hh, cl=mm, dh=ss, dl=0.01 sec
  1787.     mov    bx,60
  1788.     mov    al,ch            ; hours
  1789.     mul    bl            ; to minutes
  1790.     xor    ch,ch
  1791.     add    ax,cx            ; plus minutes
  1792.     mov    cl,dh            ; preserve seconds
  1793.     mul    bx            ; need carry out to DX
  1794.     add    ax,cx            ; add seconds
  1795.     adc    dx,0
  1796.     push    di
  1797.     push    es
  1798.     mov    di,ds
  1799.     mov    es,di
  1800.     mov    di,offset cmdbuf-30-cmdblen
  1801.     call    flnout            ; 32 bit converter
  1802.     mov    dx,offset cmdbuf-30-cmdblen
  1803.     call    strlen
  1804.     mov    si,dx
  1805.     pop    es
  1806.     pop    di
  1807.     cld
  1808.     rep    movsb
  1809.     mov    word ptr [di],0020h    ; space, null
  1810.     jmp    valtoa90
  1811.  
  1812. valtoa19:cmp    bx,V_dosver        ; \v(dosversion)?
  1813.     jne    valtoa20        ; ne = no
  1814.     mov    ax,dosnum        ; DOS verson, major high, minor low
  1815.     push    ax
  1816.     xchg    ah,al
  1817.     xor    ah,ah
  1818.     call    fdec2di            ; write major
  1819.     pop    ax
  1820.     xor    ah,ah
  1821.     cmp    al,10            ; less than 10?
  1822.     ja    valtoa19a        ; a = no
  1823.     mov    byte ptr es:[di],'0'    ; use two digits for minor
  1824.     inc    di
  1825. valtoa19a:
  1826.     call    fdec2di            ; write minor
  1827.     jmp    valtoa90
  1828. valtoa20:
  1829. ifndef    no_tcp
  1830.     cmp    bx,V_tcpip        ; \v(tcp_status)?
  1831.     jne    valtoa21        ; ne = no
  1832.                     ; SUCCESS    0
  1833.                     ; NO_DRIVER    1
  1834.                     ; NO_LOCAL_ADDRESS 2
  1835.                     ; BOOTP_FAILED    3
  1836.                     ; RARP_FAILED    4
  1837.                     ; BAD_SUBNET_MASK 5
  1838.                     ; SESSIONS_EXCEEDED 6
  1839.                     ; HOST_UNKNOWN    7
  1840.                     ; HOST_UNREACHABLE 8
  1841.                     ; CONNECTION_REJECTED 9
  1842.     mov    ax,tcp_status        ; get tcp status, if any
  1843.     call    fdec2di            ; write value
  1844.     jmp    valtoa90
  1845. endif    ; no_tcp
  1846.  
  1847. valtoa21:cmp    bx,V_parity        ; \v(parity)?
  1848.     jne    valtoa22        ; ne = no
  1849.     mov    bx,portval
  1850.     mov    bl,[bx].parflg        ; parity
  1851.     xor    bh,bh
  1852.     shl    bx,1            ; address words
  1853.     cld
  1854.     mov    si,parmsgtab[bx]    ; offset of parity name string
  1855. valtoa21a:lodsb
  1856.     stosb
  1857.     or    al,al            ; end of string?
  1858.     jnz    valtoa21a        ; nz = no
  1859.     mov    word ptr es:[di-1],0020h ; space, null
  1860.     jmp    valtoa90
  1861.  
  1862. valtoa22:cmp    bx,V_carrier        ; \v(carrier)?
  1863.     jne    valtoa23        ; ne = no
  1864.     mov    bl,flags.carrier    ; carrier
  1865.     and    bl,1            ; just one bit
  1866.     xor    bh,bh
  1867.     shl    bx,1            ; address words
  1868.     cld
  1869.     mov    si,onoffmsgtab[bx]    ; offset of carrier string
  1870. valtoa22a:lodsb
  1871.     stosb
  1872.     or    al,al            ; end of string?
  1873.     jnz    valtoa22a        ; nz = no
  1874.     mov    word ptr es:[di-1],0020h ; space, null
  1875.     jmp    valtoa90
  1876.  
  1877. valtoa23:cmp    bx,V_prompt        ; \v(prompt)?
  1878.     jne    valtoa24        ; ne = no
  1879.     push    si
  1880.     mov    si,prmptr        ; current prompt raw text
  1881.     cld
  1882. valtoa23a:lodsb                ; read a byte
  1883.     stosb                ; store
  1884.     or    al,al            ; end of string?
  1885.     jnz    valtoa23a        ; z = no
  1886.     pop    si
  1887.     dec    di            ; don't show trailing null
  1888.     mov    word ptr es:[di],0020h    ; space, null
  1889.     jmp    valtoa90
  1890.  
  1891. valtoa24:cmp    bx,V_cps        ; \v(cps)?
  1892.     jne    valtoa25        ; ne = no
  1893.     push    di
  1894.     mov    di,offset decbuf+200    ; must be in DS data seg
  1895.     call    shovarcps        ; use worker in msssho.asm
  1896.     mov    si,offset decbuf+200    ; copy this buffer to es:di buffer
  1897.     pop    di
  1898.     cld
  1899.     mov    cx,7            ; limit loop
  1900. valtoa24a:lodsb                ; read result
  1901.     or    al,al            ; terminator?
  1902.     jz    valtoa24b        ; z = yes, stop
  1903.     stosb
  1904.     loop    valtoa24a
  1905. valtoa24b:jmp    valtoa90
  1906.  
  1907. valtoa25:cmp    bx,V_space        ; \v(space)?
  1908.     jne    valtoa26        ; ne = no
  1909.     xor    cx,cx            ; drive letter (null means current)
  1910.     call    fdskspace        ; compute space, get letter into CL
  1911.     jnc    valtoa25a        ; nc = success
  1912.     xor    ax,ax
  1913.     xor    dx,dx
  1914. valtoa25a:
  1915.     push    di
  1916.     push    es
  1917.     mov    di,ds
  1918.     mov    es,di
  1919.     mov    di,offset cmdbuf-30-cmdblen
  1920.     call    flnout
  1921.     mov    dx,offset cmdbuf-30-cmdblen
  1922.     call    strlen
  1923.     mov    si,dx
  1924.     pop    es
  1925.     pop    di
  1926.     cld
  1927.     rep    movsb
  1928.     mov    word ptr [di],0020h    ; space, null
  1929.     jmp    valtoa90
  1930.  
  1931. valtoa26:cmp    bx,V_startup        ; \v(startup)?
  1932.     jne    valtoa27        ; ne = no
  1933.     push    si
  1934.     mov    si,offset startup    ; startup directory string
  1935.     mov    dx,si
  1936.     call    strlen            ; get length to cx
  1937.     rep    movsb            ; copy string
  1938.     pop    si
  1939.     mov    word ptr es:[di],0020h    ; space, null
  1940.     jmp    valtoa90
  1941.  
  1942. valtoa27:cmp    bx,V_console        ; \v(console)?
  1943.     jne    valtoa28        ; ne = no
  1944.     mov    ax,1a00h        ; get ANSI.SYS installed state
  1945.     int    2fh
  1946.     mov    si,offset ansiword    ; assume installed
  1947.     or    al,al            ; installed?
  1948.     jnz    valtoa27a        ; nz = yes
  1949.     mov    si,offset noneword    ; say "NONE"
  1950. valtoa27a:lodsb                ; read a byte
  1951.     stosb                ; store a byte
  1952.     or    al,al            ; at end of string?
  1953.     jnz    valtoa27a        ; nz = no
  1954.     mov    word ptr es:[di-1],0020h ; space, null
  1955.     jmp    valtoa90
  1956.  
  1957. valtoa28:cmp    bx,V_monitor        ; \v(monitor)?
  1958.     jne    valtoa29        ; ne = no
  1959.     mov    si,offset colormsg    ; assume color monitor
  1960.     cmp    crt_mode,7        ; mono text
  1961.     jne    valtoa28a        ; ne = no
  1962.     mov    si,offset monomsg    ; say mono
  1963. valtoa28a:jmp    short valtoa27a        ; copy material
  1964.  
  1965. valtoa29:cmp    bx,V_cmdlevel        ; \v(cmdlevel)?
  1966.     jne    valtoa30        ; ne = no
  1967.     mov    al,taklev        ; take level
  1968.     xor    ah,ah
  1969.     call    fdec2di            ; write value
  1970.     jmp    valtoa90
  1971.  
  1972. valtoa30:cmp    bx,V_input        ; \v(input)?
  1973.     jne    valtoa31        ; ne = no
  1974.     push    temp
  1975.     push    si
  1976.     push    DS
  1977.     push    es
  1978.     call    buflog            ; get INPUT buffer pointers
  1979.     mov    ax,es            ; seg of input buffer
  1980.     pop    es
  1981.     jcxz    valtoa30b        ; z = empty
  1982.     cmp    cx,tbufsiz-2-1        ; buffer plus null terminator
  1983.     jbe    valtoa30a        ; be = enough space to hold text
  1984.     mov    cx,tbufsiz-2-1        ; limit text
  1985. valtoa30a:mov    DS,ax            ; ds:si is input buffer
  1986.     cld
  1987.     rep    movsb
  1988.     xor    al,al
  1989.     mov    es:[di],al        ; null terminator
  1990. valtoa30b:pop    DS
  1991.     pop    si
  1992.     pop    temp
  1993.     jmp    valtoa90
  1994.  
  1995. valtoa31:cmp    bx,V_query        ; \v(query)?
  1996.     jne    valtoa32        ; ne = no
  1997.     mov    word ptr es:[di],0020h     ; space, null
  1998.     cmp    queryseg,0        ; is there an malloc'd seg to use?
  1999.     je    valtoa90        ; e = no
  2000.     push    es
  2001.     mov    si,queryseg
  2002.     mov    es,si
  2003.     mov    cx,es:[0]        ; get length of string
  2004.     pop    es
  2005.     push    si
  2006.     cmp    cx,tbufsiz-2        ; greater than current buffer?
  2007.     jbe    valtoa31a        ; be = no
  2008.     push    cx
  2009.     mov    ax,[bx].takbuf        ; old buffer
  2010.     mov    es,ax            ; new ES from above
  2011.     mov    ah,freemem        ; free it
  2012.     int    dos
  2013.     mov    ax,cx            ; bytes wanted
  2014.     call    malloc            ; get more space
  2015.     mov    bx,takadr
  2016.     mov    [bx].takbuf,ax        ; seg of macro def
  2017.     mov    ES,ax            ; new ES
  2018.     or    [bx].takattr,take_malloc ; remember to dispose via takclos
  2019.     pop    cx
  2020.  
  2021. valtoa31a:push    ds
  2022.     mov    si,queryseg        ; get segment of query string
  2023.     mov    ds,si
  2024.     mov    si,2            ; skip count word
  2025.     cld
  2026.     rep    movsb            ; copy to es:di
  2027.     pop    ds
  2028.     pop    si
  2029.     jmp    valtoa90
  2030.  
  2031. valtoa32:cmp    bx,V_sysid        ; \v(sysid)?
  2032.     jne    valtoa33        ; ne = no
  2033.     mov    ax,'8U'            ; always report U8
  2034.     stosw
  2035.     jmp    valtoa90
  2036.  
  2037. valtoa33:cmp    bx,V_charset        ; \v(charset)?
  2038.     jne    valtoa34        ; ne = no
  2039.     mov    ax,'PC'
  2040.     stosw                ; CP prefix
  2041.     mov    ax,flags.chrset        ; get current Code Page
  2042.     call    fdec2di
  2043.     jmp    valtoa90
  2044.  
  2045. valtoa34:cmp    bx,V_inpath        ; \v(inpath)?
  2046.     jne    valtoa35        ; ne = no
  2047.     mov    si,inpath_seg        ; get segment of in_path string
  2048.     or    si,si            ; anything there?
  2049.     jz    valtoa34b        ; z = no
  2050.     push    ds
  2051.     mov    ds,si
  2052.     xor    si,si
  2053.     cld
  2054.     lodsw                ; get count word
  2055.     mov    cx,ax
  2056.     cmp    cx,64            ; keep it reasonable
  2057.     jbe    valtoa34a        ; be = ok
  2058.     mov    cx,64
  2059. valtoa34a:rep    movsb            ; copy to es:di
  2060.     dec    di            ; don't include trailing null from env
  2061.     pop    ds
  2062. valtoa34b:jmp    valtoa90
  2063.  
  2064. valtoa35:cmp    bx,V_disk        ; \v(disk)?
  2065.     jne    valtoa36        ; ne = no
  2066.     mov    ah,gcurdsk        ; get current disk
  2067.     int    dos
  2068.     add    al,'A'            ; make 1 == A (not zero)
  2069.     cld
  2070.     stosb
  2071.     xor    al,al
  2072.     mov    es:[di],al
  2073.     jmp    valtoa90
  2074.  
  2075. valtoa36:cmp    bx,V_cmdfile        ; \v(cmdfile)?
  2076.     jne    valtoa37        ; ne = no
  2077.     mov    si,offset cmdfile    ; path of last Take file
  2078.     mov    dx,si
  2079.     call    strlen
  2080.     cld
  2081.     rep    movsb
  2082.     jmp    valtoa90
  2083.  
  2084. valtoa37:cmp    bx,V_inidir        ; \v(inidir)?
  2085.     jne    valtoa38        ; ne = no
  2086.     mov    si,offset inidir    ; path of mskermit.ini
  2087.     mov    dx,si
  2088.     call    strlen
  2089.     cld
  2090.     rep    movsb
  2091.     jmp    valtoa90
  2092.  
  2093. valtoa38:cmp    bx,V_instatus        ; \v(instatus)?
  2094.     jne    valtoa39        ; ne = no
  2095.     mov    ax,input_status        ; special INPUT status word
  2096.     cmp    ax,0            ; negative?
  2097.     jge    valtoa38a        ; ge = no
  2098.     mov    byte ptr es:[di],'-'
  2099.     inc    di
  2100.     neg    ax
  2101. valtoa38a:call    fdec2di
  2102.     jmp    valtoa90
  2103.  
  2104. valtoa39:cmp    bx,V_minput        ; \v(minput)?
  2105.     jne    valtoa40        ; ne = no
  2106.     mov    ax,minpcnt        ; get minput match count
  2107.     call    fdec2di            ; convert to decimal
  2108.     jmp    valtoa90        ; done
  2109.  
  2110. valtoa40:cmp    bx,V_return        ; \v(return)?
  2111.     jne    valtoa41        ; ne = no
  2112.     mov    si,offset retbuf    ; <word count><string from RETURN>
  2113.     mov    cx,[si]            ; get count word
  2114.     add    si,2            ; point at string
  2115.     cld
  2116.     rep    movsb            ; copy to variable buffer
  2117.     jmp    valtoa90        ; done
  2118.  
  2119. valtoa41:cmp    bx,V_connection        ; \v(connection)?
  2120.     jne    valtoa42        ; ne = no
  2121.     mov    si,offset connmsg    ; word "local"
  2122.     mov    dx,si
  2123.     call    strlen            ; length to cx
  2124.     cld
  2125.     rep    movsb            ; copy to variable buffer
  2126.     jmp    valtoa90        ; done
  2127.  
  2128. valtoa42:cmp    bx,V_filespec        ; \v(filespec)?
  2129.     jne    valtoa43        ; ne = no
  2130.     mov    si,offset vfile        ; last used file transfer name
  2131.     mov    dx,si
  2132.     call    strlen            ; length to cx
  2133.     cld
  2134.     rep    movsb            ; copy to variable buffer
  2135.     jmp    valtoa90        ; done
  2136.  
  2137. valtoa43:cmp    bx,V_fsize        ; \v(fsize)?
  2138.     jne    valtoa44        ; ne = no
  2139.     mov    ax,word ptr lastfsize    ; sent file, length dword
  2140.     mov    dx,word ptr lastfsize+2
  2141. valtoa43a:push    di
  2142.     push    es
  2143.     mov    di,ds
  2144.     mov    es,di
  2145.     mov    di,offset cmdbuf-30-cmdblen
  2146.     call    flnout
  2147.     mov    dx,offset cmdbuf-30-cmdblen
  2148.     call    strlen
  2149.     mov    si,dx
  2150.     pop    es
  2151.     pop    di
  2152.     cld
  2153.     rep    movsb
  2154.     mov    word ptr [di],0020h    ; space, null
  2155.     jmp    valtoa90        ; done
  2156.  
  2157. valtoa44:cmp    bx,V_crc16        ; \v(crc16)?
  2158.     jne    valtoa45        ; ne = no
  2159.     mov    ax,crcword        ; accumlated CRC-16
  2160.     xor    dx,dx            ; clear high word
  2161.     jmp    short valtoa43a        ; common converter
  2162.  
  2163. valtoa45:cmp    bx,V_nday        ; \v(nday)?
  2164.     je    valtoa45a        ; e = yes
  2165.     cmp    bx,V_day        ; \v(day)?
  2166.     jne    valtoa80        ; ne = no
  2167. valtoa45a:
  2168.     mov    ah,getdate        ; DOS date (cx= yyyy, dh= mm, dl= dd)
  2169.     int    dos
  2170.     sub    cx,1900            ; make 1900 be zero
  2171.     cmp    dh,3            ; month
  2172.     jge    valtoa45b        ; ge = beyond Feb
  2173.     add    dh,9
  2174.     dec    cx            ; modify year number
  2175.     jmp    short valtoa45c
  2176. valtoa45b:
  2177.     sub    dh,3
  2178. valtoa45c:
  2179.     push    bx            ; save V_ kind
  2180.     mov    al,dl            ; day
  2181.     xor    ah,ah
  2182.     mov    si,ax            ; Julian day number, partial
  2183.     mov    al,dh            ; modified month number
  2184.     mov    bx,153
  2185.     mul    bx            ; 153 * month
  2186.     add    ax,2
  2187.     mov    bx,5
  2188.     div    bx
  2189.     add    si,ax            ; (153 * m + 2) / 5 + d
  2190.     mov    ax,cx            ; year since 1900
  2191.     mov    bx,1461
  2192.     mul    bx
  2193.     mov    bx,4
  2194.     div    bx
  2195.     xor    dx,dx            ; clear remainder from division
  2196.     add    ax,si            ; (1461 * y)/4 + (153 * m + 2)/5 + d
  2197.     add    ax,15078        ; above plus 15078
  2198.     adc    dx,0            ; carry out
  2199.     mov    bx,7            ; modulo 7 setup
  2200.     div    bx            ; above % 7
  2201.     mov    ax,dx            ; remainder to ax, discard quotient
  2202.     add    ax,3            ; plus 3
  2203.     xor    dx,dx
  2204.     div    bx            ; mod 7 again
  2205.     mov    ax,dx            ; ((above % 7) + 3) % 7) to ax
  2206.     pop    bx            ; recover V_ kind
  2207.     cmp    bx,V_nday        ; \v(nday)?
  2208.     jne    valtoa45d        ; ne = no, must be V_(day)
  2209.     add    al,'0'            ; add printable bias
  2210.     stosw                ; ASCII day number + null
  2211.     dec    di            ; don't count the null
  2212.     jmp    short valtoa90        ; done
  2213. valtoa45d:                ; need ASCII string
  2214.     mov    bl,3    
  2215.     mul    bl            ; day number times three chars
  2216.     mov    bx,ax            ; get 3*day number
  2217.     add    bx,offset day        ; three char day string
  2218.     mov    ax,[bx]
  2219.     stosw                ; first two bytes
  2220.     mov    al,[bx+2]
  2221.     xor    ah,ah
  2222.     stosw                ; last byte and null
  2223.     dec    di            ; don't count the null
  2224.     jmp    short valtoa90        ; done
  2225.  
  2226. valtoa80:push    bx            ; \m(macro_name)
  2227.     push    es
  2228.     mov    di,bx            ; save seg of macro def
  2229.     mov    bx,takadr
  2230.     test    [bx].takattr,take_malloc ; buffer already allocated?
  2231.     jz    valtoa80a        ; z = no
  2232.     push    es
  2233.     mov    ax,[bx].takbuf        ; old buffer
  2234.     or    ax,ax            ; if any allocated
  2235.     jz    valtoa80b        ; z = none
  2236.     mov    es,ax
  2237.     mov    ah,freemem        ; free it
  2238.     int    dos
  2239. valtoa80b:and    [bx].takattr,not take_malloc ; say no more freeing needed
  2240.     pop    es
  2241. valtoa80a:
  2242.     mov    [bx].takbuf,di        ; seg of macro def
  2243.     mov    [bx].takptr,2        ; offset of two
  2244.     mov    es,di
  2245.     mov    cx,es:[0]        ; get length of string
  2246.     pop    es
  2247.     pop    bx
  2248.     pop    dx
  2249.     pop    di
  2250.     mov    di,cx            ; report length in di
  2251.     clc
  2252.     ret
  2253. valtoa90:pop    dx            ; trailing space flag
  2254.     or    dx,dx            ; leave the spaces?
  2255.     jnz    valtoa91        ; nz = yes
  2256.     cmp    word ptr es:[di-1],0020h ; trailing space?
  2257.     jne    valtoa91        ; ne = no
  2258.     dec    di            ; remove space
  2259. valtoa91:pop    ax            ; saved starting di
  2260.     sub    di,ax            ; di = length of the buffer contents
  2261.     clc
  2262.     ret
  2263. valtoa    endp
  2264.  
  2265. ; Far callable version
  2266. fvaltoa    proc    far
  2267.     call    valtoa
  2268.     ret
  2269. fvaltoa    endp
  2270.  
  2271. ; Make an internal macro defined as the text for one of the value variables.
  2272. ; Use incoming DX as trailing space suppression flag, if null.
  2273. ; BX has keyword value (table envtab)
  2274. ; If these fail they consume their text quietly.
  2275. evaltoa    proc    near
  2276.     push    dx            ; save trailing space flag
  2277.     push    si
  2278.     push    temp            ; save work variable
  2279.     mov    al,comand.cmdonum
  2280.     push    ax
  2281.     mov    comand.cmdonum,0    ; kill \number conversion
  2282.     call    evarg            ; get argument array
  2283.     pop    ax
  2284.     mov    comand.cmdonum,al    ; restore \number conversion state
  2285.     mov    ax,valtmp        ; where '\fname' started
  2286.     sub    ax,2            ; back over \f
  2287.     mov    cmwptr,ax        ; where to write new output
  2288.         mov     cmrptr,ax               ; readjust read pointer too
  2289.     call    takopen_sub        ; open take as text substitution
  2290.     jnc    evalt5c            ; nc = success
  2291. evalt5b:jmp    evalt99            ; fail
  2292. evalt5c:mov    bx,takadr        ; Take structure
  2293.     mov    ax,tbufsiz        ; bytes of buffer space wanted
  2294.     call    malloc
  2295.     jc    evalt5b            ; c = failed
  2296.     mov    [bx].takbuf,ax        ; seg of allocated buffer
  2297.     or    [bx].takattr,take_malloc ; remember to dispose via takclos
  2298.     mov    es,ax
  2299.     mov    word ptr es:[0],tbufsiz
  2300.     mov    di,[bx].takptr        ; where to start writing
  2301.     mov    [bx].takcnt,0        ; number of unread bytes
  2302.     mov    [bx].takper,0
  2303.     cmp    evaltype,F_contents    ; \fcontents(macro)?
  2304.     je    evalt5d            ; e = yes
  2305.     cmp    evaltype,F_definition    ; \fdefinition(macro)?
  2306.     je    evalt5d            ; e = yes
  2307.     cmp    evaltype,F_literal    ; \fliteral(string)?
  2308.     jne    evalt5e            ; ne = no
  2309. evalt5d:mov    [bx].takper,1        ; treat macro names as literals
  2310.  
  2311. evalt5e:mov    bx,takadr
  2312.     mov    di,[bx].takptr        ; destination for replacment text
  2313.     cmp    evaltype,F_length    ; \flength(text)?
  2314.     jne    evalt6            ; ne = no
  2315.     mov    ax,arglen        ; length of variable name
  2316.     call    fdec2di            ; convert to ASCII string
  2317.     mov    bx,takadr
  2318.     sub    di,[bx].takptr        ; end minus start
  2319.     mov    [bx].takcnt,di        ; string length
  2320.     jmp    evalt99
  2321.  
  2322. evalt6:    cmp    evaltype,F_upper    ; \fupper(text)?
  2323.     je    evalt6a            ; e = yes
  2324.     cmp    evaltype,F_lower    ; \flower(text)?
  2325.     jne    evalt7            ; ne = no
  2326. evalt6a:mov    si,argptr        ; point to argument
  2327.     mov    cx,arglen        ; length of string
  2328.     jcxz    evalt6e            ; z = empty
  2329.     cld
  2330. evalt6b:lodsb                ; read from parse buffer
  2331.     cmp    evaltype,F_lower    ; to lower?
  2332.     ja    evalt6c            ; a = no, upper
  2333.     call    tolowr            ; move to lower case
  2334.     jmp    short evalt6d
  2335. evalt6c:call    toupr            ; move to upper case
  2336. evalt6d:stosb                ; write byte to Take buffer
  2337.     loop    evalt6b
  2338. evalt6e:mov    bx,takadr
  2339.     mov    cx,arglen        ; argument length
  2340.     mov    [bx].takcnt,cx        ; count entered bytes
  2341.     jmp    evalt99
  2342.  
  2343. evalt7:    cmp    evaltype,F_char        ; \fcharacter(n)?
  2344.     jne    evalt8            ; ne = no
  2345.     mov    si,argptr        ; point at text of number
  2346.     mov    domath_ptr,si
  2347.     mov    ax,arglen
  2348.     mov    domath_cnt,ax
  2349.     call    domath
  2350.     jc    evalt7d            ; c = failed
  2351.     cld
  2352.     stosb                ; store single byte
  2353.     mov    bx,takadr
  2354.     mov    [bx].takcnt,1        ; one byte as result
  2355. evalt7d:jmp    evalt99
  2356.  
  2357. evalt8:    cmp    evaltype,F_substr    ; \fsubstr(text, n1, n2)?
  2358.     jne    evalt9            ; ne = no
  2359.     xor    ax,ax
  2360.     mov    si,argptr[2]        ; point to n1 string
  2361.     mov    domath_ptr,si
  2362.     mov    ax,arglen[2]
  2363.     mov    domath_cnt,ax
  2364.     call    domath
  2365.     jnc    evalt8g            ; nc = got a number
  2366. evalt8f:xor    ax,ax
  2367.     xor    dx,dx
  2368.     jmp    short evalt8a
  2369. evalt8g:or    dx,dx            ; number overflowed to high word?
  2370.     jnz    evalt8f            ; nz = yes, reset number to zero
  2371.     or    ax,ax            ; zero?
  2372.     jz    evalt8a            ; z = yes
  2373.     dec    ax            ; count from 0 internally
  2374. evalt8a:mov    si,argptr        ; point at text
  2375.     add    si,ax            ; point at text[n1]
  2376.     mov    cx,arglen        ; length of 'text'
  2377.     sub    cx,ax            ; bytes remaining in 'text'
  2378.     jle    evalt8d            ; le = nothing left to extract
  2379.     push    si
  2380.     mov    si,argptr[4]        ; point to n2 string
  2381.     mov    domath_ptr,si
  2382.     mov    ax,arglen[4]
  2383.     mov    domath_cnt,ax
  2384.     call    domath
  2385.     pop    si
  2386.     jnc    evalt8b            ; nc = got a number
  2387.     mov    ax,arglen        ; default to full string
  2388.     xor    dx,dx
  2389. evalt8b:or    dx,dx            ; number overflowed to high word?
  2390.     jnz    evalt8d            ; nz = yes
  2391.     cmp    cx,ax            ; length available more than needed?
  2392.     jbe    evalt8c            ; be = no
  2393.     mov    cx,ax            ; use smaller n2
  2394. evalt8c:mov    bx,tbufsiz-2        ; unused buffer capacity (tbufsiz-2)
  2395.     cmp    cx,bx            ; n larger than buffer?
  2396.     jbe    evalt8e            ; be = no
  2397.     mov    ax,cx            ; remember amount wanted
  2398.     call    evmem            ; get more memory, want CX bytes
  2399.     xchg    ax,cx            ; now ax=amount available, cx=wanted
  2400.     cmp    cx,ax            ; wanted > available?
  2401.     jbe    evalt8e            ; be = no
  2402.     mov    cx,ax            ; limit to available memory
  2403. evalt8e:mov    bx,takadr
  2404.     mov    [bx].takcnt,cx        ; count of bytes
  2405.     cld
  2406.     rep    movsb            ; copy bytes to Take buffer
  2407. evalt8d:jmp    evalt99
  2408.  
  2409. evalt9:    cmp    evaltype,F_right    ; \fright(text, n)?
  2410.     jne    evalt10            ; ne = no
  2411.     mov    si,argptr[2]        ; point to n string
  2412.     mov    domath_ptr,si
  2413.     mov    ax,arglen[2]
  2414.     or    ax,ax            ; empty field?
  2415.     jz    evalt9d            ; z = yes, use whole string
  2416.     mov    domath_cnt,ax
  2417.     call    domath
  2418.     cmp    domath_cnt,0
  2419.     je    evalt9a            ; e = consummed whole string
  2420.     mov    arglen,0        ; force 0
  2421. evalt9d:mov    ax,arglen        ; default to full string
  2422.     xor    dx,dx
  2423. evalt9a:or    dx,dx            ; number overflowed to high word?
  2424.     jnz    evalt9c            ; nz = yes
  2425.     mov    cx,arglen        ; length of 'text'
  2426.     cmp    cx,ax            ; length available more than needed?
  2427.     jbe    evalt9b            ; be = no
  2428.     mov    cx,ax            ; use smaller n
  2429. evalt9b:mov    si,argptr        ; start of 'text'
  2430.     add    si,arglen        ; point at last + 1 byte of 'text'
  2431.     sub    si,cx            ; minus bytes to be copied
  2432.     mov    bx,takadr
  2433.     mov    [bx].takcnt,cx        ; count of bytes
  2434.     cld
  2435.     rep    movsb            ; copy bytes to Take buffer
  2436. evalt9c:jmp    evalt99
  2437.  
  2438. evalt10:cmp    evaltype,F_literal    ; \fliteral(text)?
  2439.     jne    evalt11            ; ne = no
  2440.     mov    si,argptr        ; start of string
  2441.     mov    cx,arglen        ; length of text (inc leading spaces)
  2442.     mov    bx,takadr
  2443.     mov    [bx].takcnt,cx        ; count of bytes
  2444.     cld
  2445.     rep    movsb            ; copy bytes to Take buffer
  2446.     jmp    evalt99
  2447.  
  2448. evalt11:cmp    evaltype,F_rpad        ; \frpad(text, n, c)?
  2449.     jne    evalt12
  2450.     mov    si,argptr[2]        ; get n
  2451.     mov    domath_ptr,si
  2452.     mov    ax,arglen[2]
  2453.     mov    domath_cnt,ax
  2454.     call    domath
  2455.     jc    evalt11d        ; c = error
  2456.     or    dx,dx            ; numeric overflow to high word?
  2457.     jne    evalt11d        ; ne = yes
  2458.     mov    bx,takadr
  2459.     mov    cx,tbufsiz-2        ; unused buffer capacity (tbufsiz-2)
  2460.     cmp    ax,cx            ; n larger than buffer?
  2461.     jbe    evalt11a        ; be = no
  2462.     call    evmem            ; get more memory
  2463.     jc    evalt11d        ; c = fail
  2464.     cmp    ax,cx            ; enough?
  2465.     jbe    evalt11a        ; be = yes
  2466.     mov    ax,cx
  2467. evalt11a:mov    cx,arglen        ; length of text
  2468.     cmp    ax,cx            ; field (n) shorter than text?
  2469.     jae    evalt11b        ; ae = no, use full text
  2470.     mov    cx,ax            ; copy just n of text
  2471. evalt11b:sub    ax,cx            ; available minus used bytes
  2472.     mov    bx,takadr
  2473.     mov    [bx].takcnt,cx        ; count of bytes
  2474.     mov    si,argptr        ; text
  2475.     cld
  2476.     rep    movsb            ; copy bytes to Take buffer
  2477.     mov    cx,ax            ; padding bytes
  2478.     jcxz    evalt11d        ; z = none
  2479.     add    [bx].takcnt,cx        ; increase count of bytes
  2480.     mov    bx,argptr+4        ; point to c
  2481.     mov    al,[bx]            ; padding character, if any
  2482.     cmp    arglen+4,0        ; any char given?
  2483.     jne    evalt11c        ; ne = yes
  2484.     mov    al,' '            ; else default to space
  2485. evalt11c:cld
  2486.     rep    stosb            ; append byte to Take buffer
  2487. evalt11d:jmp    evalt99
  2488.  
  2489. evalt12:cmp    evaltype,F_lpad        ; \flpad(text, n, c)?
  2490.     jne    evalt13
  2491.     push    si
  2492.     mov    si,argptr[2]        ; get n
  2493.     mov    domath_ptr,si
  2494.     mov    ax,arglen[2]
  2495.     mov    domath_cnt,ax
  2496.     call    domath
  2497.     pop    si
  2498.     jc    evalt12e        ; c = error
  2499.     or    dx,dx            ; numeric overflow to high word?
  2500.     jne    evalt12e        ; ne = yes
  2501.     mov    bx,takadr
  2502.     mov    cx,tbufsiz-2        ; unused buffer capacity (tbufsiz-2)
  2503.     cmp    ax,cx            ; n larger than buffer?
  2504.     jbe    evalt12a        ; be = no
  2505.     call    evmem            ; get more memory
  2506.     cmp    ax,cx            ; wanted > available?
  2507.     jbe    evalt12a        ; be = no
  2508.     mov    ax,cx            ; limit text
  2509. evalt12a:sub    ax,arglen        ; n - length of 'text'
  2510.     jns    evalt12b        ; ns = no underflow
  2511.     xor    ax,ax            ; else omit padding
  2512. evalt12b:mov    cx,ax            ; padding count
  2513.     mov    [bx].takcnt,cx        ; count of bytes
  2514.     mov    bx,argptr+4        ; point to c
  2515.     mov    al,[bx]            ; padding character, if any
  2516.     cmp    arglen+4,0        ; any char given?
  2517.     jne    evalt12c        ; ne = yes
  2518.     mov    al,' '            ; default to space
  2519. evalt12c:cld
  2520.     rep    stosb            ; copy byte to Take buffer
  2521.     mov    cx,arglen        ; length of text
  2522.     mov    cx,tbufsiz-2        ; buffer capacity
  2523.     mov    bx,takadr
  2524.     sub    cx,[bx].takcnt        ; minus bytes written so far
  2525.     mov    ax,arglen        ; length of 'text'
  2526.     cmp    ax,cx            ; n larger than buffer?
  2527.     jbe    evalt12d        ; be = no
  2528.     mov    ax,cx
  2529. evalt12d:mov    cx,ax            ; append count
  2530.     mov    bx,takadr
  2531.     add    [bx].takcnt,cx        ; increase count of bytes
  2532.     mov    si,argptr        ; start of text
  2533.     cld
  2534.     rep    movsb            ; append byte to Take buffer
  2535. evalt12e:jmp    evalt99
  2536.  
  2537. evalt13:cmp    evaltype,F_code        ; \fcode(char)?
  2538.     jne    evalt14            ; ne = no
  2539.     mov    bx,argptr        ; point to char address
  2540.     mov    al,[bx]            ; original char
  2541.     xor    ah,ah
  2542.     call    fdec2di            ; convert to ASCII string, no '\'
  2543.     mov    bx,takadr        ;   prefix
  2544.     sub    di,[bx].takptr        ; end minus start
  2545.     mov    [bx].takcnt,di        ; string length
  2546.     jmp    evalt99
  2547.  
  2548. evalt14:cmp    evaltype,F_definition    ; \fdefinition(macro)?
  2549.     je    evalt14i        ; e = yes
  2550.     cmp    evaltype,F_contents    ; \fcontents(macro)
  2551.     jne    evalt15
  2552. evalt14i:cmp    arglen,0        ; any name?
  2553.     jne    evalt14a        ; e = yes
  2554.     jmp    evalt14x        ; fail
  2555. evalt14a:mov    si,argptr        ; trim trailing spaces from user name
  2556.     mov    cx,arglen        ; length of raw user string
  2557.     mov    bx,si
  2558.     add    bx,cx
  2559. evalt14j:cmp    byte ptr [bx-1],' '    ; trailing space?
  2560.     jne    evalt14k        ; ne = no
  2561.     dec    arglen            ; remove it
  2562.     dec    bx            ; preceeding char
  2563.     loop    evalt14j
  2564. evalt14k:
  2565.     mov    si,offset mcctab    ; table of macro names
  2566.     cld
  2567.     lodsb
  2568.     mov    cl,al            ; number of macro entries
  2569.     xor    ch,ch
  2570.     jcxz    evalt14x        ; z = none
  2571. evalt14b:push    cx            ; save loop counter
  2572.     lodsw                ; length of macro name
  2573.     mov    cx,arglen        ; length of user's string
  2574.     cmp    ax,cx            ; mac name shorter that user spec?
  2575.     jb    evalt14d        ; b = yes, no match
  2576.     push    ax
  2577.     push    si            ; save these around match test
  2578.     push    di
  2579.     mov    di,argptr        ; user's string
  2580. evalt14c:mov    ah,[di]
  2581.     inc    di
  2582.     lodsb                ; al = mac name char, ah = user char
  2583.     and    ax,not 2020h        ; clear bits (uppercase chars)
  2584.     cmp    ah,al            ; same?
  2585.     loope    evalt14c        ; while equal, do more
  2586.     pop    di
  2587.     pop    si            ; restore regs
  2588.     pop    ax
  2589.     je    evalt14e        ; e = a match
  2590. evalt14d:add    si,ax            ; point to next name, add name length
  2591.     add    si,2            ;  and string pointer
  2592.     pop    cx            ; recover loop counter
  2593.     loop    evalt14b        ; one less macro to examine
  2594. evalt14x:jmp    evalt99
  2595.  
  2596. evalt14e:pop    cx            ; pop loop counter above
  2597.     cmp    byte ptr[si],0        ; name starts with null char?
  2598.     jne    evalt14f        ; ne = no
  2599.     jmp    evalt99            ; yes, TAKE file, ignore
  2600. evalt14f:mov    ax,[si-2]        ; length of macro name
  2601.     add    si,ax            ; skip over name
  2602.     push    es
  2603.     mov    es,[si]            ; segment of string structure
  2604.     xor    si,si            ; es:si = address of count + string
  2605.     mov    ax,es
  2606.     mov    cx,es:[si]        ; length of string
  2607.     mov    arglen+2,cx        ; save length here
  2608.     mov    argptr+2,ax        ; save es of es:si here
  2609.     pop    es
  2610.     call    evmem            ; get new memory, set new ES:DI
  2611.     cmp    cx,arglen+2        ; enough to hold material?
  2612.     jae    evalt14h        ; ae = yes
  2613.     mov    arglen+2,cx        ; shrink string size too
  2614.  
  2615. evalt14h:push    ds
  2616.     mov    bx,takadr
  2617.     mov    cx,arglen+2        ; length of string
  2618.     mov    [bx].takcnt,cx        ; macro length
  2619.     mov    [bx].takptr,2        ; offset of text in destination
  2620.     mov    ax,argptr+2        ; seg of string
  2621.     mov    ds,ax
  2622.     mov    si,2            ; offset of string
  2623.     cld
  2624.     rep    movsb            ; copy string
  2625.     pop    ds
  2626.     jmp    evalt99
  2627.  
  2628. evalt15:cmp    evaltype,F_maximum    ; \fmaximum(n1, n2)?
  2629.     je    evalt15a        ; e = yes
  2630.     cmp    evaltype,F_minimum    ; \fminimum(n1, n2)?
  2631.     jne    evalt16            ; ne = no
  2632. evalt15a:push    si
  2633.     mov    si,argptr[0]        ; point to n1 string
  2634.     mov    domath_ptr,si
  2635.     mov    ax,arglen[0]
  2636.     mov    domath_cnt,ax
  2637.     call    domath
  2638.     pop    si
  2639.     jc    evalt15x        ; c = failure
  2640.     push    ax            ; save result
  2641.     push    dx
  2642.     push    si
  2643.     mov    si,argptr[2]        ; point to n2 string
  2644.     mov    domath_ptr,si
  2645.     mov    ax,arglen[2]
  2646.     mov    domath_cnt,ax
  2647.     call    domath
  2648.     pop    si
  2649.     pop    cx            ; high first value
  2650.     pop    bx            ; low first value
  2651.     jc    evalt15x        ; c = failure
  2652.     mov    temp,0            ; assume reporting n1, subscript 0
  2653.     cmp    cx,dx            ; n1 > n2?
  2654.     jg    evalt15e        ; g = yes
  2655.     jl    evalt15d        ; l = no, but less than
  2656.     cmp    bx,ax            ; low order n1 >= n2?
  2657.     jge    evalt15e        ; ge = yes
  2658. evalt15d:mov    temp,2            ; say n2 is larger
  2659. evalt15e:cmp    evaltype,F_maximum    ; return max of the pair?
  2660.     je    evalt15f        ; e = yes
  2661.     xor    temp,2            ; change subscript
  2662. evalt15f:push    si
  2663.     mov    bx,temp            ; get subscript of reportable
  2664.     mov    si,argptr[bx]
  2665.     mov    cx,arglen[bx]
  2666.     cld
  2667.     rep    movsb            ; copy original string
  2668.     xor    al,al
  2669.     stosb                ; and null terminate
  2670.     pop    si
  2671.     mov    bx,takadr
  2672.     sub    di,[bx].takptr        ; bytes consumed
  2673.     mov    [bx].takcnt,di
  2674. evalt15x:jmp    evalt99
  2675.  
  2676. evalt16:cmp    evaltype,F_index    ; \findex(pat, string, [offset])
  2677.     je    evalt16common
  2678.     cmp    evaltype,F_rindex    ; \frindex(pat, string, [offset])
  2679.     jne    evalt17
  2680. evalt16common:                ; used for \frindex too
  2681.     cmp    arglen+4,0        ; optional offset given?
  2682.     je    evalt16a        ; e = no, use userlevel 1
  2683.     push    si
  2684.     mov    si,argptr[4]        ; evaluate optional offset
  2685.     mov    domath_ptr,si
  2686.     mov    ax,arglen[4]
  2687.     mov    domath_cnt,ax
  2688.     call    domath
  2689.     pop    si
  2690.     jnc    evalt16b        ; nc = got a number
  2691. evalt16a:mov    ax,1            ; default to one (user style)
  2692.     xor    dx,dx
  2693. evalt16b:or    dx,dx            ; too large?
  2694.     jnz    evalt16x        ; nz = too large, fail
  2695.     dec    ax            ; offset from start (0 base it)
  2696.     mov    temp,ax            ; remember offset
  2697.     cmp    evaltype,F_rindex    ; \frindex?
  2698.     jne    evalt16g        ; ne = no
  2699.     mov    cx,arglen+2        ; length of string
  2700.     sub    cx,arglen        ; minus length of pattern
  2701.     sub    cx,ax            ; minus displacement from right
  2702.     mov    temp,cx            ; new effective offset
  2703.     mov    ax,cx
  2704. evalt16g:
  2705.     push    es
  2706.     push    di
  2707.     mov    di,ds            ; string is in DS seg
  2708.     mov    es,di
  2709.     mov    cx,arglen+2        ; length of string
  2710.     sub    cx,arglen        ; minus length of pattern
  2711.     cmp    ax,cx            ; offset larger than this?
  2712.     jbe    evalt16c        ; be = no
  2713.     mov    temp,-1            ; to report zero
  2714.     jmp    short evalt16e        ;  fail
  2715. evalt16c:mov    si,argptr        ; pattern
  2716.     mov    di,argptr+2        ; string
  2717.     add    di,temp            ; plus offset
  2718.     mov    cx,arglen        ; pattern length
  2719.     mov    ax,cx
  2720.     add    ax,temp            ; plus offset
  2721.     cmp    ax,arglen+2        ; more than string?
  2722.     jbe    evalt16d        ; be = no, keep going
  2723.     mov    temp,-1            ; to report zero
  2724.     jmp    short evalt16e        ; a = no match
  2725. evalt16d:push    cx
  2726.     cld
  2727.     repe    cmpsb            ; compare strings
  2728.     pop    cx
  2729.     je    evalt16e        ; e = matched
  2730.     inc    temp            ; move pattern right one place
  2731.     cmp    evaltype,F_rindex    ; \frindex?
  2732.     jne    evalt16c        ; ne = no
  2733.     sub    temp,2            ; work to the left instead
  2734.     jmp    short evalt16c        ; try again
  2735. evalt16e:pop    di
  2736.     pop    es
  2737.     mov    ax,temp            ; report out number
  2738.     inc    ax            ; show 0 as 1 to user
  2739.     mov    bx,takadr
  2740.     call    fdec2di            ; binary to ASCIIZ
  2741.     mov    bx,takadr
  2742.     sub    di,[bx].takptr
  2743.     mov    [bx].takcnt,di        ; length of ASCIIZ result
  2744. evalt16x:jmp    evalt99
  2745.  
  2746. evalt17:cmp    evaltype,F_repeat    ; \frepeat(text,n)?
  2747.     jne    evalt18            ; ne = no
  2748.     cmp    arglen+2,0        ; number given?
  2749.     je    evalt17g        ; e = no, fail
  2750.     mov    si,argptr[2]        ; n
  2751.     mov    domath_ptr,si
  2752.     mov    ax,arglen[2]
  2753.     mov    domath_cnt,ax
  2754.     call    domath
  2755.     jc    evalt17g        ; c = fail
  2756.     or    dx,dx            ; number overflowed?
  2757.     jz    evalt17a        ; z = no
  2758.     mov    ax,1            ; default number
  2759. evalt17a:mov    arglen+2,ax        ; save reasonable value here
  2760.     mul    arglen            ; string length * repeats
  2761.     or    dx,dx            ; overflowed (> 64KB)?
  2762.     jnz    evalt17b        ; nz = yes
  2763.     cmp    ax,cmdblen        ; more than parse buffer?
  2764.     jbe    evalt17c        ; be = no
  2765. evalt17b:mov    ax,cmdblen        ; should be plenty (1000)
  2766. evalt17c:mov    cx,ax            ; bytes wanted
  2767.     call    evmem            ; allocate memory 
  2768.     mov    dx,cx            ; buffer length available
  2769. evalt17d:mov    cx,arglen        ; length of text
  2770.     cmp    cx,dx            ; length vs buffer capacity
  2771.     jbe    evalt17e        ; be = capacity exceeds string
  2772.     mov    cx,dx            ; chop string to fit
  2773. evalt17e:sub    dx,cx            ; deduct amount done
  2774.     mov    si,argptr        ; point at string
  2775.     cld
  2776.     rep    movsb
  2777.     dec    arglen+2        ; repeat count
  2778.     cmp    arglen+2,0        ; any more repeats to do?
  2779.     je    evalt17f        ; e = no
  2780.     or    dx,dx            ; any space left?
  2781.     jg    evalt17d        ; g = yes
  2782. evalt17f:mov    bx,takadr
  2783.     sub    di,[bx].takptr        ; end minus start
  2784.     mov    [bx].takcnt,di        ; string length
  2785. evalt17g:jmp    evalt99
  2786.  
  2787. evalt18:cmp    evaltype,F_reverse    ; \freverse(text)?
  2788.     jne    evalt19            ; ne = no
  2789.     mov    cx,arglen        ; length of string
  2790.     mov    si,argptr        ; string
  2791.     add    di,cx
  2792.     dec    di            ; last byte in output string
  2793.     mov    bx,takadr
  2794.     mov    [bx].takcnt,cx        ; length of output to buffer
  2795.     jcxz    evalt18b        ; z = empty string
  2796. evalt18a:cld
  2797.     lodsb                ; read left
  2798.     std
  2799.     stosb                ; write left, displaced, backward
  2800.     cld
  2801.     loop    evalt18a
  2802. evalt18b:jmp    evalt99
  2803.  
  2804. evalt19:cmp    evaltype,F_date        ; \fdate(filename)?
  2805.     je    evalt19a        ; e = yes
  2806.     cmp    evaltype,F_size        ; \fsize(filename)?
  2807.     jne    evalt20            ; ne = no
  2808. evalt19a:
  2809.     push    di
  2810.     mov    di,offset tmpbuf    ; work buffer in this data seg
  2811.     mov    byte ptr [di],0        ; terminator
  2812.     call    filedate        ; get info
  2813.     mov    dx,offset tmpbuf
  2814.     call    strlen            ; get length of results to CX
  2815.     pop    di
  2816.     mov    bx,takadr
  2817.     mov    [bx].takcnt,cx        ; length of results
  2818.     mov    si,offset tmpbuf
  2819.     cld
  2820.     rep    movsb            ; copy to Take buffer
  2821. evalt19b:jmp    evalt99
  2822.  
  2823. evalt20:cmp    evaltype,F_directories    ; \fdirectories(filespec)?
  2824.     jne    evalt20a        ; ne = no
  2825.     mov    findkind,2        ; say directory search
  2826.     jmp    short evalt20d
  2827. evalt20a:cmp    evaltype,F_files    ; \ffiles(filespec)?
  2828.     jne    evalt20b            ; ne = no
  2829.     mov    findkind,1        ; say file search
  2830.     jmp    short evalt20d
  2831. evalt20b:cmp    evaltype,F_rdirectories    ; \frdirectories(filespec)?
  2832.     jne    evalt20c        ; ne = no
  2833.     mov    findkind,2+4        ; directory search+recursive
  2834.     jmp    evalt20d        ; common worker
  2835. evalt20c:cmp    evaltype,F_rfiles    ; \frfiles(filespec)?
  2836.     jne    evalt21            ; ne = no
  2837.     mov    findkind,1+4        ; file search+recursive
  2838.  
  2839. evalt20d:
  2840.     mov    dx,argptr        ; filename
  2841.     mov    cx,arglen        ; length of it
  2842.     call    rfprep            ; prepare filespec for use below
  2843.     mov    filecnt,0        ; file count
  2844. evalt20e:call    rgetfile        ; get item from directory structure
  2845.     jnc    evalt20e        ; found one, repeat til none
  2846.                     ; redo a findfirst for \fnextfile()
  2847.     mov    dx,argptr        ; filename
  2848.     mov    cx,arglen        ; length of it
  2849.     call    rfprep            ; prepare filespec for \fnextfile()
  2850.     mov    [bx].filename,0        ; clear name so do search for first
  2851.     mov    dx,offset buff        ; restore default dta
  2852.     mov    ah,setdma        ; set the dta address
  2853.     int    dos
  2854.     mov    ax,filecnt        ; report file count
  2855.     call    fdec2di            ; convert to ASCII string
  2856.     mov    bx,takadr
  2857.     sub    di,[bx].takptr        ; end minus start
  2858.     mov    [bx].takcnt,di        ; string length
  2859.     jmp    evalt99
  2860.  
  2861. evalt21:cmp    evaltype,F_nextfile    ; \fnextfile()?
  2862.     jne    evalt24            ; ne = no
  2863.     test    findkind,2+1        ; find files or directories?
  2864.     jz    evalt21a        ; z = neither, do nothing
  2865.  
  2866.     call    rgetfile        ; find object in file store
  2867.     pushf                ; preserve carry status
  2868.     mov    dx,offset buff        ; point at dta
  2869.     mov    ah,setdma        ; set the dta address
  2870.     int    dos
  2871.     popf
  2872.     jc    evalt21a        ; c = failure
  2873.     mov    si,offset rpathname    ; report path\filename
  2874.     mov    dx,si
  2875.     call    strlen            ; length to CX for ev21wrk
  2876.     xor    ah,ah            ; clear \ counter AH for ev32wrk
  2877.     call    ev21wrk            ; write pretty bytes, count to dx
  2878.     push    ax            ; save AH slash counter
  2879.     mov    temp,dx            ; byte count
  2880.     mov    si,rfileptr         ; current filename from DOS
  2881.     lea    dx,[si].filename
  2882.     mov    si,dx            ; for ev21wrk etc below
  2883.     call    strlen            ; length of name
  2884.     pop    ax            ; recover AH slash counter
  2885.     call    ev21wrk            ; pretty write this part too
  2886.     add    dx,temp            ; byte count
  2887.     xor    al,al            ; null terminator
  2888.     stosb                ; write it
  2889.     inc    dx
  2890.     mov    bx,takadr
  2891.     mov    [bx].takcnt,dx        ; length of result
  2892.     jmp    evalt99
  2893. evalt21a:                ; c = failure, no next
  2894.     mov    findkind,0        ; failure means no next
  2895.     jmp    evalt99
  2896.  
  2897. ; Ensure digits are protected by an even number of preceeding '\'
  2898. ; Enter with ds:si as source string of count CX bytes, destination
  2899. ; of es:di, report final byte count in DX. Enter with AH holding
  2900. ; current count of '\' chars in a row.
  2901. ev21wrk proc near
  2902.     cld
  2903.     xor    dx,dx            ; bytes written
  2904.     jcxz    ev21w5            ; z = nothing to do
  2905. ev21w1:    lodsb                ; read a byte
  2906.     cmp    al,'\'            ; slash seen?
  2907.     jne    ev21w2            ; ne = no
  2908.     inc    ah            ; count slashes in a row
  2909. ev21w2:    test    ah,1            ; odd slash count now?
  2910.     jz    ev21w4            ; z = no, nothing to do
  2911.     cmp    al,'x'            ; hex introducer?
  2912.     je    ev21w2a            ; e = yes, consider numeric
  2913.     cmp    al,'X'
  2914.     je    ev21w2a
  2915.     cmp    al,'o'            ; octal introducer
  2916.     je    ev21w2a
  2917.     cmp    al,'O'
  2918.     je    ev21w2a
  2919.     cmp    al,'d'            ; decimal introducer
  2920.     je    ev21w2a
  2921.     cmp    al,'D'
  2922.     je    ev21w2a
  2923.     cmp    al,'0'            ; digits?
  2924.     jb    ev21w3            ; b = no
  2925.     cmp    al,'9'
  2926.     ja    ev21w3            ; a = no
  2927. ev21w2a:test    ah,1            ; odd number of slashes?
  2928.     jz    ev21w3            ; z = no
  2929.     push    ax
  2930.     mov    al,'\'            ; double slash
  2931.     stosb
  2932.     pop    ax
  2933.     inc    dx            ; count byte written
  2934. ev21w3:    cmp    al,'\'            ; slash?
  2935.     je    ev21w4            ; e = yes
  2936.     xor    ah,ah            ; clear slash count
  2937. ev21w4:    stosb                ; store original byte
  2938.     inc    dx            ; count byte written
  2939.     loop    ev21w1
  2940. ev21w5:    ret
  2941. ev21wrk endp
  2942.  
  2943. evalt24:cmp    evaltype,F_replace    ; \freplace(source,pat,replacement)?
  2944.     jne    evalt25            ; ne = no
  2945.     cmp    arglen+2,0        ; arg2 omitted?
  2946.     jne    evalt24a        ; ne = no
  2947.     mov    cx,arglen        ; source length
  2948.     mov    si,argptr        ; source
  2949.     jmp    evalt24g        ; copy source to output
  2950.  
  2951. evalt24a:push    es            ; provided Take buffer, save
  2952.     push    di
  2953.     mov    ax,seg decbuf        ; use local temp buf
  2954.     mov    es,ax
  2955.     mov    di,offset decbuf    ; as es:di
  2956.     mov    si,argptr        ; make source ds:si
  2957.     mov    cx,arglen        ; length of source
  2958.     cld
  2959. evalt24b:push    cx            ; save source counter
  2960.     push    si            ; save source pointer
  2961.     cmp    cx,arglen+2        ; fewer source bytes than pattern?
  2962.     jb    evalt24e        ; b = yes, no replacement possible
  2963.     mov    bx,argptr+2        ; pattern
  2964.     mov    cx,arglen+2        ; length of pattern
  2965.  
  2966. evalt24c:mov    ah,[bx]            ; pattern byte
  2967.     cmp    [si],ah            ; same as source byte?
  2968.     jne    evalt24e        ; ne = no, no match
  2969.     inc    si            ; next bytes to match
  2970.     inc    bx
  2971.     loop    evalt24c        ; do all pattern bytes
  2972.     mov    si,argptr+4        ; get replacement pattern
  2973.     mov    cx,arglen+4        ; its length
  2974.     mov    ax,di            ; starting offset in temp buffer
  2975.     sub    ax,offset decbuf    ; minus start of buffer
  2976.     add    ax,cx            ; count of bytes to be in buffer
  2977.     cmp    ax,decbuflen        ; longer than buffer?
  2978.     jbe    evalt24d        ; be = no overflow
  2979.     sub    ax,cx            ; get back bytes already in buffer
  2980.     sub    ax,decbuflen
  2981.     neg    ax            ; bytes available
  2982.     mov    cx,ax            ; move just that many
  2983. evalt24d:rep    movsb            ; copy to output (may be nothing)
  2984.     pop    si
  2985.     pop    cx
  2986.     add    si,arglen+2        ; length of pattern
  2987.     sub    cx,arglen+2        ; bytes left in source
  2988.     jmp    short evalt24f
  2989.  
  2990. evalt24e:pop    si            ; mismatch comes here
  2991.     pop    cx
  2992.     movsb                ; write source byte to output buffer
  2993.     dec    cx            ; one less source byte
  2994. evalt24f:or    cx,cx            ; qty of source bytes remaining
  2995.     jg    evalt24b        ; g = more source bytes to examine
  2996.     sub    di,offset decbuf    ; length of output material
  2997.     mov    cx,di
  2998.     mov    ax,cx            ; save length in ax
  2999.     pop    di
  3000.     pop    es            ; original Take buffer
  3001.     call    evmem            ; allocate new Take buffer, len cx
  3002.     mov    si,offset decbuf
  3003.     mov    cx,ax            ; length used in decbuf
  3004. evalt24g:cld
  3005.     rep    movsb            ; copy decbuf to Take buffer
  3006.     mov    bx,takadr
  3007.     sub    di,[bx].takptr
  3008.     mov    [bx].takcnt,di        ; length of ASCIIZ result
  3009.     jmp    evalt99
  3010.  
  3011. evalt25:cmp    evaltype,F_eval        ; \feval(string)?
  3012.     jne    evalt26            ; ne = no
  3013.     mov    ax,argptr
  3014.     mov    domath_ptr,ax
  3015.     mov    ax,arglen
  3016.     or    ax,ax
  3017.     jz    evalt25b        ; z = no argument
  3018.     mov    domath_cnt,ax
  3019.     call    domath            ; return value in DX:AX
  3020.     jc    evalt25b        ; c = error, no value will be written
  3021.     push    di            ; save string destination es:di
  3022.     mov    di,offset decbuf    ; temp work space
  3023.     mov    word ptr [di],0        ; clear it
  3024.     or    dx,dx            ; is result negative?
  3025.     jns    evalt25a        ; ns = no, positive or zero
  3026.     neg    dx            ; flip sign
  3027.     neg    ax
  3028.     sbb    dx,0
  3029.     mov    byte ptr [di],'-'    ; show minus sign
  3030.     inc    di
  3031.  
  3032. evalt25a:call    flnout            ; convert DX:AX to ASCIIZ in DS:DI
  3033.     pop    di            ; recover original di
  3034.     mov    dx,offset decbuf
  3035.     mov    si,dx            ; save as source pointer too
  3036.     call    strlen            ; get length of string to cx
  3037.     cld
  3038.     rep    movsb            ; copy to final buffer
  3039.     mov    bx,takadr
  3040.     sub    di,[bx].takptr        ; end minus start
  3041.     mov    [bx].takcnt,di        ; string length
  3042. evalt25b:jmp    evalt99
  3043.  
  3044. evalt26:cmp    evaltype,F_verify    ; \fverify(pattern,string,offset)?
  3045.     jne    evalt27            ; ne = no
  3046.     cld
  3047.     push    es
  3048.     push    di
  3049.     mov    si,ds
  3050.     mov    es,si
  3051.     mov    di,argptr        ; pattern pointer
  3052.     mov    si,argptr+4        ; offset
  3053.     mov    ax,arglen+4        ; length of offset string
  3054.     call    domath
  3055.     jc    evalt26b        ; c = error
  3056.     mov    bx,ax            ; offset
  3057.     mov    si,argptr+2        ; string
  3058.     add    si,bx            ; string plus offset
  3059.     mov    cx,arglen+2        ; length of string
  3060.     sub    cx,bx            ; minus offset
  3061.     jg    evalt26a        ; g = have string left to compare
  3062.     mov    bx,-1            ; error, report -1 for no string
  3063.     jmp    short evalt26b
  3064. evalt26a:push    di
  3065.     lodsb                ; get a byte of string
  3066.     inc    bx            ; count based 1 for users
  3067.     push    cx            ; one less string byte
  3068.     mov    cx,arglen        ; length of pattern
  3069.     repne    scasb            ; scan for match
  3070.     pop    cx
  3071.     pop    di
  3072.     jne    evalt26b        ; ne = mismatch
  3073.     loop    evalt26a        ; next string char
  3074.     xor    bx,bx            ; report 0 if all match
  3075. evalt26b:mov    ax,bx            ; char position of first mismatch
  3076.     pop    di
  3077.     pop    es
  3078.     call    fdec2di            ; convert to ASCII string
  3079.     mov    bx,takadr
  3080.     sub    di,[bx].takptr        ; end minus start
  3081.     mov    [bx].takcnt,di        ; string length
  3082.     jmp    evalt99
  3083.  
  3084. evalt27:cmp    evaltype,F_ipaddr    ; \fipaddr(string, offset)?
  3085.     jne    evalt29            ; ne = no
  3086.     mov    si,argptr+2        ; offset
  3087.     mov    ax,arglen+2        ; length of offset string
  3088.     mov    domath_ptr,si        ; convert offset to binary
  3089.     mov    domath_cnt,ax
  3090.     call    domath            ; failure means use zero
  3091.     add    argptr,ax
  3092.     sub    arglen,ax
  3093. evalt27a:mov    si,argptr        ; scan string
  3094.     mov    cx,arglen
  3095.     cmp    cx,7            ; enough bytes to qualify?
  3096.     jl    evalt27x        ; l = no, quit
  3097.     cld
  3098. evalt27b:lodsb                ; trim leading spaces
  3099.     sub    al,'0'            ; remove ASCII bias
  3100.     cmp    al,9            ; numeric?
  3101.     jbe    evalt27c        ; be = yes
  3102.     loop    evalt27b
  3103. evalt27c:dec    si            ; break char
  3104.     inc    cx            ; byte count remaining
  3105.     mov    argptr,si        ; remember start of real text
  3106.     mov    arglen,cx        ; and its length
  3107.     call    evalt27w        ; scan for digits
  3108.     jc    evalt27x        ; c = fail
  3109.     cmp    byte ptr [si],'.'    ; looking at dot now?
  3110.     jne    evalt27x        ; ne = fail
  3111.     inc    si
  3112.     call    evalt27w        ; scan for digits
  3113.     jc    evalt27x        ; c = fail
  3114.     cmp    byte ptr [si],'.'    ; looking at dot now?
  3115.     jne    evalt27x        ; ne = fail
  3116.     inc    si
  3117.     call    evalt27w        ; scan for digits
  3118.     jc    evalt27x        ; c = fail
  3119.     cmp    byte ptr [si],'.'    ; looking at dot now?
  3120.     jne    evalt27x        ; ne = fail
  3121.     inc    si
  3122.     call    evalt27w        ; scan for digits
  3123.     jc    evalt27x        ; c = fail
  3124.     mov    al,[si]            ; break byte
  3125.     sub    al,'0'            ; remove ASCII bias
  3126.     cmp    al,9
  3127.     jbe    evalt27x        ; be = is still numberic, fail
  3128.     mov    cx,si            ; break postion
  3129.     sub    cx,argptr        ; minus start
  3130.     jle    evalt27x        ; le = problems, fail
  3131.     mov    si,argptr        ; start
  3132.     rep    movsb            ; copy string to destination
  3133.     mov    bx,takadr
  3134.     sub    di,[bx].takptr        ; end minus start
  3135.     mov    [bx].takcnt,di        ; string length
  3136.     jmp    evalt99
  3137.  
  3138. evalt27x:inc    argptr            ; step to next source byte
  3139.     dec    arglen            ; one less to examine
  3140.     cmp    arglen,7        ; any string left?
  3141.     jge    evalt27a        ; ge = yes, try again
  3142.     jmp    evalt99
  3143.  
  3144. evalt27w proc    near            ; worker to read digits, check range
  3145.     mov    cx,3            ; three digits
  3146.     xor    dx,dx            ; value of digits
  3147. evalt27w1:
  3148.     mov    ax,dx
  3149.     mul    ten            ; previous times ten to ax
  3150.     mov    dx,ax
  3151.     lodsb
  3152.     sub    al,'0'            ; remove ASCII bias
  3153.     cmp    al,9            ; validate decimal
  3154.     ja    evalt27w2        ; a = no
  3155.     add    dl,al            ; number so far
  3156.     jc    evalt27w3        ; c = overflow, fail
  3157.     loop    evalt27w1        ; while in digits
  3158.     inc    si            ; inc to offset dec below
  3159. evalt27w2:
  3160.     dec    si            ; back up to break byte
  3161.     cmp    cx,3            ; must have at least one digit
  3162.     je    evalt27w3        ; e = none, fail
  3163.     clc                ; success, have digits
  3164.     ret
  3165. evalt27w3:
  3166.     stc                ; fail
  3167.     ret
  3168. evalt27w endp
  3169.  
  3170. evalt29:cmp    evaltype,F_tod2secs    ; \ftod2secs(hh:mm:ss)?
  3171.     jne    evalt30            ; ne = no
  3172.     mov    si,argptr        ; hh:mm:ss string
  3173.     mov    bx,arglen
  3174.     mov    byte ptr [si+bx],0    ; terminate string
  3175.     call    tod2secs        ; convert to ASCII long in es:di
  3176.     jc    evalt99            ; c = failed to convert
  3177.     mov    bx,takadr
  3178.     sub    di,[bx].takptr        ; end minus start
  3179.     mov    [bx].takcnt,di        ; string length
  3180.     jmp    evalt99
  3181.  
  3182. evalt30:cmp    evaltype,F_chksum    ; \fchecksum(string)?
  3183.     jne    evalt31
  3184.     mov    si,argptr        ; string
  3185.     mov    cx,arglen
  3186.     xor    bx,bx            ; running sum, low and high portions
  3187.     xor    dx,dx
  3188.     cld
  3189. evalt30a:lodsb
  3190.     add    bl,al            ; accumulate checksum
  3191.     adc    bh,0            ; with carry
  3192.     adc    dx,0
  3193.     loop    evalt30a
  3194.     mov    ax,bx
  3195.     push    di
  3196.     push    es
  3197.     mov    di,ds
  3198.     mov    es,di
  3199.     mov    di,offset argptr
  3200.     call    flnout            ; 32 bit converter
  3201.     mov    dx,offset argptr
  3202.     call    strlen
  3203.     mov    si,dx
  3204.     pop    es
  3205.     pop    di
  3206.     cld
  3207.     rep    movsb
  3208.     mov    bx,takadr
  3209.     sub    di,[bx].takptr        ; end minus start
  3210.     mov    [bx].takcnt,di        ; string length
  3211.     jmp    evalt99
  3212.  
  3213. evalt31:cmp    evaltype,F_basename    ; \fbasename(string)?
  3214.     jne    evalt99
  3215.     mov    dx,argptr        ; string
  3216.     mov    cx,arglen
  3217.     mov    si,dx
  3218.     add    si,cx
  3219.     mov    byte ptr [si],0        ; force null termination
  3220.     push    di
  3221.     mov    di,offset decbuf+100    ; place for path part
  3222.     mov    si,offset decbuf    ; place for filename part
  3223.     call    fparse            ; split pieces
  3224.     pop    di
  3225.     mov    dx,si            ; look only at filename
  3226.     call    strlen
  3227.     rep    movsb            ; copy to result buffer
  3228.     mov    bx,takadr
  3229.     sub    di,[bx].takptr        ; end minus start
  3230.     mov    [bx].takcnt,di        ; string length
  3231.     jmp    evalt99
  3232.  
  3233. evalt99:pop    temp
  3234.     pop    si
  3235.     pop    dx            ; trailing space flag
  3236.     clc                ; success or fail, consume source
  3237.     ret
  3238. evaltoa    endp
  3239.  
  3240. ; Worker for evaltoa. 
  3241. ; Given start of string in DS:SI and string length in CMSIZ.
  3242. ; Return pointers to up to three arguments (as offset of start of their
  3243. ; non-white space) and the lengths of each argument field. Arrays argptr
  3244. ; and arglen hold these 16 bit values.
  3245. ; Bare commas separate arguments, curly braces and parentheses protect commas,
  3246. ; curly braces protect strings, starting with a curly brace is considered to 
  3247. ; mean use material within braces. A closing curly brace closes parens.
  3248. evarg    proc    near
  3249.     push    ax
  3250.     push    bx
  3251.     push    cx
  3252.     push    dx
  3253.     push    si
  3254.     push    temp
  3255.     mov    al,taklev
  3256.     xor    ah,ah
  3257.     mov    temp,ax
  3258.     xor    ax,ax
  3259.     mov    argptr,ax        ; clear arg pointer list
  3260.     mov    argptr+2,ax
  3261.     mov    argptr+4,ax
  3262.     mov    arglen,ax        ; clear arg length list
  3263.     mov    arglen+2,ax
  3264.     mov    arglen+4,ax
  3265.     xor    bx,bx            ; index of argument
  3266.                     ; read start of argument
  3267. evarg10:mov    si,cmrptr        ; last byte + 1 read
  3268.     xor    dx,dx            ; paren and curly brace counters
  3269.     mov    argptr[bx],si        ; where argument string starts
  3270.     mov    cmsiz,0            ; argument length counter
  3271.  
  3272. evarg12:mov    cl,comand.cmper        ; read next argument byte
  3273.     push    cx            ; preserve comand.cmper around call
  3274.     push    temp            ; preserve take level monitor
  3275.     push    evaltype        ; preserve, reading may recurse
  3276.     push    bx            ; preserve argument counter
  3277.     push    dx            ; preserve curly brace counter
  3278.     push    cmsiz
  3279.     mov    cmsflg,0        ; see leading spaces/tabs
  3280.     push    argptr            ; save our working variables
  3281.     push    argptr+2
  3282.     push    argptr+4
  3283.     push    arglen
  3284.     push    arglen+2
  3285.     push    arglen+4
  3286.     cmp    evaltype,F_contents    ; \fcontents(macro)?
  3287.     je    evarg13            ; e = yes
  3288.     cmp    evaltype,F_literal    ; \fliteral(string)?
  3289.     jne    evarg14            ; ne = no
  3290. evarg13:mov    comand.cmper,1        ; arg, treat macros as literals
  3291. evarg14:call    cmgtch            ; recursively read a character into ah
  3292.     pop    arglen+4
  3293.     pop    arglen+2
  3294.     pop    arglen
  3295.     pop    argptr+4
  3296.     pop    argptr+2
  3297.     pop    argptr
  3298.     pop    cmsiz
  3299.     pop    dx
  3300.     pop    bx
  3301.     pop    evaltype
  3302.     pop    temp
  3303.     pop    cx
  3304.     mov    comand.cmper,cl        ; restore macro expansion state
  3305.     pushf                ; save carry flag from cmgtch
  3306.     inc    cmsiz            ; count argument byte
  3307.     push    bx
  3308.     mov    bl,taklev        ; current take level
  3309.     cmp    bl,byte ptr temp    ; react to specials?
  3310.     pop    bx
  3311.     jbe    evarg15            ; be = yes, else in macro/take
  3312.     popf                ; clear stack of cmgtch carry status
  3313.     jmp    evarg12            ; read another byte
  3314.  
  3315. evarg15:popf                ; recover cmgtch carry status
  3316.     jnc    evarg50            ; nc = non-terminator
  3317.     cmp    ah,' '            ; just a space?
  3318.     jne    evarg17            ; ne = no
  3319.     cmp    cmsiz,1            ; is it a leading space?
  3320.     jne    evarg12            ; ne = no, consider it to be data
  3321.     cmp    evaltype,F_literal    ; \fliteral(string)?
  3322.     je    evarg12            ; e = yes, keep leading spaces
  3323.     jmp    evarg10            ; skip space, get first arg byte
  3324.     
  3325. evarg17:cmp    ah,'?'            ; need help?
  3326.     jne    evarg20            ; ne = no
  3327.     call    fcmdhlp            ; compute cmhlp to proper string
  3328.     mov    cmsiz,0            ; so help is with provided text
  3329.     call    cmkyhlp
  3330.     jmp    repars
  3331.  
  3332. evarg20:dec    cmsiz            ; omit byte from count
  3333.     cmp    ah,escape        ; ESC?
  3334.     jne    evarg22            ; ne = no
  3335.     cmp    evaltype,F_contents    ; \fcontents(macro)?
  3336.     je    evarg21            ; e = yes
  3337.     cmp    evaltype,F_definition    ; \fdefinition(macro)?
  3338.     jne    evarg22            ; ne = no
  3339. evarg21:push    cmptab            ; save working pointers
  3340.     push    cmsptr
  3341.     mov    cmptab,offset mcctab    ; look at table of macro names
  3342.     mov    cx,argptr[bx]        ; look at whole word todate
  3343.     mov    cmsptr,cx        ; start of word
  3344.     push    bx
  3345.     call    cmkyesc            ; do escape word completion
  3346.     pop    bx
  3347.     pop    cmsptr
  3348.     pop    cmptab
  3349.     xor    ah,ah            ; in case no match
  3350. evarg22:call    bufreset        ; reset cmwptr
  3351.     cmp    ah,escape        ; escape?
  3352.     jne    evarg30            ; ne = no
  3353.     push    ax
  3354.     push    dx
  3355.     mov    ah,conout        ; ring the bell
  3356.     mov    dl,bell
  3357.     int    dos
  3358.     pop    dx
  3359.     pop    ax
  3360.     jmp    evarg12            ; get next arg byte
  3361. evarg30:jmp    repars            ; reparse command
  3362.  
  3363. evarg50:cmp    ah,braceop        ; opening curly brace?
  3364.     jne    evarg52            ; ne = no
  3365.     inc    dl            ; count up curly brace
  3366.     jmp    evarg12            ; get next argument byte
  3367. evarg52:cmp    ah,bracecl        ; closing curly brace?
  3368.     jne    evarg60            ; ne = no
  3369.     sub    dl,1            ; count down curly braces
  3370.     jge    evarg54            ; ge = no underflow
  3371.      xor    dx,dx            ; clamp at zero
  3372. evarg54:jmp    evarg12
  3373.  
  3374. evarg60:or    dl,dl            ; within curly braces
  3375.     jnz    evarg3            ; nz = yes, don't count parens there
  3376.     cmp    ah,'('            ; opening paren?
  3377.     jne    evarg62            ; ne = no
  3378.     inc    dh            ; count up paren
  3379.     jmp    evarg12            ; get next byte of argument
  3380.  
  3381. evarg62:cmp    ah,')'            ; closing paren?
  3382.     jne    evarg3            ; ne = no
  3383.     sub    dh,1            ; count down closing paren
  3384.     jl    evarg64            ; l = reached terminator
  3385.     jmp    evarg12            ; get next byte of argument
  3386.  
  3387. evarg64:mov    cx,cmsiz
  3388.     dec    cx            ; don't count closing paren
  3389.     mov    arglen[bx],cx
  3390.     cmp    evaltype,F_literal    ; \fliteral(string)?
  3391.     je    evarg66            ; e = yes, keep trailing spaces
  3392.     mov    si,argptr[bx]        ; start of argument
  3393.     add    si,cx
  3394.     dec    si            ; last byte of argument
  3395.     jcxz    evarg66            ; z = no text to trim
  3396. evarg65:cmp    byte ptr [si],' '    ; trailing space?
  3397.     jne    evarg66            ; ne = no
  3398.     dec    si
  3399.     dec    arglen[bx]        ; one less argument byte
  3400.     loop    evarg65
  3401. evarg66:jmp    evarg7            ; ")" marks END of function
  3402.  
  3403. evarg3:    cmp    ah,','            ; possible argument terminator?
  3404.     jne    evarg5            ; ne = no
  3405.     or    dx,dx            ; within paren or curly braces?
  3406.     jne    evarg5            ; ne = yes, treat commas as data
  3407.     cmp    evaltype,F_literal    ; doing \fliteral()?
  3408.     je    evarg5            ; e = yes, commas are data
  3409.     cmp    evaltype,F_eval        ; doing \feval()?
  3410.     je    evarg5            ; e = yes, commas are data
  3411.     dec    cmsiz            ; do not count comma
  3412. evarg4:    mov    cx,cmsiz
  3413.     cmp    bx,3*2            ; done three args?
  3414.     jae    evarg4a            ; ae = yes, add no more args
  3415.     mov    arglen[bx],cx        ; mark argument length
  3416.     add    bx,2            ; next arg array element
  3417. evarg4a:jmp    evarg10            ; get next argument
  3418.  
  3419. evarg5:    jmp    evarg12            ; get next byte in argument
  3420.  
  3421. evarg7:    pop    temp
  3422.     pop    si
  3423.     pop    dx
  3424.     pop    cx
  3425.     pop    bx
  3426.     pop    ax
  3427.     clc
  3428.     ret
  3429. evarg    endp
  3430.  
  3431. ; Reallocates more memory than provided in the default takopen 128 byte buffer.
  3432. ; Enter with desired byte quantity in CX and a Take structure already
  3433. ; present at takadr. Returns with new buffer length in CX, ES:DI pointing
  3434. ; at buffer+2 ([bx].takbuf:[bx].takptr)
  3435. evmem    proc    near
  3436.     push    ax
  3437.     push    bx
  3438.     push    cx
  3439.     mov    bx,takadr
  3440.     mov    ax,[bx].takbuf        ; segment of preallocated memory
  3441.     mov    bx,cx            ; string length, in bytes
  3442.     add    bx,2+1+15        ; count + null term + round up
  3443.     mov    cl,4
  3444.     shr    bx,cl            ; convert to paragraphs (divide by 16)
  3445.     mov    cx,bx            ; remember desired paragraphs
  3446.     mov    ah,alloc        ; allocate a memory block
  3447.     int    dos            ; ax=seg, bx=paragraphs
  3448.     jc    evmem3            ; c = error, not enough memory
  3449.      cmp    bx,cx            ; obtained vs wanted
  3450.     jae    evmem2            ; ae = enough
  3451.     push    bx
  3452.     mov    bx,takadr
  3453.     mov    cx,[bx].takcnt        ; old unread-bytes
  3454.     shr    cx,1
  3455.     shr    cx,1
  3456.     shr    cx,1
  3457.     shr    cx,1            ; to paragraphs
  3458.     pop    bx
  3459.     cmp    bx,cx            ; more than we had originally?
  3460.     ja    evmem2            ; a = yes, so use the new larger buff
  3461.     push    es
  3462.     mov    es,ax            ; new allocated segment
  3463.     mov    ah,freemem        ; free it
  3464.     int    dos
  3465.     pop    es
  3466.     jmp    short evmem3        ; use original segment
  3467.  
  3468. evmem2:    mov    cl,4
  3469.     shl    bx,cl            ; paragraphs to bytes
  3470.     mov    cx,bx            ; return new bytes in CX
  3471.     sub    cx,2            ; minus two for len field
  3472.     mov    es,ax            ; set new segment
  3473.     mov    bx,takadr
  3474.     xchg    [bx].takbuf,ax        ; new segment, swap with old seg
  3475.     or    ax,ax            ; if any allocated
  3476.     jz    evmem2a            ; z = none
  3477.     push    es
  3478.     mov    es,ax            ; old allocated segment
  3479.     mov    ah,freemem        ; free old segment
  3480.     int    dos
  3481.     pop    es
  3482. evmem2a:mov    di,2            ; new offset
  3483.     mov    bx,takadr
  3484.     mov    [bx].takptr,di        ; new offset
  3485.     mov    [bx].takcnt,0        ; total buffer, unread bytes
  3486.     or    [bx].takattr,take_malloc ; malloc'd buffer to remove later
  3487.     pop    bx            ; discard old cx, return new cx
  3488.     pop    bx
  3489.     pop    ax
  3490.     ret
  3491.  
  3492. evmem3:    pop    cx            ; return original values
  3493.     pop    bx
  3494.     pop    ax
  3495.     ret
  3496. evmem    endp
  3497.  
  3498. ; Worker for evaltoa. Sets cmhlp to help string matching evaltype
  3499. fcmdhlp    proc    near
  3500.     mov    ax,evaltype        ; kind of \f command
  3501.     mov    bx,offset f1hlp        ; number
  3502.     cmp    ax,F_char        ; go through types
  3503.     je    fcmdh1            ; e = matched
  3504.     cmp    ax,F_maximum
  3505.     je    fcmdh1
  3506.     cmp    ax,F_minimum
  3507.     je    fcmdh1
  3508.     mov    bx,offset f2hlp        ; char
  3509.     cmp    ax,F_code
  3510.     je    fcmdh1
  3511.     mov    bx,offset f3hlp
  3512.     cmp    ax,F_contents
  3513.     je    fcmdh1
  3514.     cmp    ax,F_definition
  3515.     je    fcmdh1
  3516.     mov    bx,offset f8hlp
  3517.     cmp    ax,F_index
  3518.     je    fcmdh1
  3519.     mov    bx,offset f12hlp
  3520.     cmp    ax,F_lpad
  3521.     je    fcmdh1
  3522.     cmp    ax,F_rpad
  3523.     je    fcmdh1
  3524.     mov    bx,offset f15hlp
  3525.     cmp    ax,F_nextfile
  3526.     je    fcmdh1
  3527.     mov    bx,offset f16hlp
  3528.     cmp    ax,F_repeat
  3529.     je    fcmdh1
  3530.     mov    bx,offset f18hlp
  3531.     cmp    ax,F_right
  3532.     je    fcmdh1
  3533.     mov    bx,offset f20hlp
  3534.     cmp    ax,F_substr
  3535.     je    fcmdh1
  3536.     mov    bx,offset f22hlp
  3537.     cmp    ax,F_date
  3538.     je    fcmdh1
  3539.     cmp    ax,F_basename
  3540.     je    fcmdh1
  3541.     cmp    ax,F_size
  3542.     je    fcmdh1
  3543.     cmp    ax,F_files
  3544.     je    fcmdh1
  3545.     mov    bx,offset f25hlp
  3546.     cmp    ax,F_eval
  3547.     je    fcmdh1
  3548.     mov    bx,offset f8hlp
  3549.     cmp    ax,F_rindex
  3550.     je    fcmdh1
  3551.     mov    bx,offset f30hlp
  3552.     cmp    ax,F_chksum
  3553.     je    fcmdh1
  3554.     mov    bx,offset f9hlp        ; default to text
  3555. fcmdh1:    mov    cmhlp,bx        ; set help text pointer
  3556.     ret
  3557. fcmdhlp    endp
  3558.  
  3559. ; Obtain file date/time or size.
  3560. ; Incoming match pattern is in argptr (not ASCIIZ), length of arglen.
  3561. ; Write results to ds:di, not es:di.
  3562. filedate proc    near
  3563.     mov    bx,argptr        ; filename
  3564.     mov    dx,bx            ; for file open
  3565.     add    bx,arglen        ; length of it
  3566.     cmp    arglen,0        ; empty argument?
  3567.     je    filed2            ; e = yes
  3568.     mov    byte ptr [bx],0        ; make it ASCIIZ
  3569.     push    dx
  3570.     mov    dx,offset buff        ; use default dta
  3571.     mov    ah,setdma        ; set the dta address
  3572.     int    dos
  3573.     pop    dx
  3574.     mov    cx,10h            ; find dir and files
  3575.     mov    ah,first2        ; DOS 2.0 search for first
  3576.     int    dos            ; get file's characteristics
  3577.     jnc    filed1            ; nc = success
  3578.     ret                ; fail
  3579. filed1:    mov    bx,offset buff        ; default dta
  3580.     jmp    short filed3
  3581. filed2:    mov    bx,rfileptr        ; no filename, use last find info
  3582.     or    bx,bx            ; any?
  3583.     jnz    filed3            ; nz = yes
  3584.     mov    bx,offset buff        ; revert to default dta
  3585. filed3:    cmp    evaltype,F_date        ; want date/time stamp?
  3586.     je    filed5            ; e = yes
  3587.     cmp    evaltype,F_size        ; want file length?
  3588.     jne    filed4            ; ne = no (probably \ffiles(..))
  3589.     mov    ax,[bx].mysizelo    ; file size to dx:ax
  3590.     mov    dx,[bx].mysizehi
  3591.     call    flnout            ; convert to ASCII
  3592.     mov    byte ptr [di],0        ; terminate
  3593. filed4:    clc
  3594.     ret
  3595.  
  3596. filed5:    call    filetdate        ; do the work
  3597.     clc
  3598.     ret
  3599. filedate endp
  3600.  
  3601. ; Write file date and timestamp to ds:di, given DTA in ds:bx.
  3602. filetdate proc    far
  3603.     push    es            ; time/date stamp yyyymmdd hh:mm:ss
  3604.     mov    ax,ds
  3605.     mov    es,ax
  3606.     xor    ah,ah
  3607.     mov    al,[bx+25]        ; yyyyyyym from DOS via file open
  3608.     shr    al,1            ; get year
  3609.     add    ax,1980            ; add bias
  3610.     xor    dx,dx
  3611.     call    flnout            ; put year (1990) in buffer
  3612.     mov    ax,[bx+24]        ; yyyyyyyym mmmddddd  year+month+day
  3613.     shr    ax,1            ; month to al
  3614.     xor    ah,ah
  3615.     mov    cl,4
  3616.     shr    al,cl            ; month to low nibble
  3617.     mov    byte ptr[di],'0'    ; leading digit
  3618.     inc    di
  3619.     cmp    al,9            ; more than one digit?
  3620.     jbe    filetd1            ; be = no
  3621.     mov    byte ptr[di-1],'1'    ; new leading digit
  3622.     sub    al,10            ; get remainder
  3623. filetd1:add    al,'0'            ; to ascii
  3624.     stosb                ; end of month
  3625.     mov    al,[bx+24]        ; get day of month
  3626.     and    al,1fh            ; select day bits
  3627.     xor    ah,ah
  3628.     mov    cl,10
  3629.     div    cl            ; quot = al, rem = ah
  3630.     add    ax,'00'            ; add ascii bias
  3631.     stosw                ; leading digit and end of date
  3632.     mov    al,' '            ; space separator
  3633.     stosb
  3634.     mov    al,[bx+23]        ; hours  hhhhhmmm
  3635.     mov    cl,3
  3636.     shr    al,cl            ; move to low nibble
  3637.     xor    ah,ah
  3638.     mov    cl,10
  3639.     div    cl            ; quot = al, rem = ah
  3640.     add    ax,'00'            ; add ascii bias
  3641.     stosw                ; store hours
  3642.     mov    al,':'            ; separator
  3643.     stosb
  3644.     mov    ax,[bx+22]        ; get minutes: hhhhhmmm mmmsssss
  3645.     mov    cl,5
  3646.     shr    ax,cl            ; minutes to low byte
  3647.     and    al,3fh            ; six bits for minutes
  3648.     xor    ah,ah
  3649.     mov    cl,10
  3650.     div    cl
  3651.     add    ax,'00'            ; add ascii bias
  3652.     stosw
  3653.     mov    al,':'            ; separator
  3654.     stosb
  3655.     mov    al,[bx+22]        ; get seconds (double secs really)
  3656.     and    al,1fh
  3657.     shl    al,1            ; DOS counts by two sec increments
  3658.     xor    ah,ah
  3659.     mov    cl,10
  3660.     div    cl
  3661.     add    ax,'00'            ; add ascii bias
  3662.     stosw
  3663.     mov    byte ptr [di],0        ; terminate
  3664.     pop    es
  3665.     ret
  3666. filetdate endp
  3667.  
  3668. ; Setup to find files or directories given in ds:dx, length in cx.
  3669. ; Regularize path to always end in \, add .\ if necessary.
  3670. rfprep proc    far
  3671.     push    di
  3672.     mov    bx,dx            ; filename
  3673.     add    bx,cx            ; length of it
  3674.     mov    byte ptr [bx],0        ; ensure ASCIIZ
  3675.     jcxz    rfprep2            ; z = empty argument
  3676.     mov    ax,word ptr [bx-2]    ; last two chars
  3677.     mov    cl,[bx-3]        ; preceeding char
  3678.     cmp    ah,'*'            ; ends in *?
  3679.     jne    rfprep5            ; ne = no
  3680.     cmp    al,'.'            ; .* extension given?
  3681.     je    rfprep5            ; e = yes
  3682.     cmp    cl,'.'            ; . given?
  3683.     je    rfprep5            ; e = yes
  3684.     mov    word ptr [bx],'*.'    ; force .* extension
  3685.     mov    byte ptr [bx+2],0
  3686. rfprep5:push    cx            ; see if pattern is a directory
  3687.     push    dx
  3688.     mov    dx,offset buff        ; default dta
  3689.     mov    ah,setdma
  3690.     int    dos
  3691.     pop    dx            ; filename to ds:dx
  3692.     push    dx            ; save again
  3693.     mov    cx,10h            ; find dir and files
  3694.     mov    ah,first2        ; DOS 2.0 search for first
  3695.     int    dos
  3696.     pop    dx
  3697.     pop    cx
  3698.     jc    rfprep1            ; c = nothing found
  3699.     test    byte ptr buff+21,10h    ; directory?
  3700.     jz    rfprep1            ; z = not a directory
  3701.     cmp    byte ptr [bx-1],'*'    ; ends in wild card?
  3702.     je    rfprep1            ; e = yes, leave intact
  3703.     mov    word ptr [bx],0+'\'    ; yes, ensure parsed as dir
  3704.     inc    bx
  3705. rfprep1:mov    al,byte ptr [bx-1]    ; last byte, if any
  3706.     cmp    al,'.'            ; path for this dir or above (.,..)?
  3707.     jne    rfprep2            ; ne = no, use descriptor as-is
  3708.     mov    word ptr [bx],0+'\'    ; append "\"
  3709.  
  3710. rfprep2:mov    di,offset rpathname    ; path
  3711.     mov    si,offset rfilename    ; filename
  3712.     call    fparse            ; split source pattern from ds:dx
  3713.     cmp    rfilename,0        ; any file pattern?
  3714.     jne    rfprep3            ; ne = yes
  3715.     mov    word ptr rfilename,'.*' ; force wild card terminator
  3716.     mov    word ptr rfilename+2,0+'*'
  3717. rfprep3:push    dx
  3718.     mov    dx,offset rpathname    ; path
  3719.     call    strlen
  3720.     mov    di,dx
  3721.     pop    dx
  3722.     add    di,cx
  3723.     or    cx,cx            ; path present?
  3724.     jz    rfprep4            ; z = no
  3725.     cmp    byte ptr [di-1],':'    ; path ends as drive separator?
  3726.     je    rfprep4            ; e = yes
  3727.     cmp    byte ptr [di-1],'\'    ; ends in \?
  3728.     je    rfprep4            ; e = yes
  3729.     mov    word ptr [di],0+'\'    ; append trailing slash
  3730. rfprep4:pop    di
  3731.     mov    bx,offset fileio
  3732.     mov    rfileptr,bx        ; point to fileio array for dta
  3733.     mov    [bx].filename,0        ; clear name to do find first
  3734.     ret
  3735. rfprep    endp
  3736.  
  3737. ; Find next item (file or directory), given dta in rfileptr.
  3738. ; Return carry clear if found, else carry set
  3739. ; Use filekind to allow recursion and to set kind of item desired.
  3740. ; Rfileptr points to current dta (amongst array of fileio).
  3741. ; Final name is in rfileptr.filename, path is in rpathname
  3742. rgetfile proc far
  3743.     mov    bx,rfileptr        ; current dta pointer
  3744.     mov    dx,bx            ; point at dta
  3745.     mov    ah,setdma        ; set the dta address
  3746.     int    dos
  3747. rgfile1:cmp    [bx].filename,0        ; filename established?
  3748.     jnz    rgfile5            ; nz = yes, do search for next
  3749.     push    di
  3750.     mov    di,offset decbuf    ; scratch buffer
  3751.     mov    si,offset rpathname    ; path
  3752.     call    strcpy            ; path to decbuf
  3753.     mov    si,offset rfilename    ; filename pattern
  3754.     call    strcat            ; append filename
  3755.     mov    dx,di            ; search for first uses ds:dx
  3756.     pop    di
  3757.     xor    cx,cx            ; find files only
  3758.     test    findkind,1        ; want files only?
  3759.     jnz    rgfile2            ; nz = yes
  3760.     mov    cx,10h            ; directories and files
  3761. rgfile2:mov    ah,first2        ; DOS 2.0 search for first
  3762.     int    dos            ; get file's characteristics
  3763.     jc    rgfile6            ; c = failed
  3764.                     ; separate files from directories
  3765. rgfile3:test    findkind,1        ; want files only?
  3766.     jnz    rgfile4            ; nz = yes, done
  3767.     test    byte ptr [bx]+21,10h    ; directory?
  3768.     jz    rgfile5            ; z = no, try again
  3769.     cmp    [bx].filename,'.'    ; dot or dot-dot?
  3770.     je    rgfile5            ; e = yes, ignore dots, search again
  3771. rgfile4:inc    filecnt            ; file count
  3772.     clc                ; success
  3773.     ret
  3774.                     ; search for next
  3775. rgfile5:mov    dx,bx            ; dta pointer
  3776.     mov    ah,next2        ; DOS 2.0 search for next
  3777.     int    dos
  3778.     jnc    rgfile3            ; nc = success, found entry, filter
  3779.  
  3780.                     ; walk tree when out of items
  3781. rgfile6:test    findkind,4        ; recursion allowed?
  3782.     jz    rgfile9            ; z = no, done (failed)
  3783.     mov    [bx].filename,0        ; clear found name, for find first
  3784. rgfile7:call    rnxtdir            ; get next directory at this level
  3785.     jc    rgfile8            ; c = failure, none, go up tree
  3786.     call    rsubdir            ; step into this subdir
  3787.     jmp    short rgfile1        ; search again
  3788. rgfile8:call    rprvdir            ; go up one directory level, if any
  3789.     jnc    rgfile7            ; nc = got next dir at new level up
  3790. rgfile9:stc                ; c = failure
  3791.     ret
  3792. rgetfile endp
  3793.  
  3794. ; Scan current dta for next directory. If rfileptr.filename is non-zero
  3795. ;  do a search for next, else search for first.
  3796. ; rpathname has entire pattern.
  3797. ; Return carry clear on success with rfileptr.filename holding new
  3798. ; dir component, else carry set.
  3799. ; Reuse current dta.
  3800. rnxtdir proc    near
  3801.     mov    bx,rfileptr        ; current dta
  3802.     cmp    byte ptr [bx].filename,0 ; any name pattern yet?
  3803.     jne    rnxtdir3        ; ne = yes, use get next
  3804.     push    di
  3805.     mov    di,offset rpathname    ; path
  3806.     mov    dx,di
  3807.     call    strlen
  3808.     add    di,cx            ; last byte plus one
  3809.     mov    word ptr [di],'.*'    ; wildcard pattern match for all dirs
  3810.     mov    word ptr [di+2],0+'*'
  3811.     mov    cx,10h            ; find dir and files
  3812.     mov    ah,first2        ; DOS 2.0 search for first
  3813.     int    dos
  3814.     mov    byte ptr [di],0        ; remove the '*.*'
  3815.     pop    di
  3816.     jnc    rnxtdir2        ; nc = success
  3817.     ret                ; c = failed
  3818.  
  3819. rnxtdir2:test    byte ptr [bx]+21,10h    ; directory?
  3820.     jz    rnxtdir3        ; z = no, must be regular file
  3821.     cmp    [bx].filename,'.'    ; dot or dot-dot?
  3822.     je    rnxtdir3        ; e = yes, ignore, keep looking
  3823.     clc                ; say success
  3824.     ret
  3825.  
  3826. rnxtdir3:mov    dx,bx            ; dta pointer
  3827.     mov    ah,next2        ; DOS 2.0 search for next
  3828.     int    dos
  3829.     jnc    rnxtdir2        ; nc = success
  3830.     ret                ; failure to find another
  3831. rnxtdir    endp
  3832.  
  3833. ; Go up one directory level.
  3834. ; Current dta needs to be removed. rpathname has current path.
  3835. ; We need to strip off one "dir\" and pop the scan level.
  3836. ; Returns carry clear, except carry set when already at top level.
  3837. rprvdir    proc    near
  3838.     cmp    rfileptr,offset fileio    ; at top dir level now?
  3839.     ja    rprvdir1        ; a = no
  3840.     stc                ; say already at top level
  3841.     ret
  3842.  
  3843. rprvdir1:push    di
  3844.     mov    di,offset rpathname    ; path
  3845.     mov    dx,di
  3846.     call    strlen            ; length of path string
  3847.     jcxz    rprvdir3        ; no path so far
  3848.     add    di,cx            ; last path char+1
  3849.     dec    di            ; last path char
  3850.     cmp    byte ptr [di],'\'    ; ends in \ now?
  3851.     jne    rprvdir2        ; ne = no
  3852.     mov    byte ptr [di],' '    ; change to space for backscan
  3853. rprvdir2:cmp    byte ptr [di],'\'    ; at path separator?
  3854.     je    rprvdir3        ; e = yes
  3855.     cmp    byte ptr [di],':'    ; drive letter colon?
  3856.     je    rprvdir3        ; e = yes
  3857.     dec    di            ; backup til separator
  3858.     loop    rprvdir2        ; keep backing up
  3859. rprvdir3:mov    byte ptr [di+1],0    ; terminate
  3860.     sub    rfileptr,size fileiost    ; go up one dir level
  3861.     mov    dx,rfileptr
  3862.     mov    bx,dx
  3863.     mov    ah,setdma        ; set the dta address
  3864.     int    dos
  3865.     pop    di
  3866.     clc
  3867.     ret
  3868. rprvdir    endp
  3869.  
  3870. ; Step into subdirectory. [bx].filename as new dir, rpathname has current 
  3871. ; path with \ ASCIIZ termination. Changes to new dta and clears .filename.
  3872. ; Changes rpathname to contain new path addition. 
  3873. rsubdir    proc    near
  3874.     mov    bx,rfileptr
  3875.     push    di
  3876.     mov    di,offset rpathname    ; current \ terminated path
  3877.     mov    dx,di
  3878.     call    strlen            ; get length of this part
  3879.     mov    ax,cx            ; length
  3880.     lea    si,[bx].filename    ; get new directory component
  3881.     mov    dx,si
  3882.     call    strlen            ; length of addition
  3883.     add    ax,cx            ; combined length
  3884.     cmp    ax,64            ; too long?
  3885.     jae    rsubdir2        ; ae = yes
  3886.     call    strcat            ; append si to end of di
  3887.     mov    dx,di
  3888.     call    strlen
  3889.     add     di,cx
  3890.     mov    word ptr [di],0+'\'    ; terminate with \
  3891.     pop    di
  3892.     jmp    short rsubdir1
  3893. rsubdir2:stc                ; fail
  3894.     ret
  3895.  
  3896. rsubdir1:cmp    rfileptr,offset fileio + (maxdepth - 1) * size fileiost
  3897.     ja    rsubdir2        ; a = will exceed max depth
  3898.     add    rfileptr,size fileiost    ; size of structure
  3899.     mov    dx,rfileptr        ; point at dta
  3900.     mov    ah,setdma        ; set the dta address
  3901.     int    dos
  3902.     mov    bx,dx
  3903.     mov    [bx].filename,0        ; clear filename field
  3904.     clc                ; success
  3905.     ret
  3906. rsubdir    endp
  3907.  
  3908. ; Set envadr to the string following the <variable=> keyword in the DOS
  3909. ; Environment and set envlen to its length after removing leading and
  3910. ; trailing whitespace.
  3911. ; <variable> starts at ds:<valtmp>, of length cmsiz-1, and can be mixed case.
  3912. ; Return carry set if can't find the <variable=> line in the Environment.
  3913. envvar    proc    near
  3914.     push    es
  3915.     mov    bx,valtmp        ; start of variable name
  3916.     mov    cx,cmsiz        ; length of variable name, w/o ')'
  3917.     or    cx,cx            ; empty?
  3918.     jle    envvar3            ; le = nothing to look for, fail
  3919.     push    bx
  3920.     push    cx
  3921. envvar1:mov    al,byte ptr [bx]    ; scan variable name in our buffer
  3922.     cmp    al,'a'            ; lower case
  3923.     jb    envvar2            ; b = no
  3924.     cmp    al,'z'            ; still in lower case range?
  3925.     ja    envvar2            ; a = no
  3926.     and    al,not 20h        ; convert to DOS's upper case
  3927.     mov    byte ptr [bx],al    ; replace char
  3928. envvar2:inc    bx
  3929.     loop    envvar1
  3930.     pop    cx
  3931.     pop    bx            ; find "<variable>=" in Environment
  3932.     call    fgetenv            ; dx = offset in Environment of "="
  3933.     jnc    envvar4            ; nc = success
  3934.  
  3935.     push    bx
  3936.     push    cx
  3937. envvar1a:mov    al,byte ptr [bx]    ; scan variable name in our buffer
  3938.     cmp    al,'A'            ; upper case
  3939.     jb    envvar2a        ; b = no
  3940.     cmp    al,'Z'            ; still in upper case range?
  3941.     ja    envvar2a        ; a = no
  3942.     or    al,20h            ; convert to DOS's lower case
  3943.     mov    byte ptr [bx],al    ; replace char
  3944. envvar2a:inc    bx
  3945.     loop    envvar1a
  3946.     pop    cx
  3947.     pop    bx            ; find "<variable>=" in Environment
  3948.     call    fgetenv            ; dx = offset in Environment of "="
  3949.     jnc    envvar4            ; nc = success
  3950.  
  3951. envvar3:pop    es            ; no such variable
  3952.     stc                ; c = failure
  3953.     ret
  3954. ; dx has offset in Environment of char "="
  3955. ; ds:valtmp is start of variable name, cmsiz is length + 1 of the name.
  3956. ; Return seg:offset and length variables so we can copy from there in valtoa
  3957. envvar4:push    di
  3958.     push    si
  3959.     xor    ax,ax
  3960.     mov    word ptr envadr,ax    ; offset in env of variable's string
  3961.     mov    word ptr envadr+2,ax    ; seg of same
  3962.     mov    envlen,ax        ; length of string
  3963.     mov    es,psp            ; our Prog Seg Prefix segment
  3964.     mov    ax,es:word ptr[env]    ; pick up Environment address
  3965.     mov    es,ax            ; set es: to Environment segment
  3966.     mov    di,dx            ; line scan pointer
  3967.     cmp    byte ptr es:[di],'='    ; did we stop on this?
  3968.     jne    envvar5            ; ne = no
  3969.     inc    di            ; skip the "=" char
  3970. envvar5:mov    al,es:[di]        ; scan over leading white space
  3971.     inc    di
  3972.     or    al,al            ; end of line terminator?
  3973.     jz    envvarf            ; z = yes, fail
  3974.     cmp    al,TAB            ; HT?
  3975.     jne    envvar6            ; ne = no
  3976.     mov    al,' '            ; HT becomes a space
  3977. envvar6:cmp    al,' '            ; white space?
  3978.     je    envvar5            ; scan off white space
  3979.     dec    di            ; backup to non-white char
  3980.     mov    word ptr envadr,di    ; offset of string in Environment
  3981.     mov    word ptr envadr+2,es    ; seg of string in Environment
  3982.     mov    si,di            ; remember starting offset here
  3983.                     ; remove trailing spaces from string
  3984.     xor    al,al            ; a null
  3985.     mov    cx,127            ; max length to search
  3986.     cld
  3987.     repne    scasb            ; skip over non-nulls
  3988.     dec    di            ; backup to null
  3989.     dec    di            ; backup to last string char
  3990.     mov    cx,di            ; ending offset
  3991.     inc    cx            ; count the char
  3992.     sub    cx,si            ; minus starting offset yields length
  3993.     jcxz    envvar9            ; z = empty string
  3994. envvar7:mov    al,es:[di]        ; last char
  3995.     dec    di            ; backup one char
  3996.     cmp    al,' '            ; space?
  3997.     je    envvar8            ; e = yes
  3998.     cmp    al,TAB            ; HT?
  3999.     jne    envvar9            ; ne = no, end of white space
  4000. envvar8:loop    envvar7            ; keep looking
  4001. envvar9:mov    envlen,cx        ; store the length
  4002.     clc                ; say success
  4003.     jmp    short envvarx
  4004. envvarf:stc                ; say failure
  4005. envvarx:pop    si
  4006.     pop    di
  4007.     pop    es
  4008.     ret
  4009. envvar    endp
  4010.  
  4011. ; Worker for Remote Query System name in mssser.
  4012. ; Enter with variable name in decbuf+6 et seq, name length byte in decbuf+5
  4013. ; with ' ' bias.
  4014. fqryenv    proc    far
  4015.     mov    valtmp,offset decbuf+6    ; start of variable name
  4016.     mov    cl,decbuf+5        ; name length plus bias
  4017.     sub    cl,' '            ; remove bias
  4018.     xor    ch,ch
  4019.     mov    cmsiz,cx        ; name length
  4020.     call    envvar            ; get address of environmental string
  4021.     jnc    fqryenv1        ; nc = success
  4022.     xor    cx,cx            ; failure
  4023.     ret
  4024.  
  4025. fqryenv1:mov    cx,envlen        ; length of string in environment
  4026.     push    es
  4027.     push    ds
  4028.     lds    si,envadr        ; pointer to string
  4029.     mov    di,seg encbuf        ; destination buffer
  4030.     mov    es,di
  4031.     mov    di,offset encbuf
  4032.     cld
  4033.     rep    movsb            ; copy string
  4034.     pop    ds
  4035.     pop    es
  4036.     mov    cx,envlen        ; string length again
  4037.     ret
  4038. fqryenv    endp
  4039.  
  4040. ; Read chars from Take file, keyboard, or redirected stdin. Edit and remove
  4041. ; BS & DEL, Tab becomes space, act on Control-C, pass Control-U and Control-W.
  4042. ; Do echoing unless comand.cmquiet is non-zero. Do semicolon comments in Take
  4043. ; and indirect stdin files (\; means literal semicolon). Return char in AL.
  4044. CMGETC    proc    near            ; Basic raw character reader
  4045.     mov    read_source,0        ; assume direct reading
  4046.     mov    endedbackslash,0    ; clear end of sub scan flag
  4047. cmget01:cmp    comand.cmdirect,0    ; read directly?
  4048.     jne    cmget02            ; ne = yes
  4049.     cmp    taklev,0        ; in a Take file?
  4050.     jne    cmget1            ; ne = yes, do Take reader section
  4051. cmget02:call    isdev            ; is stdin a device or a file?
  4052.     jnc    cmget20            ; nc = file (redirection of stdin)
  4053.     jmp    cmget10            ; c = device, do separately
  4054.  
  4055. cmget20:call    iseof            ; see if file is empty
  4056.     jc    cmget21            ; c = EOF on disk file
  4057.     mov    ah,coninq        ; read the char from file, not device
  4058.     int    dos
  4059.     cmp    al,cr            ; is it a cr?
  4060.     je    cmget01            ; yes, ignore and read next char
  4061.     cmp    al,ctlz            ; Control-Z?
  4062.     je    cmget21            ; e = yes, same as EOF here
  4063.     cmp    al,lf            ; LF's end lines from disk files
  4064.     jne    cmget12            ; ne = not LF, pass along as is
  4065.     mov    al,cr            ; make LF a CR for this parser
  4066.     call    iseof            ; see if this is the last char in file
  4067.     jnc    cmget12            ; nc = not EOF, process new CR
  4068. cmget21:mov    flags.extflg,1        ; EOF on disk file, set exit flag
  4069.     jmp    cmget12            ; do echoing and return
  4070.  
  4071. cmget1:    push    bx            ; read from Take file
  4072.     mov    bx,takadr        ; offset of this Take structure
  4073.     mov    al,[bx].taktyp
  4074.     mov    read_source,al
  4075.     cmp    [bx].takcnt,0        ; bytes remaining in Take buffer
  4076.     jne    cmget4            ; ne = not empty
  4077.     cmp    al,take_file        ; type of Take (file?)
  4078.     jne    cmget3            ; ne = no (macro)
  4079.     call    takrd            ; read another buffer
  4080.     cmp    [bx].takcnt,0        ; anything in the buffer?
  4081.     jne    cmget4            ; ne = yes
  4082. cmget3:    pop    bx            ; clear stack
  4083.     jmp    short cmget5        ; close the Take file
  4084.  
  4085. cmget4:    push    si            ; read from Take non-empty buffer
  4086.     push    es
  4087.     mov    es,[bx].takbuf        ; segment of Take buffer
  4088.     mov    si,[bx].takptr        ; current offset in Take buffer
  4089.     mov    al,es:[si]        ; read a char from Take buffer
  4090.     pop    es
  4091.     inc    si
  4092.     mov    [bx].takptr,si        ; move buffer pointer
  4093.     pop    si
  4094.     dec    [bx].takcnt        ; decrease number of bytes remaining
  4095.     cmp    read_source,take_sub    ; substitution macro?
  4096.     jne    cmget4b            ; ne = no
  4097.     cmp    [bx].takcnt,0        ; read last byte?
  4098.     jne    cmget4b            ; ne = no
  4099.     cmp    al,'\'            ; ended on a backsash?
  4100.     jne    cmget4b            ; ne = no
  4101.     mov    endedbackslash,al    ; setup break end of sub scan
  4102. cmget4b:pop    bx
  4103.  
  4104.     cmp    read_source,take_file    ; kind of Take/macro, file?
  4105.     jne    cmget12            ; ne = no, not a file
  4106.     cmp    al,LF            ; LF?
  4107.     je    cmget01            ; e = yes, ignore
  4108.     cmp    al,ctlz            ; Control-Z?
  4109.     jne    cmget12            ; ne = no, else ^Z = EOF
  4110.  
  4111. cmget5:    push    bx            ; end of file on Take buffer
  4112.     mov    bx,takadr        ; offset of this Take structure
  4113.     mov    ah,[bx].takattr        ; save kind of autoCR
  4114.     pop    bx
  4115.     cmp    read_source,take_sub    ; text subsititution?
  4116.     je    cmget5a            ; e = yes, cannot keep open
  4117.     cmp    comand.cmkeep,0        ; keep Take/macro open after eof?
  4118.     jne    cmget5b            ; ne = yes
  4119. cmget5a:
  4120.     push    ax
  4121.     push    bx
  4122.     mov    bx,takadr
  4123. cmget5c:mov    al,[bx].takinvoke    ; take level of last DO or command
  4124.     call    ftakclos        ; close take file, saves reg ax
  4125.     mov    bx,takadr        ; next Take
  4126.     test    [bx].takattr,take_autocr ; if macro needs special ending
  4127.     jnz    cmget5d            ; nz = it does, do not autoclose here
  4128.     cmp    [bx].takcnt,0        ; empty?
  4129.     jne    cmget5d            ; ne = no, done closing
  4130.     cmp    taklev,0        ; any Take levels left?
  4131.     je    cmget5d            ; e = no
  4132.     cmp    taklev,al        ; still in last DO?
  4133.     jae    cmget5c            ; ae = yes, try closing again
  4134. cmget5d:pop    bx
  4135.     pop    ax
  4136.  
  4137.     test    ah,take_autocr        ; add CR on EOF?
  4138.     jnz    cmget5b            ; nz = yes
  4139.     jmp    cmgetc            ; internal macros have no auto CR
  4140. cmget5b:mov    al,CR            ; report CR as last char
  4141.     jmp    short cmget12
  4142.                     ; read from tty device
  4143. cmget10:mov    ah,coninq        ; Get a char from device, not file
  4144.     int    dos            ;  with no echoing
  4145.     or    al,al
  4146.     jnz    cmget11            ; ignore null bytes of special keys
  4147.     int    dos            ; read and discard scan code byte
  4148.     jmp    short cmget10        ; try again
  4149. cmget11:cmp    al,LF            ; LF?
  4150.     jne    cmget12            ; ne = no
  4151.     mov    al,CR            ; replace interactive LF with CR
  4152.  
  4153. cmget12:cmp    al,'C'and 1Fh        ; Control-C?
  4154.     je    cmget14            ; e = yes
  4155.     cmp    al,TAB            ; tab is replaced by space
  4156.     jne    cmget13            ; ne = not tab
  4157.     mov    al,' '
  4158. cmget13:ret                ; normal exit, char is in AL
  4159.  
  4160. cmget14:cmp    in_showprompt,0        ; Non-zero when in showprompt
  4161.     jne    cmget13            ; ne = yes, no editing
  4162.     test    read_source,take_sub+take_macro    ; substitution or macro?
  4163.     jnz    cmget13            ; nz = yes, do not edit line
  4164.     mov    bracecnt,0
  4165.     mov    ah,prstr        ; Control-C handler
  4166.     push    dx
  4167.     mov    dx,offset ctcmsg    ; show Control-C
  4168.     int    dos
  4169.     pop    dx
  4170.     mov    flags.cxzflg,'C'    ; tell others the news
  4171.     jmp    cmexit            ; fail immediately via longjmp
  4172. cmgetc    endp
  4173.  
  4174. ; Read chars from user (cmgetc). Detect terminators. Reads from buffer
  4175. ; cmbuf. Set read pointer cmrptr to next free buffer byte if
  4176. ; char is not a terminator: chars CR, LF, FF, '?' (returns carry set for
  4177. ; terminators). Do ^U, ^W editing, convert FF to CR plus clear screen.
  4178. ; Edit "-<cr>" as line continuation, "\-<cr>" as "-<end of line>".
  4179. ; Return char in AH.
  4180. CMINBF    proc    near            ; Buffer reader, final editor
  4181.     cmp    cmwptr,offset cmdbuf+size cmdbuf-3 ; max buffer size - 3
  4182.     jb    cminb1            ; b = not full for writing
  4183.     mov    ah,conout        ; almost full, notify user
  4184.     push    dx
  4185.     mov    dl,bell
  4186.     int    dos
  4187.     pop    dx
  4188.     cmp    cmrptr,offset cmdbuf+size cmdbuf ; reading beyond buffer?
  4189.     jb    cminb1            ; b = no
  4190. cminb0:    mov    al,taklev        ; current Take level
  4191.     or    al,al
  4192.     jz    cminb0a            ; z = none open, exit parse error
  4193.     call    ftakclos        ; close Take file
  4194.     jmp    short cminb0        ; do all
  4195. cminb0a:mov    ah,prstr
  4196.     mov    dx,offset cmer09    ; command too long
  4197.     int    dos
  4198.     jmp    prserr            ; overflow = parse error
  4199.  
  4200. cminb1:    push    bx
  4201.     mov    bx,cmrptr        ; read pointer
  4202.     mov    ah,[bx]            ; get current command char while here
  4203.     cmp    bx,cmwptr        ; do we need to read more?
  4204.     pop    bx            ; no if cmrptr < cmwptr
  4205.     jb    cminb2            ; b: cmrptr < cmwptr (have extra here)
  4206.     call    cmgetc            ; no readahead, read another into al
  4207.     mov    ah,al            ; keep char in 'ah'
  4208.     cmp    read_source,take_sub    ; substitution?
  4209.     je    cminb1a            ; e = yes
  4210.     cmp    cmrawptr,offset rawbuf+cmdblen ; overflow check on rawbuf
  4211.     ja    cminb0            ; a = overflowed
  4212.     push    bx
  4213.     mov    bx,cmrawptr        ; where raw writes go (inc remakes)
  4214.     mov    [bx],ah            ; store byte
  4215.     inc    cmrawptr
  4216.     pop    bx
  4217.  
  4218. cminb1a:push    bx
  4219.     mov    bx,cmwptr        ; get the pointer into the buffer
  4220.     mov    [bx],ah            ; put it in the buffer
  4221.     inc    bx
  4222.     mov    cmwptr,bx        ; inc write pointer
  4223.     pop    bx
  4224.     jmp    short cminb1        ; call cmgetc until cmwptr >= cmrptr
  4225.                     ; Char to be delivered is in ah
  4226. cminb2:    cmp    in_showprompt,0        ; Non-zero when in showprompt
  4227.     jne    cminb5            ; ne = yes, no editing
  4228.     test    read_source,take_sub+take_macro    ; substitution or macro?
  4229.     jnz    cminb4            ; nz = yes, do not edit line
  4230.     cmp    ah,'W' and 1fh        ; is it a ^W?
  4231.     jne    cminb3            ; ne = no
  4232.     call    cntrlw            ; kill the previous word
  4233.     jmp    repars            ; need a new command scan (cleans stk)
  4234.  
  4235. cminb3:    cmp    ah,'U' and 1fh        ; is it a ^U?
  4236.     jne    cminb3a            ; ne = no
  4237.     mov    cmwptr,offset cmdbuf    ; reset buffer write pointer
  4238.     mov    cmrptr,offset cmdbuf
  4239.     mov    cmrawptr,offset rawbuf    ; clear raw buffer
  4240.     jmp    repars            ; go start over (cleans stack)
  4241.                     ; BS and DEL
  4242. cminb3a:cmp    ah,DEL            ; delete code?
  4243.     je    cminb3b            ; e = yes
  4244.     cmp    ah,BS            ; Backspace (a delete operator)?
  4245.     jne    cminb4            ; ne = no
  4246. cminb3b:call    bufdel            ; delete char from buffer
  4247.     jc    cminb3c            ; c = did erasure
  4248.     jmp    cminbf            ; no erasure, ignore BS, get more
  4249. cminb3c:jmp    repars            ; could have deleted previous token
  4250.  
  4251. cminb4:    push    bx            ; look for hyphen or \hyphen
  4252.     cmp    ah,CR            ; check for hyphen line continuation
  4253.     jne    cminb4b            ; ne = not end of line
  4254.     call    crprot            ; protect CR?
  4255.     jnc    cminb4e            ; nc = no
  4256.                     ; cmtxt {continued lines} section
  4257.     dec    cmwptr            ; back over CR for both buffers
  4258.     dec    cmrptr
  4259.     mov    bx,cmwptr
  4260.     mov    ah,','            ; replace CR with comma
  4261.     mov    [bx-1],ah
  4262.     mov    bx,cmrawptr
  4263.     mov    [bx-1],ah
  4264.     mov    bx,cmrptr
  4265.     mov    [bx],ah
  4266.                     ; deal with echoing
  4267.     cmp    comand.cmquiet,0    ; quiet mode?
  4268.     jne    cminb4g            ; yes, skip echoing
  4269.     cmp    comand.cmdirect,0    ; direct reading?
  4270.     jne    cminb4f            ; ne = yes, do echoing
  4271.     cmp    taklev,0        ; in a take file?
  4272.     je    cminb4f            ; e = no
  4273.     cmp    read_source,take_comand    ; command reread macro?
  4274.     je    cminb4f            ; e = yes, echo
  4275.     cmp    flags.takflg,0        ; echo take file?
  4276.     je    cminb4g            ; e = no
  4277.     cmp    read_source,take_sub    ; don't echo string substitution
  4278.     je    cminb4g
  4279. cminb4f:
  4280.     push    ax
  4281.     push    dx
  4282.     mov    dl,ah
  4283.     mov    ah,conout        ; echo comma to screen now
  4284.     int     dos
  4285.     mov    ah,prstr        ; and then visually break the line
  4286.     mov    dx,offset crlf
  4287.     int    dos
  4288.     pop    dx
  4289.     pop    ax
  4290. cminb4g:pop    bx
  4291.     clc                ; returns ah = comma
  4292.     ret
  4293.                     ; start regular hyphenation part
  4294. cminb4e:mov    bx,cmwptr        ; get the pointer into the buffer
  4295.     cmp    bx,offset cmdbuf+2    ; do we have a previous char?
  4296.     jb    cminb4b            ; b = no
  4297.     cmp    byte ptr[bx-2],'-'    ; previous char was a hyphen?
  4298.     jne    cminb4b            ; ne = no
  4299.     pop    bx
  4300.     sub    cmwptr,2        ; back over -<cr> for both buffers
  4301.     sub    cmrawptr,2
  4302.     jmp    repars            ; reparse what we now have
  4303. cminb4b:pop    bx
  4304.                     ; Echoing done here
  4305.     cmp    comand.cmquiet,0    ; quiet mode?
  4306.     jne    cminb5            ; yes, skip echoing
  4307.     cmp    comand.cmdirect,0    ; direct reading?
  4308.     jne    cminb4a            ; ne = yes, do echoing
  4309.     cmp    taklev,0        ; in a take file?
  4310.     je    cminb4a            ; e = no
  4311.  
  4312.     cmp    read_source,take_comand    ; command reread macro?
  4313.     je    cminb4a            ; e = yes, echo
  4314.     cmp    flags.takflg,0        ; echo take file?
  4315.     je    cminb5            ; e = no
  4316.     cmp    read_source,take_sub    ; don't echo string substitution
  4317.     je    cminb5
  4318.  
  4319. cminb4a:push    ax            ; save the char
  4320.     cmp    ah,' '            ; printable?
  4321.     jae    cminb4c            ; yes, no translation needed
  4322.     cmp    ah,CR            ; this is printable
  4323.     je    cminb4c
  4324.     cmp    ah,LF
  4325.     je    cminb4c
  4326.     cmp    ah,ESCAPE        ; escape?
  4327.     je    cminb4d            ; do not echo this character
  4328.     push    ax            ; show controls as caret char
  4329.     push    dx
  4330.     mov    dl,5eh            ; caret
  4331.     mov    ah,conout
  4332.     int    dos
  4333.     pop    dx
  4334.     pop    ax
  4335.     add    ah,'A'-1        ; make control code printable
  4336. cminb4c:push    dx
  4337.     mov    dl,ah
  4338.     mov    ah,conout
  4339.     int    dos            ; echo it ourselves
  4340.     pop    dx
  4341. cminb4d:pop    ax            ; and return char in ah
  4342.  
  4343. cminb5:    cmp    ah,CR            ; carriage return?
  4344.     je    cminb6            ; e = yes
  4345.     cmp    ah,LF            ; line feed?
  4346.     je    cminb6
  4347.     cmp    ah,FF            ; formfeed?
  4348.     jne    cminb7            ; none of the above, report bare char
  4349.     call    fcmblnk            ; FF: clear the screen and
  4350.     push    bx
  4351.     push    cx
  4352.     push    dx
  4353.     call    flocate            ; Home the cursor
  4354.     mov    bx,cmwptr        ; make the FF parse like a cr
  4355.     mov    byte ptr [bx-1],cr    ; pretend a carriage return were typed
  4356.     pop    dx
  4357.     pop    cx
  4358.     pop    bx
  4359. cminb6: cmp    cmwptr,offset cmdbuf    ; parsed any chars yet?
  4360.     jne    cminb7            ; ne = yes
  4361.     cmp    comand.cmcr,0        ; bare cr's allowed?
  4362.     jne    cminb7            ; ne = yes
  4363.     jmp    prserr            ; If not, just start over
  4364. cminb7:    clc
  4365.     ret
  4366. cminbf    endp
  4367.  
  4368. ; Read chars from cminbf. Cmrptr points to next char to be read.
  4369. ; Compresses repeated spaces if cmsflg is non-zero. Exit with cmrptr pointing
  4370. ; at a terminator or otherwise at next free slot.
  4371. ; Non-space then space acts as a terminator but cmrptr is incremented.
  4372. ; Substitution variables, '\%x', and \numbers are detected and expanded.
  4373. ; Return char in AH.
  4374. ; If a \blah parse fails then report the \ to the caller and start over, 
  4375. ; without echo, on the char following that \. Replay_skip does the \
  4376. ; passage without calling subst, replay_cnt rereads bytes after it.
  4377. CMGTCH    proc    near            ; return char in AH, from rescan buf
  4378.     cmp    replay_cnt,0        ; starting a replay?
  4379.     je    cmgtc5            ; e = no
  4380.     mov    replay_skip,1        ; set latch to skip subst on 1st byte
  4381. cmgtc5:    cmp    in_reparse,0        ; replaying material?
  4382.     je    cmgtc0            ; e = no
  4383.     call    remake            ; remake command line as macro
  4384.  
  4385. cmgtc0:    cmp    replay_cnt,0        ; replay with no echo?
  4386.     jne    cmgtc1            ; ne = yes, have in cmdbuf now
  4387.     call    cminbf            ; get char from buffer or user
  4388. cmgtc1:    push    bx
  4389.     mov    bx,cmrptr        ; get read pointer into the buffer
  4390.     mov    ah,[bx]            ; read the next char
  4391.     inc    bx
  4392.     mov    cmrptr,bx        ; where to read next time
  4393.     cmp    replay_cnt,0        ; replaying?
  4394.     jne    cmgtc6            ; ne = yes, don't take the branch
  4395.     cmp    bx,cmwptr        ; still reading analyzed text?
  4396. cmgtc6:    pop    bx
  4397.     jb    cmgtc2            ; b = yes, don't reexpand
  4398.     cmp    replay_skip,0        ; replay latch set?
  4399.     je    cmgtc7            ; e = no, re-read new bytes in subst
  4400.     mov    replay_skip,0         ; clear latch after the first re-read byte
  4401.     jmp    cmgtc2            ; and skip subst on that first byte
  4402.     cmp    replay_cnt,0        ; replaying?
  4403.     jne    cmgtc2            ; ne = yes
  4404. cmgtc7:
  4405.     call    subst            ; examine for text substitution
  4406.     jc    cmgtch            ; c = reread input material
  4407.     cmp    report_binary,0        ; report binary number directly?
  4408.     je    cmgtc2b            ; e = no
  4409.     mov    report_binary,0        ; clear flag
  4410.     clc                ; value is in ah, non-terminator
  4411.     ret
  4412.  
  4413. cmgtc2:    push    bx
  4414.     mov    bx,replay_cnt        ; count down replay bytes
  4415.     or    bx,bx    
  4416.     jz    cmgtc2a            ; z = empty 
  4417.     dec    replay_cnt
  4418. cmgtc2a:pop    bx
  4419. cmgtc2b:cmp    ah,','            ; comma?
  4420.     jne    cmgtc2c            ; ne = no
  4421.     cmp    comand.cmcomma,0    ; comma is space equivalent?
  4422.     je    cmgtc2c            ; e = no
  4423.     mov    ah,' '            ; convert comma to space
  4424. cmgtc2c:cmp    ah,' '            ; space?
  4425.     jne    cmgtc3            ; ne = no
  4426.     cmp    cmsiz,0            ; started user data?
  4427.     je    cmgtc2d            ; e = no, this is a leading space
  4428.     cmp    cmkind,cmline        ; doing full lines of text?
  4429.     je    cmgtc3            ; e = yes, retain all spaces
  4430. cmgtc2d:cmp    bracecnt,0        ; are we within braces?
  4431.     jne    cmgtc3            ; ne = yes, treat space as literal
  4432.     cmp    cmsflg,0        ; space flag, was last char a space?
  4433.     jne    cmgtch            ; ne = yes, get another char
  4434.     mov    cmsflg,0FFH        ; set the space(s)-seen flag
  4435.     stc                ; set carry for terminator
  4436.     ret                ; return space as a terminator
  4437. cmgtc3: mov    cmsflg,0        ; clear the space-seen flag
  4438.     cmp    ah,braceop        ; opening brace?
  4439.     jne    cmgtc3b            ; ne = no
  4440.     inc    bracecnt        ; count it
  4441.     jmp    short cmgtc3c
  4442. cmgtc3b:cmp    ah,bracecl        ; closing brace?
  4443.     jne    cmgtc3c            ; ne = no
  4444.     sub    bracecnt,1        ; count down and get a sign bit
  4445.     jns    cmgtc3c            ; ns = no underflow
  4446.     mov    bracecnt,0        ; catch underflows
  4447.                     ; terminators remain in buffer but
  4448.                     ;  are ready to be overwritten
  4449. cmgtc3c:test    read_source,take_sub    ; substitution macro?
  4450.     jnz    cmgtc3d            ; nz = yes, pass all bytes as data
  4451.     cmp    ah,escape        ; Escape expander?
  4452.     jne    cmgtc3e            ; ne = no
  4453.     cmp    read_source,0        ; reading from keyboard directly?
  4454.     jne    cmgtc4            ; ne = no, escape is regular char
  4455.  
  4456. cmgtc3e:cmp    ah,'?'            ; is the user curious?
  4457.     jne    cmgtc3a            ; ne = no
  4458.     cmp    taklev,0        ; in a Take file?
  4459.     jne    cmgtc3d            ; ne = yes, make query ordinary char
  4460.     je    cmgtc4
  4461. cmgtc3a:cmp    ah,CR            ; these terminators?
  4462.     je    cmgtc4            ; e = yes
  4463.     cmp    ah,LF
  4464.     je    cmgtc4
  4465.     cmp    ah,FF
  4466.     je    cmgtc4
  4467.     cmp    ah,ESCAPE
  4468.     je    cmgtc4
  4469. cmgtc3d:clc                ; carry clear for non-terminator
  4470.     ret
  4471. cmgtc4:    dec    cmrptr            ; point at terminating char
  4472.     stc                ; set carry to say it is a terminator
  4473.     ret
  4474. cmgtch    endp
  4475.  
  4476. ; Reset cmdbuf write pointer (cmwptr) to where the read pointer
  4477. ; (cmrptr) is now. Discards material not yet read.
  4478. bufreset proc    near
  4479.     push    cmrptr            ; where next visible char is read
  4480.     push    ax            ; count removed curly braces
  4481.     push    si
  4482.     mov    si,cmrptr        ; where to look
  4483.     mov    cx,cmwptr        ; last place being removed
  4484.     xor    ah,ah            ; get a null
  4485.     sub    cx,si            ; length to examine
  4486.     jle    bufres3a        ; le = nothing to scan
  4487.     cld
  4488. bufres1:lodsb
  4489.     cmp    al,braceop        ; opening brace, counted already?
  4490.     jne    bufres2            ; ne = no
  4491.     dec    bracecnt        ; uncount it
  4492.     jmp    short bufres3
  4493. bufres2:cmp    al,bracecl        ; closing brace, counted already?
  4494.     jne    bufres3            ; jne = no
  4495.     inc    bracecnt        ; uncount it
  4496. bufres3:loop    bufres1
  4497. bufres3a:cmp    bracecnt,ah        ; negative?
  4498.     jge    bufres4            ; ge = no
  4499.     mov    bracecnt,ah
  4500. bufres4:mov    word ptr [si],0        ; terminate buffer
  4501.     pop    si
  4502.     pop    ax
  4503.     pop    cmwptr            ; where new char goes in buffer
  4504.     ret
  4505. bufreset endp
  4506.  
  4507. ; Delete character from screen and adjust buffer. Returns carry clear if
  4508. ; no erasure, carry set otherwise.
  4509. bufdel    proc    near
  4510.     cmp    read_source,take_sub    ; substitution macro?
  4511.     je    bufdel0            ; e = yes, does not change cmrawptr
  4512.     cmp    cmrawptr,offset rawbuf    ; anything written to base level buf?
  4513.     ja    bufdel7            ; a = yes
  4514.     mov    cmrawptr,offset rawbuf    ; safety
  4515.     mov    ax,offset cmdbuf
  4516.     mov    cmrptr,ax
  4517.     mov    cmwptr,ax
  4518.     clc                ; do nothing
  4519.     ret
  4520. bufdel7:dec    cmrawptr        ; backup one byte
  4521.     push    bx
  4522.     mov    bx,cmrawptr
  4523.     cmp    byte ptr [bx],BS    ; last read was BackSpace?
  4524.     pop    bx
  4525.     jne    bufdel8            ; ne = not BS
  4526.     dec    cmrawptr        ; backup write pointer over BS too
  4527. bufdel8:cmp    cmrawptr,offset rawbuf    ; sanity check, back too far?
  4528.     jae    bufdel9            ; ae = not too far
  4529.     mov    cmrawptr,offset rawbuf    ; reset to start of buffer
  4530. bufdel9:cmp    comand.cmdirect,0    ; doing direct reading?
  4531.     jne    bufdel0            ; ne = yes
  4532.     mov    ax,offset cmdbuf
  4533.     mov    cmwptr,ax        ; reset normal parse buffer
  4534.     mov    cmrptr,ax
  4535.     mov    bx,ax
  4536.     mov    word ptr [bx],0        ; terminate buffer
  4537.     mov    in_reparse,1        ; say need to replay from rawbuf
  4538.     jmp    repars            ; reparse command
  4539.  
  4540. bufdel0:cmp    cmrptr,offset cmdbuf    ; at start of buffer now?
  4541.     jbe    bufdel5            ; be = yes
  4542.     push    ax
  4543.     push    si
  4544.     mov    si,cmrptr
  4545.     mov    al,[si]
  4546.     dec    si
  4547.     xor    ah,ah            ; get a null
  4548.     cmp    al,braceop        ; opening brace, counted already?
  4549.     jne    bufdel1            ; ne = no
  4550.     dec    bracecnt        ; uncount it
  4551.     jmp    short bufdel2
  4552. bufdel1:cmp    al,bracecl        ; closing brace?
  4553.     jne    bufdel2            ; ne = no
  4554.     inc    bracecnt        ; uncount it
  4555. bufdel2:cmp    bracecnt,ah        ; negative?
  4556.     jge    bufdel3            ; ge = no
  4557.     mov    bracecnt,ah
  4558. bufdel3:pop    si
  4559.     pop    ax
  4560.     mov    replay_cnt,0        ; clear protected replay byte count
  4561.     dec    cmrptr            ; remove previous char from buffer
  4562.     cmp    cmrptr,offset cmdbuf    ; back too far?
  4563.     jae    bufdel4            ; ae = no, material can be erased
  4564.     mov    cmrptr,offset cmdbuf    ; set to start of buffer
  4565.     mov    bracecnt,ah        ; ensure this is now cleared
  4566. bufdel5:call    bufreset        ; reset buffer
  4567.     clc                ; say no erasure
  4568.     ret
  4569. bufdel4:call    bufreset        ; reset buffer
  4570.     stc                ; say did erasure
  4571.     ret
  4572. bufdel    endp
  4573.  
  4574. ; Remake the current command line, omitting substitution macro contents,
  4575. ; as a macro (kind is comand, almost "macro"). Current command line is
  4576. ; in rawbuf, with cmrawptr pointing next place to write.
  4577. ; Closes currently active substitution macros.
  4578. remake    proc    near
  4579.     push    bx
  4580.     push    cx
  4581.     push    dx
  4582.     push    di
  4583.     mov    cmrptr,offset cmdbuf
  4584.     mov    cmwptr,offset cmdbuf    ; clear work buffer
  4585.     mov    cx,cmrawptr        ; where to write next
  4586.     mov    cmrawptr,offset rawbuf
  4587.     sub    cx,offset rawbuf    ; length of string
  4588.     jle    remake3            ; le = nothing to do
  4589. remake1:cmp    taklev,0        ; in a take file?
  4590.     je    remake2            ; e = no
  4591.     mov    bx,takadr
  4592.     cmp    [bx].taktyp,take_sub    ; sub macro?
  4593.     jne    remake2            ; ne = no
  4594.     push    cx
  4595.     call    ftakclos        ; close sub macros
  4596.     pop    cx
  4597.     jmp    short remake1        ; next victim
  4598. remake2:call    takopen_macro        ; open take as macro (comand kind)
  4599.     mov    bx,takadr        ; pointer to new Take structure
  4600.     mov    [bx].taktyp,take_comand ; that's us
  4601.     and    [bx].takattr,not take_autocr ; no CR at end of macro please
  4602.     mov    [bx].takcnt,cx        ; number of unread bytes
  4603.     mov    [bx].takbuf,seg rawbuf    ; segment of Take buffer
  4604.     mov    cx,offset rawbuf
  4605.     mov    [bx].takptr,cx         ; offset to read from
  4606.     mov    cmrawptr,cx
  4607. remake3:mov    in_reparse,0        ; clear our flag
  4608.     mov    bracecnt,0
  4609.     mov    replay_cnt,0
  4610.     mov    comand.cmkeep,0
  4611.     pop    di
  4612.     pop    dx
  4613.     pop    cx
  4614.     pop    bx
  4615.     ret
  4616. remake endp
  4617.  
  4618. ; Come here is user types ^W when during input. Remove word from buffer.
  4619. cntrlw    proc    near
  4620.     push    ax
  4621.     push    cx
  4622.     push    dx
  4623.     call    bufreset        ; truncate buffer at cmrptr
  4624.     mov    si,cmwptr        ; assumed source pointer
  4625.     mov    cx,cmrptr        ; read pointer
  4626.     sub    cx,offset cmdbuf    ; compute chars in buffer
  4627.     cmp    comand.cmdirect,0    ; direct reading?
  4628.     jne    cntrl3            ; ne = yes, only one buffer
  4629.  
  4630.     dec    cmrawptr        ; back over ^W
  4631.     mov    si,cmrawptr        ; use raw buf as source pointer
  4632.     mov    cx,si            ; point at last written byte
  4633.     sub    cx,offset rawbuf    ; count of bytes before ^W
  4634.     jle    ctlw2            ; le = none
  4635. cntrl3:    clc                ; say have not yet modified line
  4636.     jcxz    ctlw2            ; z = nothing to do, exit no-carry
  4637.     push    es
  4638.     std                ; scan backward
  4639.     mov    ax,ds
  4640.     mov    es,ax            ; point to the data are
  4641.     mov    di,si            ; looking from here
  4642.     dec    di
  4643.     mov    al,' '
  4644.     repe    scasb            ; look for non-space
  4645.     je    ctlw1            ; all spaces, nothing to do
  4646.     inc    di            ; move back to non-space
  4647.     inc    cx
  4648.     repne    scasb            ; look for a space
  4649.     jne    ctlw1            ; no space, leave ptrs alone
  4650.     inc    di
  4651.     inc    cx            ; skip back over space
  4652. ctlw1:    inc    di
  4653.     pop    es
  4654.     cld                ; reset    direction flag
  4655.     mov    cmwptr,di        ; update pointer
  4656.     cmp    comand.cmdirect,0    ; direct reading?
  4657.     jne    ctlw2            ; ne = yes, only one buffer
  4658.     inc    di            ; leave a char for bufdel
  4659.     mov    cmrawptr,di        ; setup delete initial byte of word
  4660.     call     bufdel            ; delete item, reparse
  4661. ctlw2:    pop    dx
  4662.     pop    cx
  4663.     pop    ax
  4664.     ret
  4665. cntrlw    endp
  4666.  
  4667. ; returns carry set if CR is to be protected
  4668. ; returns carry clear if CR is unprotected
  4669. crprot proc near            ; cmtxt {continued lines} section
  4670.     cmp    comand.cmcnvkind,cmcnv_crprot    ; braces span lines?
  4671.     jne    crprotx            ; ne = no, do regular hyphenation
  4672.     cmp    cmkind,cmkey
  4673.     je    crprotx
  4674.     cmp    bracecnt,0        ; are curly braces matched yet?
  4675.     je    crprotx            ; e = yes, then this is ignorable
  4676.     stc                ; CR needs protection
  4677.     ret
  4678. crprotx:clc
  4679.     ret
  4680. crprot    endp
  4681.  
  4682. ; Jump to REPARS to do a rescan of the existing buffer.
  4683. ; Jump to PRSERR on a parsing error (quits command, clears old read material)
  4684.  
  4685. PRSERR    PROC FAR
  4686.     mov    cmwptr,offset cmdbuf    ; initialize write pointer
  4687.     mov    ah,prstr
  4688.     mov    dx,offset crlf        ; leave old line, start a new one
  4689.     int    dos
  4690.     call    rprompt            ; restore master prompt level
  4691.                     ; reparse current line
  4692. REPARS:    mov    cmrptr,offset cmdbuf    ; reinit read pointer
  4693.     xor    ax,ax
  4694.     mov    cmsflg,0ffh        ; strip leading spaces
  4695.     mov    subcnt,al        ; clear substitution state variables
  4696.     mov    subtype,al
  4697.     mov    bracecnt,al
  4698.     call    optionclr        ; clear parser options
  4699.     cmp    in_showprompt,al    ; redoing the prompt itself?
  4700.     jne    prser3            ; ne = yes, suppress display
  4701.     cmp    comand.cmdirect,al    ; doing direct reading?
  4702.     jne    prser2            ; ne = yes
  4703.     cmp    taklev,al        ; in Take cmd?
  4704.     je    prser2            ; e = no
  4705.     cmp    flags.takflg,al        ; echo contents of Take file?
  4706.     je    prser3            ; e = no
  4707. prser2:    call    fctlu            ; clear display's line, reuse it
  4708.     mov    dx,comand.cmprmp    ; display the asciiz prompt
  4709.     call    prtasz
  4710. prser3:    mov    bx,0ffffh        ; returned keyword value
  4711.     mov    sp,comand.cmostp    ; set new sp to old one
  4712.     jmp    dword ptr comand.cmrprs    ; jump to just after the prompt call
  4713. PRSERR    ENDP
  4714.  
  4715. ; Restore prompt material to that of the master prompt. This removes settings
  4716. ; of local PROMPT calls so we can reprompt at the main Kermit level.
  4717. RPROMPT    proc    near
  4718.     push    ax            ; Must preserve AX
  4719.     mov    replay_cnt,0
  4720.     mov    ax,mcmprmp        ; address of prompt string
  4721.     or    ax,ax            ; any address given yet?
  4722.     jz    rprompt1        ; z = none, not inited yet
  4723.     mov    comand.cmprmp,ax    ; set current address ptr
  4724.     mov    ax,word ptr mcmrprs    ; offset of reparse address
  4725.     mov    word ptr comand.cmrprs,ax
  4726.     mov    ax,word ptr mcmrprs+2    ; segment of reparse address
  4727.     mov    word ptr comand.cmrprs+2,ax
  4728.     mov    ax,mcmostp        ; stack ptr at reparse time
  4729.     mov    comand.cmostp,ax
  4730.     mov    cmrawptr,offset rawbuf    ; base/raw buffer write ptr, reset
  4731.     mov    in_reparse,0        ; clear buffer remake flag
  4732. rprompt1:pop    ax
  4733.     ret
  4734. RPROMPT    endp
  4735.  
  4736. ; Clear comand.* options to defaults
  4737. optionclr proc    near
  4738.     push    ax
  4739.     xor    ax,ax
  4740.     mov    comand.cmwhite,al    ; clear whitespace flag
  4741.     mov    comand.cmper,al        ; reset to variable recognition
  4742.     mov    comand.cmkeep,al    ; do not keep Take file open
  4743.     mov    comand.impdo,al        ; clear flag to prevent loops
  4744.     mov    comand.cmquiet,al    ; permit echoing again
  4745.     mov    comand.cmblen,ax    ; set user buffer length to unknown
  4746.     mov    comand.cmdonum,al    ; defeat \number expansion
  4747.     mov    comand.cmcomma,al    ; comma is not a space
  4748.     mov    comand.cmarray,al    ; disallow sub in [..]
  4749.     mov    comand.cmcnvkind,cmcnv_none ; default is no conversion
  4750.     pop    ax
  4751.     ret
  4752. optionclr endp
  4753.  
  4754. ; write \v(ARGC) contents to ds:di
  4755. wrtargc    proc    near
  4756.     xor    ax,ax
  4757.     cmp    taklev,0        ; in a Take/Macro?
  4758.     je    wrtarg1            ; e = no
  4759.     mov    bx,takadr        ; current Take structure
  4760.     mov    ax,[bx].takargc        ; get ARGC
  4761. wrtarg1:call    fdec2di            ; write as ascii
  4762.     mov    word ptr es:[di],0020h    ; space and null terminator
  4763.     ret
  4764. wrtargc    endp
  4765.  
  4766. ; write \v(COUNT) text to ds:di
  4767. wrtcnt    proc    near
  4768.     xor    ax,ax
  4769.     cmp    taklev,0        ; in a Take/Macro?
  4770.     je    wrtcnt1            ; e = no
  4771.     mov    bx,takadr        ; current Take structure
  4772.     mov    ax,[bx].takctr        ; get COUNT
  4773. wrtcnt1:call    fdec2di            ; write as ascii
  4774.     ret
  4775. wrtcnt    endp
  4776.  
  4777. ; write \v(DATE) text to ds:di
  4778. wrtdate    proc    near
  4779.     push    cx
  4780.     push    dx
  4781.     mov    ah,getdate        ; DOS date (cx= yyyy, dh= mm, dl= dd)
  4782.     int    dos
  4783.     xor    ah,ah
  4784.     mov    al,dl            ; day
  4785.     call    wrtdat5
  4786.     mov    byte ptr es:[di],' '
  4787.     inc    di
  4788.     xor    bh,bh
  4789.     mov    bl,dh            ; month
  4790.     dec    bx            ; count from zero
  4791.     shl    bx,1            ; count four byte groups
  4792.     shl    bx,1
  4793.     mov    ax,word ptr month[bx]
  4794.     stosw
  4795.     mov    ax,word ptr month[bx+2]
  4796.     stosw
  4797.     mov    ax,cx            ; year
  4798. wrtdat3:call    wrtdat5
  4799.     mov    word ptr es:[di],0020h    ; space and null terminator
  4800.     pop    dx
  4801.     pop    cx
  4802.     ret
  4803.  
  4804. wrtdat5:cmp    ax,10            ; leading tens digit present?
  4805.     jae    wrtdat6            ; ae = yes
  4806.     mov    byte ptr es:[di],'0'    ; insert leading 0
  4807.     inc    di
  4808. wrtdat6:call    fdec2di            ; write decimal asciiz to buffer
  4809.     ret
  4810.  
  4811. wrtdate    endp
  4812.  
  4813. ; write \v(ERRORLEVEL) text to ds:di
  4814. wrterr    proc    near
  4815.     mov    al,errlev        ; current Errorlevel
  4816.     xor    ah,ah
  4817.     call    fdec2di            ; write as ascii
  4818.     mov    word ptr es:[di],0020h    ; space and null terminator
  4819.     ret
  4820. wrterr    endp
  4821.  
  4822. ; write \v(KEYBOARD) text to ds:di
  4823. wrtkbd    proc    near
  4824.     mov    ax,keyboard        ; 88 or 101 keyboard keys
  4825.     call    fdec2di            ; write as ascii
  4826.     mov    word ptr es:[di],0020h    ; space and null terminator
  4827.     ret
  4828. wrtkbd    endp
  4829.  
  4830. ; write \v(NDATE) text to ds:di
  4831. ; where NDATE is YYYYMMDD
  4832. wrtndate proc    near
  4833.     mov    ah,getdate        ; DOS date (cx= yyyy, dh= mm, dl= dd)
  4834.     int    dos
  4835.     push    dx            ; save dx
  4836.     mov    ax,cx            ; year
  4837.     call    fdec2di            ; convert it
  4838.     pop    dx            ; get mm:dd
  4839.     push    dx
  4840.     mov    al,dh            ; months are next
  4841.     xor    ah,ah
  4842.     cmp    al,10            ; less than 10?
  4843.     jae    wrtndat1        ; ae = no
  4844.     mov    byte ptr es:[di],'0'    ; leading 0
  4845.     inc    di
  4846. wrtndat1:call    fdec2di
  4847.     pop    dx
  4848.     mov    al,dl            ; get days
  4849.     xor    ah,ah
  4850.     cmp    al,10            ; less than 10?
  4851.     jae    wrtndat2        ; ae = no
  4852.     mov    byte ptr es:[di],'0'    ; leading 0
  4853.     inc    di
  4854. wrtndat2:call    fdec2di
  4855.     mov    word ptr es:[di],0020h    ; space and null terminator
  4856.     ret
  4857. wrtndate endp
  4858.  
  4859. ; write \v(DIRECTORY) text to es:di
  4860. wrtdir    proc    near
  4861.     push    si
  4862.     mov    ah,gcurdsk        ; get current disk
  4863.     int    dos
  4864.     add    al,'A'            ; make al = 0 == 'A'
  4865.     stosb
  4866.     mov    ax,'\:'
  4867.     stosw
  4868.     mov    si,di            ; work buffer
  4869.     push    ds
  4870.     mov    cx,es
  4871.     mov    ds,cx
  4872.     mov    ah,gcd            ; get current directory
  4873.     xor    dl,dl            ; use current drive
  4874.     int    dos            ; get ds:si = asciiz path (no drive)
  4875.     pop    ds
  4876.     mov    dx,si
  4877.     push    ds
  4878.     mov    cx,es
  4879.     mov    ds,cx
  4880.     call    strlen
  4881.     pop    ds
  4882.     add    di,cx
  4883. wrtdir1:mov    word ptr es:[di],0020h    ; space and null terminator
  4884.     pop    si
  4885.     ret
  4886. wrtdir    endp
  4887.  
  4888. fwrtdir    proc    far
  4889.     call    wrtdir
  4890.     ret
  4891. fwrtdir    endp
  4892.  
  4893. ; write \v(PLATFORM) text to ds:di
  4894. wrtplat    proc    near
  4895.     push    si
  4896.     mov    si,offset machnam    ; machine name in sys dep file
  4897.     cld
  4898. wrtplat1:lodsb                ; get a char
  4899.     cmp    al,'$'            ; terminator?
  4900.     je    wrtplat2        ; e = yes
  4901.     stosb                ; store char
  4902.     jmp    short wrtplat1        ; keep going
  4903. wrtplat2:mov    word ptr es:[di],0020h    ; space and null terminator
  4904.     pop    si
  4905.     ret
  4906. wrtplat    endp
  4907.  
  4908. ; write \v(PORT) text to ds:di
  4909. wrtport    proc    near
  4910.     push    bx
  4911.     push    si
  4912.     mov    al,flags.comflg        ; get coms port indicator
  4913.     mov    bx,offset comptab    ; table of comms ports
  4914.     mov    cl,[bx]            ; number of entries
  4915.     xor    ch,ch
  4916.     inc    bx
  4917. wrtpor3:mov    dx,[bx]            ; length of this entry
  4918.     mov    si,bx
  4919.     add    si,2            ; points to entry text string
  4920.     add    si,dx            ; point to qualifier
  4921.     cmp    [si],al            ; our port?
  4922.     je    wrtpor4            ; e = yes
  4923.     add    bx,[bx]            ; add text length
  4924.     add    bx,4            ; plus count and qualifier
  4925.     loop    wrtpor3            ; next entry
  4926.     jmp    short wrtpor5        ; no match, curious
  4927. wrtpor4:mov    si,bx            ; point at entry
  4928.     add    si,2            ; point at string
  4929.     mov    cx,[bx]            ; length of string
  4930.     cld
  4931.     rep    movsb            ; copy to DS:DI
  4932. wrtpor5:mov    word ptr es:[di],0020h    ; space and null terminator
  4933.     pop    si
  4934.     pop    bx
  4935.     ret
  4936. wrtport    endp
  4937.  
  4938. ; write \v(PROGRAM) text to ds:si
  4939. wrtprog    proc    near
  4940.     push    si
  4941.     mov    si,offset progm        ; source string
  4942.     cld
  4943. wrtprg1:lodsb
  4944.     cmp    al,'$'            ; terminator?
  4945.     je    wrtprg2            ; e = yes
  4946.     stosb                ; store the char
  4947.     jmp    short wrtprg1
  4948. wrtprg2:mov    word ptr es:[di],0020h    ; space and null terminator
  4949.     pop    si
  4950.     ret
  4951. wrtprog    endp
  4952.  
  4953. ; write \v(STATUS) text to ds:di
  4954. wrtstat    proc    near
  4955.     mov    ax,kstatus        ; Kermit status word
  4956.     call    fdec2di
  4957.     mov    word ptr es:[di],0020h    ; space and null terminator
  4958.     ret
  4959. wrtstat    endp
  4960.  
  4961. ; write \v(SYSTEM) text to ds:di
  4962. wrtsystem proc    near
  4963.     push    si
  4964.     mov    si,offset system    ; system string "MS-DOS", dollar sign
  4965.     cld
  4966.     jmp    wrtplat1        ; use some common code
  4967. wrtsystem endp
  4968.  
  4969. ; write \v(TIME) text to ds:di
  4970. wrttime    proc    near
  4971.     mov    ah,gettim        ; DOS tod (ch=hh, cl=mm, dh=ss, dl=.s)
  4972.     int    dos
  4973.     push    dx            ; save dx
  4974.     xor    ah,ah
  4975.     mov    al,ch            ; Hours
  4976.     cmp    al,10            ; leading digit?
  4977.     jae    wrttim1            ; ae = yes
  4978.     mov    byte ptr es:[di],'0'    ; make our own
  4979.     inc    di
  4980. wrttim1:call    fdec2di            ; write decimal asciiz to buffer
  4981.     mov    byte ptr es:[di],':'
  4982.     inc    di
  4983.     xor    ah,ah
  4984.     mov    al,cl            ; Minutes
  4985.     cmp    al,10            ; leading digit?
  4986.     jae    wrttim2            ; ae = yes
  4987.     mov    byte ptr es:[di],'0'    ; make our own
  4988.     inc    di
  4989. wrttim2:call    fdec2di            ; write decimal asciiz to buffer
  4990.     mov    byte ptr es:[di],':'
  4991.     inc    di
  4992.     pop    dx
  4993.     xor    ah,ah
  4994.     mov    al,dh            ; Seconds
  4995.     cmp    al,10            ; leading digit?
  4996.     jae    wrttim3            ; ae = yes
  4997.     mov    byte ptr es:[di],'0'    ; make our own
  4998.     inc    di
  4999. wrttim3:call    fdec2di            ; write decimal asciiz to buffer
  5000.     mov    word ptr es:[di],0020h    ; space and null terminator
  5001.     ret
  5002. wrttime    endp
  5003.  
  5004. ; write \v(Version) text to ds:di
  5005. wrtver    proc    near
  5006.     mov    si,offset verident    ; MS Kermit version string in mssker
  5007.     cld
  5008. wrtver1:lodsb
  5009.     stosb
  5010.     cmp    al,'$'            ; end of string?
  5011.     jne    wrtver1            ; ne = no, continue copying
  5012.     dec    di
  5013.     mov    word ptr es:[di],0020h    ; space and null terminator
  5014.     ret
  5015. wrtver    endp
  5016.  
  5017. ; write \v(TERMINAL) text to ds:di
  5018. wrtterm    proc    near
  5019.     mov    ax,flags.vtflg        ; current terminal type
  5020.     mov    bx,offset termtb    ; terminal type table msx...
  5021.     mov    cl,[bx]            ; number of entries in our table
  5022.     xor    ch,ch
  5023.     inc    bx            ; point to the data
  5024. wrtter1:mov    si,[bx]            ; length of keyword
  5025.     cmp    ax,[bx+si+2]        ; value fields match?
  5026.     je    wrtter2            ; e = yes
  5027.     add    bx,si            ; add word length
  5028.     add    bx,4            ; skip count and value fields
  5029.     loop    wrtter1            ; keep searching
  5030.     jmp    short wrtter4        ; no match, use just a space
  5031. wrtter2:mov    cx,[bx]            ; get length of counted string
  5032.     mov    si,bx
  5033.     add    si,2            ; look at text
  5034.     cld
  5035.     rep    movsb
  5036. wrtter4:mov    word ptr es:[di],0020h    ; space and null terminator
  5037.     ret
  5038. wrtterm    endp
  5039.  
  5040. fcmgtch    proc    far
  5041.     call    cmgtch
  5042.     ret
  5043. fcmgtch    endp
  5044.  
  5045. code1    ends
  5046.  
  5047. code    segment
  5048.     assume    cs:code
  5049.  
  5050. ; Set master prompt level. Enter with DX = offset of prompt string
  5051. MPROMPT    proc    near
  5052.     mov    mcmprmp,dx        ; offset of prompt string
  5053.     pop    ax            ; get the return address
  5054.     mov    word ptr mcmrprs,ax     ; offset to go to on reparse
  5055.     mov    mcmostp,sp        ; stack pointer at reparse time
  5056.     push    ax            ; put it on the stack again
  5057.     mov    ax,cs            ; our current code segment
  5058.     mov    word ptr mcmrprs+2,ax     ; segment of reparse address
  5059.     mov    comand.cmdirect,0    ; read normally (vs force to kbd/file)
  5060.     mov    cmrawptr,offset rawbuf    ; base/raw buffer write ptr, reset
  5061.     mov    cmwptr,offset cmdbuf
  5062.     mov    cmrptr,offset cmdbuf
  5063. MPROMPT    endp
  5064.  
  5065. ; This routine prints the prompt and specifies the reparse address.
  5066. ; Enter with pointer to prompt string in dx. 
  5067. PROMPT    PROC  NEAR
  5068.     xor    ax,ax            ; get a zero
  5069.     xchg    al,comand.cmdirect    ; set cmdirect to zero for showprompt
  5070.     xchg    ah,comand.cmper
  5071.     push    ax
  5072.     xor    al,al
  5073.     xchg    al,flags.takflg        ; take echo flag, off in showprompt
  5074.     push    ax
  5075.     call    showprompt        ; convert string with variables
  5076.     pop    ax
  5077.     xchg    al,flags.takflg        ; restore take echo flag
  5078.     pop    ax
  5079.     mov    comand.cmdirect,al    ; restore direct mode (if any)
  5080.     mov    comand.cmper,ah
  5081.     mov    comand.cmprmp,dx    ; save the prompt
  5082.     pop    ax            ; get the return address
  5083.     mov    word ptr comand.cmrprs,ax ; offset to go to on reparse
  5084.     mov    comand.cmostp,sp    ; save for later restoration
  5085.     push    ax            ; put it on the stack again
  5086.     mov    ax,cs            ; our current code segment
  5087.     mov    word ptr comand.cmrprs+2,ax ; segment of reparse address
  5088.     mov    ax,offset cmdbuf
  5089.     mov    cmrptr,ax        ; reset buffer read/write pointers
  5090.     mov    cmwptr,ax
  5091.     mov    cmrawptr,offset rawbuf
  5092.     mov    in_reparse,0
  5093.     xor    ax,ax
  5094.     mov    subcnt,al        ; substitution variable state info
  5095.     mov    subtype,al
  5096.     mov    bracecnt,al
  5097.     mov    replay_cnt,ax
  5098.     mov    cmsiz,ax
  5099.     mov    comand.cmper,al        ; allow substitutions
  5100.     mov    cmsflg,0ffh        ; remove leading spaces
  5101.     cmp    comand.cmdirect,al    ; doing direct reading?
  5102.     jne    promp1            ; ne = yes
  5103.     cmp    flags.takflg,al        ; look at Take flag, zero?
  5104.     jne    promp1            ; ne=supposed to echo, skip this check
  5105.     cmp    taklev,al        ; inside a take file?
  5106.     je    promp1            ; e = no, keep going
  5107.     clc
  5108.     ret                ; yes, return
  5109. promp1:    mov    ah,prstr
  5110.     mov    dx,offset crlf
  5111.     int    dos
  5112.     mov    dx,comand.cmprmp    ; prompt pointer
  5113.     call    prtasz            ; show asciiz prompt string
  5114.     clc
  5115.     ret
  5116. PROMPT    ENDP
  5117.  
  5118. ; Convert ASCIIZ prompt string given in DS:DX to string in DS:comand.cmprmp
  5119. ; with expansion of substitution variables. Final prompt is in DX and in
  5120. ; comand.cmprmp = offset cmdbuf+cmdblen/2 (of a 1000 byte total buffer).
  5121. showprompt proc far
  5122.     cmp    in_showprompt,0        ; doing this routine already?
  5123.     je    showp0            ; e = no
  5124.     mov    dx,comand.cmprmp    ; yes, return current prompt string
  5125.     ret
  5126. showp0:    mov    word ptr cmdbuf+cmdblen/2,0    ; clear final prompt string
  5127.     mov    comand.cmprmp,offset cmdbuf+cmdblen/2 ; prompt pointer
  5128.     mov    si,dx            ; raw prompt string ds:dx
  5129.     call    strlen            ; get string length
  5130.     or    cx,cx            ; empty?
  5131.     jnz    showp1            ; nz = no
  5132.     ret                ; return empty prompt string
  5133. showp1:    inc    cx            ; include null in length
  5134.     mov    al,taklev        ; entering taklev
  5135.     cmp    al,maxtak        ; room in take level?
  5136.     jb    showp2            ; b = yes
  5137.     mov    comand.cmprmp,dx    ; original raw prompt offset
  5138.     ret                ; return it
  5139.  
  5140. showp2:    push    ax            ; save take level
  5141.     mov    si,offset cmdbuf    ; reset parse buffer pointers
  5142.     mov    cmrptr,si
  5143.     mov    cmwptr,si
  5144.     mov    cmrawptr,offset rawbuf
  5145.     xor    ax,ax
  5146.     mov    subcnt,al        ; clear substitution state variables
  5147.     mov    subtype,al
  5148.     mov    bracecnt,al
  5149.     call    takopen_sub        ; open take as text substitution
  5150.     mov    bx,takadr        ; address of take structure
  5151.     mov    ax,ds
  5152.     mov    [bx].takbuf,ax        ; segment of Take buffer
  5153.     mov    [bx].takptr,dx         ; offset of beginning of def text
  5154.     mov    [bx].takcnt,cx        ; # of chars in buffer (includes NUL)
  5155.     or    [bx].takattr,take_autocr ; imply CR at end of prompt
  5156.     mov    in_showprompt,1        ; say making new prompt
  5157.     xor    ax,ax
  5158.     mov    cmsflg,al        ; get all spaces
  5159.     mov    comand.cmper,al        ; reset to variable recognition
  5160.     mov    comand.cmquiet,1    ; no echo
  5161.     mov    comand.cmwhite,1    ; leading white space is ok
  5162.     mov    comand.cmcr,1        ; bare cr's allowed
  5163.     mov    comand.cmostp,sp    ; set reprompt stack pointer here
  5164.     mov    comand.cmcnvkind,cmcnv_none ; default is no conversion
  5165.     mov    cmkind,cmword        ; say getting a word, not a line
  5166.     mov    comand.cmdonum,0    ; disable \number expansion
  5167.     mov    word ptr comand.cmrprs,offset showp3 ; offset for reparse
  5168.     mov    ax,cs            ; our current code segment
  5169.     mov    word ptr comand.cmrprs+2,ax ; segment of reparse address
  5170.  
  5171. showp3:    mov    cmptab,offset cmdbuf+cmdblen/2 ; buffer for new string
  5172.     mov    comand.cmblen,(cmdblen/2)-100 ; length of our buffer (plenty)
  5173.  
  5174. showp4:    call    fcmgtch            ; get a char
  5175.     jnc    showp5            ; nc = non-terminator
  5176.     cmp    ah,' '            ; space terminator?
  5177.     je    showp5            ; e = yes
  5178.     inc    cmrptr            ; 'read' other kinds of terminator
  5179. showp5:    mov    bx,cmptab        ; pointer into destination array
  5180.     mov    [bx],ah            ; put char into the buffer
  5181.     inc    bx
  5182.     xor    al,al
  5183.     mov    [bx],al            ; insert null terminator
  5184.     mov    cmptab,bx
  5185.     cmp    bx,offset cmdbuf+cmdblen ; end of buffer?
  5186.     jae    showp6            ; ae = end of buffer, terminate
  5187.     mov    cmsflg,0        ; still get spaces
  5188.     or    ah,ah            ; this terminator?
  5189.     jnz    showp4            ; nz = no, keep reading
  5190.  
  5191. showp6:    pop    ax            ; saved entering taklev
  5192.     cmp    al,taklev        ; saved versus current taklev
  5193.     jae    showp7            ; ae = nothing new to be removed
  5194.     push    ax            ; save again for loop
  5195.     call    takclos            ; close Take file
  5196.     jmp    showp6            ; keep at it
  5197. showp7:    xor    ax,ax
  5198.     mov    comand.cmwhite,al    ; clear leading whitespace flag
  5199.     mov    comand.cmblen,ax    ; set user buffer length to unknown
  5200.     mov    comand.cmquiet,al    ; enable echo
  5201.     mov    comand.cmcr,al        ; bare cr's not allowed
  5202.     mov    comand.cmdonum,al    ; disable \number expansion
  5203.     mov    in_showprompt,al    ; done making new prompt
  5204.     mov    dx,offset cmdbuf+cmdblen/2 ; return in DX
  5205.     mov    comand.cmprmp,dx     ; returned prompt pointer
  5206.     mov    subcnt,al
  5207.     mov    subtype,al
  5208.     ret
  5209. showprompt endp
  5210. code    ends
  5211.  
  5212. code1    segment
  5213.     assume    cs:code1
  5214.  
  5215. ISDEV    PROC    FAR            ; Set carry if STDIN is non-disk
  5216.     push    ax
  5217.     push    bx
  5218.     push    dx
  5219.     xor    bx,bx            ; handle 0 is stdin
  5220.     xor    al,al            ; get device info
  5221.     mov    ah,ioctl
  5222.     int    dos
  5223.     rcl    dl,1            ; put ISDEV bit into the carry bit
  5224.     pop    dx            ; carry is set if device
  5225.     pop    bx
  5226.     pop    ax
  5227.     ret                ; carry set if device
  5228. ISDEV    ENDP
  5229.  
  5230. ISEOF    PROC    FAR            ; Set carry if STDIN is at EOF
  5231.     push    ax            ;  but only if stdin is a non-device
  5232.     push    bx
  5233.     push    dx
  5234.     xor    bx,bx            ; handle 0 is stdin
  5235.     xor    al,al            ; get device info
  5236.     mov    ah,ioctl
  5237.     int    dos
  5238.     mov    ah,ioctl
  5239.     mov    al,6            ; get handle input status, set al
  5240.     test    dl,80h            ; bit set if handle is for a device
  5241.     jnz    iseof1            ; nz = device, always ready (al != 0)
  5242.     int    dos
  5243. iseof1:    or    al,al            ; EOF?
  5244.     pop    dx
  5245.     pop    bx
  5246.     pop    ax
  5247.     jnz    iseof2            ; nz = no
  5248.     stc                ; set carry for eof
  5249.     ret
  5250. iseof2:    clc                ; clear carry for not-eof
  5251.     ret
  5252. ISEOF    ENDP
  5253.  
  5254. ; Convert ascii characters in al and ah to lowercase.
  5255. ; All registers are preserved except AX, of course.
  5256.  
  5257. TOLOWR PROC FAR
  5258.     cmp    ah,'A'            ; less that cap A?
  5259.     jl    tolow1            ; l = yes. leave untouched
  5260.     cmp    ah,'Z'+1        ; more than cap Z?
  5261.     jns    tolow1            ; ns = yes
  5262.     or    ah,20H            ; convert to lowercase
  5263. tolow1:    cmp    al,'A'            ; less that cap A?
  5264.     jl    tolow2            ; l = yes. leave untouched
  5265.     cmp    al,'Z'+1        ; more than cap Z?
  5266.     jns    tolow2            ; ns = yes
  5267.     or    al,20H            ; convert to lower case
  5268. tolow2:    ret
  5269. TOLOWR    endp
  5270.  
  5271. TOUPR PROC FAR
  5272.     cmp    ah,'a'            ; less that lower A?
  5273.     jb    toup1            ; l = yes. leave untouched
  5274.     cmp    ah,'z'            ; more than lower Z?
  5275.     ja    toup1            ; ns = yes
  5276.     and    ah,not 20H        ; convert to upper case
  5277. toup1:    cmp    al,'a'            ; less that lower A?
  5278.     jb    toup2            ; l = yes. leave untouched
  5279.     cmp    al,'z'            ; more than lower Z?
  5280.     ja    toup2            ; ns = yes
  5281.     and    al,not 20H        ; convert to upper case
  5282. toup2:    ret
  5283. TOUPR    endp
  5284.  
  5285. ; Revise string in ds:si et seq. Expect incoming length to be in CX.
  5286. ; Controlled by comand.cmcnvkind.
  5287. ; Original string modified to replace bare commas with Carriage Returns.
  5288. ; Top level braces removed, but only if the string begins with an 
  5289. ; opening brace, and hence commas revealed are also converted to CR.
  5290. ; Converted text has a forced null termination to assist callers of cmtxt
  5291. ; which don't remember returned byte count.
  5292. ; All registers preserved except CX (returned length).
  5293. unbrace    proc    far
  5294.     or    cx,cx            ; count of zero or less?
  5295.     jg    unbrac1            ; g = no, but could be huge
  5296.     ret
  5297. unbrac1:push    ax
  5298.     push    si            ; src and dest are ds:si
  5299.     push    di
  5300.     mov    di,si            ; si = source
  5301.     add    di,cx            ; skip to end of line + 1
  5302. unbrac2:cmp    byte ptr [di-1],' '    ; trailing space?
  5303.     jne    unbrac3            ; ne = no
  5304.     dec    di            ; backup another byte
  5305.     loop    unbrac2
  5306. unbrac3:cmp    byte ptr [si],braceop    ; now opens with brace?
  5307.     jne    unbrac4            ; ne = no, nothing to do
  5308.     cmp    byte ptr [di-1],bracecl    ; ended on brace?
  5309.     jne    unbrac4            ; ne = no, nothing to do
  5310.     mov    cx,di            ; ending spot
  5311.     sub    cx,si            ; minus start
  5312.     mov    di,si            ; destination
  5313.     sub    cx,2            ; minus opening and closing braces
  5314.     inc    si            ; skip opening brace
  5315.     push    cx
  5316.     push    es
  5317.     mov    ax,ds
  5318.     mov    es,ax
  5319.     cld
  5320.     rep    movsb            ; copy down text inside braces
  5321.     xor    al,al
  5322.     mov    bx,di            ; report null in BX
  5323.     stosb                ; forced null terminator
  5324.     pop    es
  5325.     pop    cx
  5326. unbrac4:pop    di
  5327.     pop    si
  5328.     pop    ax
  5329.     clc
  5330.     ret
  5331. unbrace    endp
  5332.  
  5333. ; Parse control sequences and device control strings.
  5334. ; Expect CSI, Escape [, or DCS lead-in characters to have been read.
  5335. ; Puts numerical Parameters in array param (16 bits, count is nparam) and
  5336. ;  a single letter Parameter in lparam, (Parameters are all ASCII column 3)
  5337. ;  Intermediate characters in array inter (count is ninter), (ASCII column 2)
  5338. ;  Final character in AL (ASCII columns 4-7).
  5339. ; Invoke by setting state to offset atparse, set pardone to offset of
  5340. ; procedure to jump to after reading Final char (0 means do just ret)
  5341. ; and optionally setting parfail to address to jump to if parsing failure.
  5342. ; When the Final char has been accepted this routine jumps to label held in
  5343. ; pardone for final action. Before the Final char has been read successful
  5344. ; operations return carry clear.
  5345. ; Failure exits are carry set, and an optional jump through parfail (if 
  5346. ; non-zero) or a return.
  5347.  
  5348. atparse    proc    near
  5349.     mov    bx,parstate        ; get parsing state
  5350.     or    bx,bx            ; have any state?
  5351.     jnz    atpars1            ; nz = have a state
  5352.     call    atpclr            ; do initialization
  5353.     mov    bx,parstate        ; get initial state
  5354. atpars1:call    bx            ; execute it
  5355.     jc    atpfail            ; c = failure
  5356.     cmp    parstate,offset atpdone    ; parsed final char?
  5357.     je    atpdone            ; e = yes
  5358.     ret                ; no, wait for another char
  5359.  
  5360.                 ; successful conclusion, final char is in AL
  5361. atpdone:mov    parstate,0        ; reset parsing state
  5362.     cmp    pardone,0        ; separate return address defined?
  5363.     jne    atpdon1            ; ne = yes
  5364.     clc
  5365.     ret                ; else just return
  5366. atpdon1:clc
  5367.     jmp    pardone            ; jmp to supplied action routine
  5368.  
  5369. atpfail:mov    parstate,0        ; failed, reset parser to normal state
  5370.     cmp    parfail,0        ; jump address specified?
  5371.     je    atpfail1        ; e = no
  5372.     jmp    parfail            ; yes, exit this way
  5373. atpfail1:stc
  5374.     ret
  5375.                     ; parsing workers
  5376. atparm:    cmp    ninter,0        ; Parameter, started intermediate yet?
  5377.     jne    atinter            ; ne = yes, no more parameters
  5378.     cmp    al,';'            ; argument separator?
  5379.     jne    atparm3            ; ne = no
  5380.     mov    ax,nparam        ; number of Parameters
  5381.     inc    ax            ; say a new one
  5382.     cmp    ax,maxparam        ; too many?
  5383.     jb    atparm2            ; b = no, continue
  5384.     stc                ; set carry to say failed
  5385.     ret                ; too many, ignore remainder
  5386. atparm2:mov    nparam,ax        ; say doing another Parameter
  5387.     clc
  5388.     ret
  5389.  
  5390. atparm3:mov    ah,al            ; copy char
  5391.     and    ah,not 0fh        ; ignore low nibble
  5392.     cmp    ah,30h            ; column 3, row 0? (30h='0')
  5393.     jne    atparm6            ; ne = no, check Intermediate/Final
  5394.     cmp    al,'9'            ; digit?
  5395.     ja    atparm5            ; a = no, check letter Parameters
  5396.     sub    al,'0'            ; ascii to binary
  5397.     mov    bx,nparam        ; current parameter number
  5398.     shl    bx,1            ; convert to word index
  5399.     mov    cx,param[bx]        ; current parameter value
  5400.     shl    cx,1            ; multiply by 10.  2 * cl
  5401.     push    bx
  5402.     mov    bx,cx            ; save 2 * cl
  5403.     shl    cx,1            ; 4 * cl
  5404.     shl    cx,1            ; 8 * cl
  5405.     add    cx,bx            ; 10 * cl
  5406.     pop    bx
  5407.     add    cl,al            ; add new digit
  5408.     adc    ch,0
  5409.     jnc    atparm4            ; nc = no carry out (65K or below)
  5410.     mov    cx,0ffffh        ; set to max value
  5411. atparm4:mov    param[bx],cx        ; current Parameter value
  5412.     clc
  5413.     ret
  5414.                     ; check non-numeric Parameters
  5415. atparm5:cmp    al,'?'            ; within column 3?
  5416.     ja    atfinal            ; a = no, check Final char
  5417.     mov    lparam,al        ; store non-numeric Parameter
  5418.     clc
  5419.     ret
  5420.  
  5421. atparm6:cmp    nparam,0        ; started a parameter yet?
  5422.     jne    atparm7            ; ne = yes
  5423.     cmp    param,0            ; got anything for param[0]?
  5424.     je    atinter            ; e = no
  5425. atparm7:inc    nparam            ; yes, say finished with another
  5426.  
  5427. atinter:mov    parstate,offset atinter    ; next state (intermediate)
  5428.     cmp    al,';'            ; argument separator?
  5429.     jne    atinte1            ; ne = no
  5430.     cmp    ninter,maxinter        ; too many intermediates?
  5431.     jb    atinte2            ; b = no, continue
  5432.     stc                ; carry = failed
  5433.     ret                ; too many, ignore remainder
  5434. atinte1:test    al,not 2fh        ; column two = 20h - 2fh?
  5435.     jnz    atfinal            ; nz = not an Intermediate, try Final
  5436.     cmp    ninter,maxinter        ; too many intermediates?
  5437.     jb    atinte1a        ; b = no, continue
  5438.     stc                ; carry = failed
  5439.     ret                ; too many, ignore remainder
  5440. atinte1a:mov    bx,ninter        ; current Intermediate slot number
  5441.     mov    inter[bx],al        ; current Intermediate value
  5442. atinte2:inc    ninter            ; say doing another Intermediate
  5443.     clc
  5444.     ret
  5445.  
  5446. atfinal:cmp    al,40h            ; Final character, range is 40h to 7fh
  5447.     jb    atfina1            ; b = out of range
  5448.     cmp    al,7fh
  5449.     ja    atfina1            ; a = out of range
  5450.     mov    parstate,offset atpdone    ; next state is "done"
  5451.     clc                ; success, final char is in AL
  5452.     ret
  5453. atfina1:stc                ; c = failed
  5454.     ret
  5455. atparse    endp
  5456.  
  5457. ; Clear Parameter, Intermediate arrays in preparation for parsing
  5458. atpclr    proc    near
  5459.     push    ax
  5460.     push    cx
  5461.     push    di
  5462.     push    es
  5463.     xor    ax,ax            ; get a null
  5464.     mov    parstate,offset atparm    ; init parser state
  5465.     mov    lparam,al        ; clear letter Parameter
  5466.     mov    nparam,ax        ; clear Parameter count
  5467.     mov    cx,maxparam        ; number of Parameter slots
  5468.     mov    di,offset param        ; Parameter slots
  5469.     push    ds
  5470.     pop    es            ; use data segment for es:di below
  5471.     cld                ; set direction forward
  5472.     rep    stosw            ; clear the slots
  5473.     mov    ninter,ax        ; clear Intermediate count
  5474.     mov    cx,maxinter        ; number of Intermediate slots
  5475.     mov    di,offset inter        ; Intermediate slots
  5476.     rep    stosb            ; clear the slots
  5477.     pop    es
  5478.     pop    di
  5479.     pop    cx
  5480.     pop    ax
  5481.     ret
  5482. atpclr    endp
  5483.  
  5484. ; Dispatch table processor. Enter with BX pointing at table of {char count,
  5485. ; address of action routines, characters}. Jump to matching routine or return.
  5486. ; Enter with AL holding received Final char.
  5487. atdispat proc near
  5488.     mov    cl,[bx]            ; get table length from first byte
  5489.     xor    ch,ch
  5490.     mov    di,bx            ; main table
  5491.     add    di,3            ; point di at first char in table
  5492.     push    es
  5493.     push    ds
  5494.     pop    es            ; use data segment for es:di below
  5495.     cld                ; set direction forward
  5496.     repne    scasb            ; find matching character
  5497.     pop    es
  5498.     je    atdisp2            ; e = found a match, get action addr
  5499.     ret                ; ignore escape sequence
  5500. atdisp2:sub    di,bx            ; distance scanned in table
  5501.     sub    di,4            ; skip count byte, address word, inc
  5502.     shl    di,1            ; convert to word index
  5503.     inc    bx            ; point to address of action routines
  5504.     mov    bx,[bx]            ; get address of action table
  5505.     jmp    word ptr [bx+di]    ; dispatch to the routine
  5506. atdispat endp
  5507. code1    ends
  5508.     end
  5509.