home *** CD-ROM | disk | FTP | other *** search
/ World of Shareware - Software Farm 2 / wosw_2.zip / wosw_2 / CPROG / CMDSRC.ZIP / CMDMACRO.ASM < prev    next >
Assembly Source File  |  1990-06-24  |  43KB  |  1,529 lines

  1. ; CMDMACRO.ASM
  2. ; (c) 1989, 1990 Ashok P. Nadkarni
  3. ;
  4. ; Module implementing macro and symbol feature for CMDEDIT.
  5. ;
  6. ; Symbols :
  7. ; CMDEDIT symbols can be defined either from the command line or read
  8. ; from a file during initialization. The syntax is given by
  9. ;    defs symbolname expansion
  10. ; When the defined symbol appears as the first word in the line, it is
  11. ; replaced by its expansion. The rest of the line is unchanged. The
  12. ; following line defines a symbol called 'ed' that runs my editor.
  13. ;
  14. ;    defs ed c:\util\editor
  15. ;
  16. ; Now if you type
  17. ;
  18. ;    ed newfile
  19. ;
  20. ; the command
  21. ;
  22. ;    c:\util\editor newfile
  23. ;
  24. ; will be executed.
  25. ;
  26. ; Symbols are expanded recursively.
  27. ;
  28. ; Macros :
  29. ;
  30. ; CMDEDIT macros can be defined either from the command line or read
  31. ; from a macro file when CMDEDIT is installed. In both cases, macros
  32. ; are defined using the same syntax. Macros may expand into multiple 
  33. ; lines. In the latter case, each line of the expansion in passed to 
  34. ; the calling application one at a time.
  35. ;
  36. ; Macros are defined using the CMDEDIT command 'defm' followed by the
  37. ; macro name. The macro name is separated from the 'defm' keyword by one 
  38. ; or more spaces/tabs. Any characters after the name of the macro are ignored.
  39. ; Each line of the macro expansion is defined on a separate line. The
  40. ; expansion may contain any number of lines (limited by buffer space)
  41. ; and is terminated by a line that begins with the keyword 'endm'. For
  42. ; example, the following lines define a macro that will change the
  43. ; current directory from any disk:
  44. ;    defm gotc
  45. ;    c:
  46. ;    cd \TURBOC
  47. ;    endm
  48. ; Macro keywords are case-insensitive.
  49. ;
  50. ; Macro Parameters:
  51. ;    Similar to batch files, macros can be passed parameters. (Read
  52. ; your DOS manual to find out about parameters). Although the
  53. ; concept is similar to DOS batch files, CMDEDIT parameters behave a
  54. ; little differently. Upto 9 parameters can be defined. These are
  55. ; indicated in macro definitions as '%n' where n is a digit from 1 to 9.
  56. ; A parameter can appear anywhere in the definition and need
  57. ; not be surrounded by whitespace. Also, the character % itself can be
  58. ; placed anywhere in the definition as long as it is not followed by a
  59. ; digit. If you do want a '%n' sequence in the expansion, indicate the '%'
  60. ; character as '%%'.
  61. ; For example, consider
  62. ;    defm bf 
  63. ;    copy %1 a:\%1\%%1
  64. ;    endm
  65. ; Then, when you type
  66. ;    "bf myfile"
  67. ; the macro will expand to
  68. ;    "copy myfile a:\myfile\%1"
  69. ; Note how %1 has been replaced by 'myfile' in two places but not the third.
  70. ;
  71. ; A macro cannot call another macro except if the call is the last
  72. ; line in the macro. Macros anywhere else are not expanded and the line
  73. ; is passed to the calling application without modification.
  74. ;
  75. ; Note that the macro name can be null as well. In this case, hitting
  76. ; carraige return on an blank line will result in that macro being run.
  77.  
  78.     INCLUDE common.inc
  79.     INCLUDE general.inc
  80.     INCLUDE ascii.inc
  81.     INCLUDE dos.inc
  82.  
  83.     PUBLIC    macro_init
  84.     PUBLIC    symbol_init
  85.     PUBLIC    execute_defm
  86.     PUBLIC    execute_defs
  87.     PUBLIC    execute_delm
  88.     PUBLIC    execute_dels
  89.     PUBLIC    execute_rstsym
  90.     PUBLIC    execute_rstmac
  91.     PUBLIC    execute_cmdstat
  92.     PUBLIC    get_macro_line
  93.     PUBLIC    expand_macro
  94.     PUBLIC    expand_symbol
  95.     PUBLIC    expand_var
  96.     PUBLIC    get_symbol
  97.     PUBLIC    mac_stk
  98.     PUBLIC    sym_stk
  99.     PUBLIC    ismacsym
  100.  
  101. PLACEHOLDER equ PERCENT                ;Placeholder character
  102.  
  103.     INCLUDE buffers.inc
  104.  
  105. CSEG    SEGMENT    PARA PUBLIC 'CODE'
  106. DGROUP    GROUP    CSEG
  107.  
  108.     EXTRN    endm_cmd:BYTE
  109.     EXTRN    defm:BYTE
  110.     EXTRN    defs:BYTE
  111.     EXTRN    get_kbd_line:ABS
  112.     EXTRN    source:WORD
  113.     EXTRN    macro_level:WORD
  114.     EXTRN    macro_ignore_char:BYTE
  115.     EXTRN    lastchar:WORD
  116.     EXTRN    linebuf:BYTE
  117.     EXTRN    dot:WORD
  118.     EXTRN    LINEBUF_END:ABS
  119.     EXTRN    cur_macro:BYTE
  120.     EXTRN    cur_macro_len:WORD
  121.  
  122. mac_stk $string_stack    <>        ;Descriptor for macro buffer
  123. sym_stk    $string_stack    <>        ;Descriptor for sym buffer
  124. separator db    13            ;Separator string between macros
  125. sep_len    equ    $-separator        ;Length of separator string
  126. macro_noroom_msg db 'Table full. Definition ignored.',CR,LF,DOLLAR
  127. macro_prompt    db  CR,LF,'DEFM>',DOLLAR
  128.  
  129.  
  130.     EXTRN    push_word:PROC
  131.     EXTRN    push_string:PROC
  132.     EXTRN    get_next_line:PROC
  133.     EXTRN    reset_line:PROC
  134.     EXTRN    abort_processing:PROC
  135.     EXTRN    getargs:PROC
  136.     EXTRN    stre_cmp:PROC
  137.     EXTRN    set_disp_marks:PROC
  138.     EXTRN    isspace:PROC
  139.     EXTRN    isdelim:PROC
  140.     EXTRN    skip_whitespace:PROC
  141.     EXTRN    skip_nonwhite:PROC
  142.     EXTRN    skip_nondelim:PROC
  143.     EXTRN    makeroom:PROC
  144.     EXTRN    remove_chars:PROC
  145.     EXTRN    insert_chars:PROC
  146.     EXTRN    output_counted_string:PROC
  147.     EXTRN    output_newline:PROC
  148.     EXTRN    locate_dosenv:PROC
  149.  
  150.     ASSUME    CS:DGROUP,DS:DGROUP,ES:DGROUP,SS:DGROUP
  151.  
  152.  
  153. ;+
  154. ; FUNCTION : macro_init,symbol_init
  155. ;
  156. ; Initializes the various data structures associated with the
  157. ; macro /symbol buffer. CALLER MUST ENSURE PASSED ADDRESSES ARE VALID
  158. ; AND BUFFER IS LARGE ENOUGH.
  159. ;
  160. ; Parameters:
  161. ;    AX     - length of buffer in bytes
  162. ;    BX    - address of buffer
  163. ;
  164. ; Returns:
  165. ;    Nothing.
  166. ; Registers destroyed :
  167. ;    BX
  168. ;-
  169. macro_init proc    near
  170.     push    bx
  171.     mov    bx,offset DGROUP:mac_stk ;bx := address of buffer descriptors
  172.     jmp    short @macsym_init
  173. symbol_init LABEL near
  174.     push    bx
  175.     mov    bx,offset DGROUP:sym_stk ;bx := address of buffer descriptors
  176. @macsym_init:
  177.     xchg    ax,cx            ;CX = buffer size
  178.     pop    ax            ;AX = Buffer address
  179.                     ;BX points to appropriate descriptor
  180.     call    near ptr strstk_init    ;Initialize buffer and descriptor
  181.  
  182. ;Store a separator into the macro buffer
  183.     call    near ptr separate
  184.  
  185.     ret
  186. macro_init    endp
  187.  
  188.  
  189.  
  190.  
  191.  
  192. ;+
  193. ; FUNCTION : execute_rstmac, execute_rstsym
  194. ;
  195. ; Resets the various data structures associated with the
  196. ; macro /symbol buffer.
  197. ;
  198. ; Parameters:
  199. ;    None.
  200. ;
  201. ; Returns:
  202. ;    Nothing.
  203. ;
  204. ; Registers destroyed :
  205. ;    AX,CX,BX,DX
  206. ;-
  207. rst_macsym proc near
  208. execute_rstmac LABEL near
  209. ;    mov    macro_level,0
  210.     mov    bx,offset DGROUP:mac_stk ;bx := address of buffer descriptors
  211.     jmp    short @rst_macsym
  212. execute_rstsym LABEL near
  213.     mov    bx,offset DGROUP:sym_stk ;bx := address of buffer descriptors
  214. @rst_macsym:
  215.     call    near ptr strstk_reset    ;Re-initialize buffer and descriptor
  216.  
  217. ;Store a separator into the macro buffer
  218.     call    near ptr separate
  219.     ret
  220. rst_macsym    endp
  221.  
  222.  
  223.  
  224.  
  225. ;+
  226. ; FUNCTION : ismacsym
  227. ;
  228. ;    Called to check if the passed string is a valid symbol or macro
  229. ;    name. If found, the symbol/macro string stack pointer is set to the
  230. ;    first line of the expansion of the symbol/macro.
  231. ;
  232. ; Parameters:
  233. ;    AX    - length of symbol name to be checked
  234. ;    BX    - address of the string stack descriptor (symbol or macro)
  235. ;    SI    - pointer to the symbol to be checked.
  236. ;
  237. ; Returns:
  238. ;    CF    - 1 if symbol not found, the string stack current pointer
  239. ;          is undefined
  240. ;          0 if symbol found. The string stack pointer points to the
  241. ;          first line of the expansion of the found symbol/macro.
  242. ; Register(s) destroyed:
  243. ;    AX,CX,DX
  244. ;-
  245. ismacsym proc near
  246.     @save    si,di
  247.  
  248.     mov    di,ax            ;DI<-length of symbol
  249.     call    near ptr strstk_settop    ;Reset macro stack pointer
  250.  
  251. @ismacsym_20:
  252. ; Loop start, DI = num chars in word, SI->start of word
  253.  
  254.     mov    ax,si            ;AX->word
  255.     mov    cx,di            ;CX<-length of word
  256.     call    near ptr strstk_bck_find ;Look backward for string
  257. ;                     Params AX,BX,CX
  258. ;                     BX unchanged
  259.     jc    @ismacsym_98        ;Not found so return with CF set
  260. ;    The name matched. Now make sure it is a macro name by ensuring
  261. ;    previous string is a separator.
  262.     xor    cx,cx            ;cx<-length of pattern
  263.     call    near ptr strstk_bck_match ;Set current to previous string
  264. ;                     BX unchanged
  265.     jc    @ismacsym_98        ;start of buffer so return
  266. ;                     with CF set
  267.     push    bx
  268.     call    near ptr check_separator
  269.     pop    bx
  270.     je    @ismacsym_50        ;This is the one
  271. ;    Wasn't a separator. Move back over it to start the hunt again.
  272.     xor    cx,cx
  273.     call    near ptr strstk_fwd_match
  274.     jmp    short @ismacsym_20
  275.  
  276. @ismacsym_50:
  277. ; The macro/symbol has been found.
  278.     xor    cx,cx            ;CX<-match length
  279.     call    near ptr strstk_fwd_match ;Skip over separator
  280.     xor    cx,cx            ;CX<-match length
  281.     call    near ptr strstk_fwd_match ;Skip over macro/symbol name
  282.     clc                ;Clear return flag
  283. @ismacsym_98:
  284.     @restore
  285.     ret
  286. ismacsym endp
  287.  
  288.  
  289.  
  290. ;+
  291. ; FUNCTION : execute_dels, execute_delm
  292. ;
  293. ;    execute_dels and execute_delm respectively delete symbols and
  294. ;    macros from the appropriate stack. All symbols/macros listed on the
  295. ;    line are deleted. If a particular symbol/macro is not defined, no
  296. ;    error is generated.
  297. ;    
  298. ; Parameters:
  299. ;    SI    -> first char in linebuf following this command
  300. ;    CX    == remaining num chars in the line
  301. ;
  302. ; Returns:
  303. ;    Nothing.
  304. ;
  305. ; Register(s) destroyed:
  306. ;    AX,BX,CX,DX
  307. del_macsym proc near
  308. execute_dels LABEL near
  309.     mov    bx,offset DGROUP:sym_stk
  310.     jmp    short @del_macsym_5
  311. execute_delm LABEL near
  312.     cmp    macro_level,1
  313.     jne    @execute_delm_1
  314.     mov    ax,E_NESTED_DELM
  315.     jmp    near ptr abort_processing
  316. @execute_delm_1:
  317.     mov    bx,offset DGROUP:mac_stk
  318. @del_macsym_5:
  319.     @save    si,di
  320.     push    bp
  321.     mov    bp,sp
  322.     sub    sp,2
  323. num_remain equ <word ptr [bp-2]>
  324. ;
  325. @del_macsym_10:
  326. ; At this point, SI->remaining chars in line, CX is number of chars
  327.     jcxz    @del_macsym_99            ;No more chars in line
  328. ;    Find first word
  329.     push    bx                ;Save buffer descriptor addr
  330.     call    near ptr skip_whitespace    ;SI->first non blank
  331.     mov    di,si                ;DI->start of word
  332.     call    near ptr skip_nondelim        ;SI->char after word
  333.     mov    ax,si                ;
  334.     xchg    si,di                ;SI->word, DI->rest of line
  335.     sub    ax,si                ;AX<-num chars in word
  336.     pop    bx                ;BX->buffer descriptor
  337.     push    cx                ;Save remaining count
  338.     call    near ptr ismacsym        ;Is it in macro/symbol stack ?
  339.     pop    cx                ;Restore remaining count
  340.     mov    si,di                ;SI->rest of line
  341.     jc    @del_macsym_10            ;Not found so go onto next
  342. ;                         symbol 
  343. ; The macro or symbol has been found. The current stack pointer is the
  344. ; first line of the expansion. Move it down to the separator just before
  345. ; the macro or symbol name itself. Then keep deleting strings from the
  346. ; stack until we hit another separator.
  347.     push    cx                ;Save remaining count
  348.     xor    cx,cx
  349.     call    near ptr strstk_bck_match    ;Point to name
  350.     xor    cx,cx
  351.     call    near ptr strstk_bck_match    ;Point to separator
  352. @del_macsym_20:
  353.     call    near ptr strstk_kill        ;Delete the string
  354. ;    Check if separator
  355.     call    near ptr check_separator    ;Is new 'current' a
  356. ;                         separator ?
  357.     jne    @del_macsym_20            ;No, so keep deleting
  358. ;    Reached a separator.
  359.     pop    cx                ;Restore count
  360.     jmp    short @del_macsym_10        ;Keep looping for rest of line
  361.  
  362. @del_macsym_99:
  363.     mov    sp,bp
  364.     pop    bp
  365.     @restore
  366.     ret
  367. del_macsym endp
  368.  
  369.  
  370.  
  371. ;+
  372. ; FUNCTION : expand_macro, expand_symbol
  373. ;
  374. ;    expand_macro and expand_symbol attempt expand the first word in
  375. ;    linebuf as a macro name and symbol respectively. If no macro
  376. ;    expansion is going on, the expand_macro routine will attempt to
  377. ;    expand the first word in the line buffer as a macro (leading
  378. ;    whitespace is ignored). If an expansion is found, it is stored
  379. ;    in the line buffer with the parameters (if any) filled in. If
  380. ;    there is already a macro expansion going on or no macro is
  381. ;    found, the line buffer is unchanged.
  382. ;
  383. ;    In contrast, the expand_symbol routine always tries to
  384. ;    expand the first word as a symbol.
  385. ;
  386. ;    If the first character of the line is a macro_ignore_char, the
  387. ;    character is removed, the rest of the line moved up and no
  388. ;    expansions are done.
  389. ;
  390. ; Parameters:
  391. ;
  392. ; Returns:
  393. ;    CF =    0 if line buffer changed
  394. ;        1 otherwise (no macro/symbol or ongoing macro expansion)
  395. ;
  396. ; Register(s) destroyed:
  397. ; AX,BX,CX,DX
  398. ;-
  399. expand_macro proc near
  400.     mov    bx,offset DGROUP:mac_stk ;BX->macro descriptor
  401.     jmp    short @expand
  402. expand_symbol LABEL near
  403.     mov    bx,offset DGROUP:sym_stk ;BX->symbol descriptor
  404. @expand:
  405.     push    si
  406.     push    di
  407.     push    bp
  408.     mov    bp,sp
  409.     sub    sp,2
  410. num_remain  equ <word ptr [bp-2]>
  411.  
  412.     cmp    bx,offset DGROUP:mac_stk
  413.     jne    @expand_10        ;If symbol, don't worry about
  414. ;                     whether any macro expansions
  415. ;                     are ongoing
  416.     cmp    macro_level,0        ;Already expanding a macro?
  417.     je    @expand_10        ;No macro expansion currently ongoing
  418.     stc                ;set CF to indicate no expansion
  419.     jmp    @expand_98        ;Yes, exit with carry flag set
  420.  
  421. @expand_10:
  422. ; Look back through the macro/symbol stack buffer for a macro definition.
  423.     
  424. ;    Find first word of line.
  425.     push    bx            ;Save buffer descriptor addr
  426.     mov    cx,lastchar        ;End of line
  427.     mov    si,offset DGROUP:linebuf ;SI->line buffer
  428.     sub    cx,si            ;CX<-length of line
  429.     call    near ptr skip_whitespace ;SI->first non blank
  430.     mov    di,si            ;DI->start of word
  431.     call    near ptr skip_nondelim    ;SI->char after word
  432.     mov    num_remain,cx        ;Remember num chars remaining
  433. ;                     in line
  434.     mov    ax,si            ;
  435.     mov    si,di            ;SI->word
  436.     sub    ax,si            ;AX<-num chars in word
  437.     pop    bx            ;BX->buffer descriptor
  438.     call    near ptr ismacsym    ;Is it in macro/symbol stack ?
  439.     jc    @expand_98        ;Not found so return with CF set
  440. ; The macro or symbol has been found.
  441.  
  442. ; CHeck if it is the macro/symbol string stack
  443.     cmp    bx,offset DGROUP:mac_stk  ;Expanding a macro ?
  444.     jne    @expand_50        ;No, expanding symbol
  445.  
  446.     mov    macro_level,1        ;Indicate expansion going on
  447. ;    Copy linebuf into cur_macro (to remember arguments)
  448.     mov    di,offset DGROUP:cur_macro
  449.     mov    si,offset DGROUP:linebuf
  450.     mov    cx,lastchar
  451.     sub    cx,si            ;CX<-length of linebuf
  452.     mov    cur_macro_len,cx    ;Store length of invocation line
  453.     rep    movsb            ;Remember macro invocation line
  454. ; The current stack string is the first expansion line of the macro
  455.     mov    sp,bp            ;clean up stack
  456.     pop    bp            ;restore registers
  457.     pop    di
  458.     pop    si
  459.     jmp    short get_macro_line    ;Yes, copy it into the linebuf buffer
  460. ;    get_macro_line will return to caller with  the appropriate value
  461. ;    of the carry flag.
  462.  
  463. @expand_50:
  464. ; We are expanding a symbol.
  465. ; num_remain = num chars in linebuf after symbol
  466. ; First we move these characters to the end of the buffer
  467.  
  468.     mov    cx,num_remain        ;CX<-number of chars to move back
  469.  
  470.     call    near ptr makeroom    ;Make room in buffer    
  471.     mov    si,di            ;SI->chars copied to the back
  472. ;    Now copy the string
  473.     mov    ax,offset DGROUP:linebuf ;AX->line buffer
  474.     mov    cx,LINEBUF_SIZE        ;CX<-size of line buffer
  475.     call    near ptr strstk_copy    ;Copy line. Never mind if end
  476. ;                     string overwritten.
  477. ;                     AX<-length of expanded string
  478.     mov    di,offset DGROUP:linebuf
  479.     add    di,ax            ;DI->location after expansion
  480.     mov    cx,num_remain        ;CX<-num chars to copy after expansion
  481.     add    ax,cx            ;ax<-total length of line
  482.     cmp    ax,LINEBUF_SIZE        ;End chars overwritten ?
  483.     jbe    @expand_60
  484.     mov    ax,E_TRUNCATE        ;Truncation error
  485.     jmp    near ptr abort_processing
  486. @expand_60:
  487. ; Copy trailing chars to end of expansion
  488.     rep    movsb
  489.     mov    lastchar,di
  490.     mov    dot,di
  491.     clc                ;Indicate expansion took place
  492. @expand_98:
  493. ; MUST NOT CHANGE CARRY FLAG AFTER THIS POINT.
  494.     mov    sp,bp            ;clean up stack
  495.     pop    bp            ;restore registers
  496.     pop    di
  497.     pop    si
  498.     ret
  499. expand_macro    endp
  500.  
  501.  
  502.  
  503.  
  504. ;+
  505. ; FUNCTION : get_macro_line
  506. ;
  507. ;    Copies a line from the macro stack to linebuf. All parameters
  508. ;    codes (%1, %2 etc.) are replaced with their corresponding
  509. ;    parameters. If this line is the last in the macro definition,
  510. ;    the macro_level flag is reset. This allows the last line in a
  511. ;    macro definition to be treated as a macro itself. In case of
  512. ;    any errors( eg. expansion too long), the macro expansion is
  513. ;    aborted and the input is directed to the keyboard.
  514. ;
  515. ; Parameters:
  516. ;    None.
  517. ;
  518. ; Returns:
  519. ;    CF    = 0 if a line copied to linebuf
  520. ;          1 if no more lines in expansion or no ongoing expansion
  521. ; Register(s) destroyed:
  522. ;    AX,BX,CX,DX
  523. ;-
  524. get_macro_line    proc near
  525.     cmp    macro_level,0        ;Expanding a macro ?
  526.     jne    @get_macro_line_1    ;Yes
  527.     stc                ;CF = 1 for no expansion
  528.     ret
  529. @get_macro_line_1:
  530.     push    si
  531.     push    di
  532.     push    bp
  533.     mov    bp,sp
  534.     sub    sp,LINEBUF_SIZE
  535. tempbuf    equ    <byte ptr [bp-LINEBUF_SIZE]>
  536.  
  537. ; Copy the current string from the macro buffer into temp buffer.
  538.     mov    bx,offset DGROUP:mac_stk ;BX->address of buffer descriptors
  539.     lea    ax,tempbuf        ;AX->destination address
  540.     mov    cx,LINEBUF_SIZE        ;CX<-size of buffer
  541.     call    near ptr strstk_copy    ;Copy macro into temp buffer
  542.                     ;AX<-length of expansion
  543.                     ; No error possible
  544. ; Replace any placeholders in the macro line by the corresponding paramters.
  545. ; copy all chars until first placeholder. Copy argument.
  546. ; Repeat for whole expansion. If at any time there is no place in
  547. ; buffer, then abort.
  548.     xchg    cx,ax            ;CX<-length of expansion
  549.     lea    si,tempbuf        ;SI->tempbuf (macro expansion)
  550.     mov    di,offset DGROUP:linebuf ;DI->linebuf
  551. @get_macro_line_38:
  552.     jcxz    @get_macro_line_60    ;Jump if no more expansion
  553.  
  554. @get_macro_line_40:
  555. ; At the start of the loop, the following hold :
  556. ; DI->next empty location in the linebuf
  557. ; SI->next char of macro expansion to be examined (in tempbuf)
  558. ; CX = remaining number of chars in expansion (> 0)
  559.     mov    al,PLACEHOLDER        ;Going to search for placeholder
  560.     mov    dx,cx            ;DX<-length of remaining expansion
  561.     push    di            ;Save DI
  562.     mov    di,si            ;DI->start point for
  563. ;                     placeholder scan
  564.     repne    scasb            ;Look for placeholder
  565.                     ;assumes! (ES == SS)
  566. ;    CX is number of chars after placeholder
  567.     sub    dx,cx            ;DX<-num chars to be copied
  568.     xchg    dx,cx            ;CX<-num chars to be copied
  569.                     ;DX<-num chars after placeholder
  570.     pop    di            ;DI->destination in linebuf
  571.     rep    movsb            ;Move chars from tempbuf to linebuf
  572.                     ;assumes! (ES == SS)
  573. ;    Note we don't care if the characters at the end of the linebuf
  574. ;    are overwritten.
  575.     mov    cx,dx            ;CX<-remaining number of chars
  576.     jcxz    @get_macro_line_60    ;All chars copied (placeholder
  577.                     ; not found or last char in line)
  578. ;    We have found a placeholder character in tempbuf. SI points to the 
  579. ;    character AFTER the placeholder. Based on the JCXZ above, there
  580. ;    is at least one character after the placeholder. If it is a
  581. ;    char between '0' and '9' then it is a genuine placeholder. If
  582. ;    it is another placeholder character, then a single placeholder
  583. ;    is stored. Else both the placeholder as well as the character
  584. ;    will be stored into linebuf.
  585.     mov    al,[si]            ;AL<-char after placeholder
  586.     cmp    al,PLACEHOLDER        ;Is it a placeholder ?
  587.     jne    @get_macro_line_45    ;No
  588. ;    Skip over second placeholder
  589.     inc    si
  590.     loop    @get_macro_line_40
  591.     jmp    short @get_macro_line_60 ;No more chars in expansion
  592. @get_macro_line_45:
  593.     cmp    al,'9'
  594.     ja    @get_macro_line_40    ;Not a digit
  595.     sub    al,'0'-1        ;Compaer with '0'. At the same
  596. ;                     time translate '0'->1,'1'->2
  597. ;                     ..and so on to '9'->10 since
  598. ;                     getargs counts from 1, not 0.
  599.     jbe    @get_macro_line_40    ;Not a digit
  600.     dec    di            ;Cancel stored PLACEHOLDER char
  601.     push    cx            ;Save CX
  602.     mov    bx,di            ;BX->destination for argument
  603.     mov    dx,LINEBUF_END
  604.     sub    dx,bx            ;DX<-remaining space in buffer
  605.     mov    di,si            ;Save SI in DI
  606.     mov    si,offset DGROUP:cur_macro ;SI->current macro being expanded
  607.     xor    ah,ah            ;AX<-arg number (AL already
  608. ;                     holds actual arg number)
  609.     mov    cx,cur_macro_len    ;Length of original macro string
  610.     call    near ptr getargs    ;Get the argument into linebuf
  611.                     ;AX<-num chars copied, BX unchanged
  612.                     ;CF indicates error condition
  613.     pop    cx            ;Restore CX (num remaining
  614. ;                     chars in macro expansion)
  615.     mov    si,di            ;Restore SI
  616. ;                    (SI->char AFTER placeholder char)
  617.     mov    di,bx            ;Start of copied characters
  618.     jc    @get_macro_line_101    ;Jump if getargs returned error
  619. @get_macro_line_50:
  620.     add    di,ax            ;DI->next destination char in linebuf
  621.     inc    si            ;SI->next char of macro expansion
  622.     loop    @get_macro_line_40    ;Decrement remaing characters
  623.  
  624. @get_macro_line_60:
  625.  
  626.     mov    lastchar,di            ;Update end of line
  627.     mov    dot,di                ;Update cursor
  628. ; Set the lastchar and display end pointers
  629.     IF    0
  630.     Currently no need to set display pointers since macro lines are
  631.     not displayed
  632.     mov    ax,di                ;AX->Potential disp_end
  633.     mov    dx,offset DGROUP:linebuf     ;Potential disp_begin
  634.     call    near ptr set_disp_marks        ;Set the marks
  635.     ENDIF
  636.  
  637. ; Finally check to see if this line is the last in the macro expansion.
  638.     mov    bx,offset DGROUP:mac_stk ;BX->macro stack descriptor
  639.     xor    cx,cx            ;Move to next string in stack
  640.     call    near ptr strstk_fwd_match
  641.     call    near ptr check_separator ;Is this the last line of
  642. ;                      expansion ?
  643.     jne    @get_macro_line_90    ;No
  644.  
  645. ; This was the last line in the macro expansion. Reset macro flag
  646.     mov    macro_level,0        ;Reset flag
  647. @get_macro_line_90:
  648. ;    @unlink
  649.     mov    sp,bp            ;clean up stack
  650.     pop    bp            ;restore registers
  651.     pop    di
  652.     pop    si
  653.     clc                ;Return macro expanded
  654.     ret
  655.  
  656. @get_macro_line_101:
  657. ; truncation error
  658.     mov    ax,E_TRUNCATE        ;Indicate truncation of line
  659.     jmp    near ptr abort_processing
  660.  
  661. get_macro_line    endp
  662.  
  663.  
  664.  
  665.  
  666. ;+
  667. ; FUNCTION : check_separator
  668. ;
  669. ;    Checks to see if the current macro/symbol buffer line is a
  670. ;    separator.
  671. ;
  672. ; Parameters:
  673. ;    BX    = address of macro/symbol stack descriptor
  674. ;
  675. ; Returns:
  676. ;    ZF = 1 if current buffer line is the separator string
  677. ;         0 otherwise.
  678. ;
  679. ; Register(s) destroyed:
  680. ;    AX,CX
  681. ;-
  682. check_separator proc near
  683.     mov    ax,offset DGROUP:separator
  684.     mov    cx,sep_len
  685.     call    near ptr strstk_compare    ;Is it the separator ?
  686. ;    strstk_comapre sets ZF.
  687.     ret
  688. check_separator endp
  689.  
  690.  
  691.  
  692.  
  693. ;+
  694. ; FUNCTION : execute_defs
  695. ;
  696. ;    Called to define a symbol.
  697. ;
  698. ; Parameters:
  699. ;    SI    -> first char in linebuf following this command
  700. ;    CX    == remaining num chars in the line
  701. ;
  702. ; Returns:
  703. ;    Nothing.
  704. ;
  705. ; Register(s) destroyed:
  706. ;    AX,BX,CX,DX
  707. ;-
  708. execute_defs    proc near
  709.     mov    bx,offset DGROUP:sym_stk    ;BX->stack descriptor
  710. ; Push macro name
  711.     call    near ptr push_word        ;Push first word onto
  712. ;                         the stack. Params :
  713. ;                         SI->string
  714. ;                         CX=num chars
  715. ;                         Returns:
  716. ;                         AX<-status code
  717. ;                         SI->char after word
  718. ;                         CX<-num remaining chars
  719.     cmp    ax,0                ;Check status
  720.     jg    @execute_defs_99        ;No word on line,
  721. ;                         ignore command
  722.     jl    @execute_defs_109        ;No room in stack
  723.  
  724. ; Now push the rest of the string as is except that leading whitespace
  725. ; is compressed.
  726.     call    near ptr skip_whitespace    ;Skip leading whitespace
  727.                         ;Params SI, CX
  728.                         ;Returns SI->first
  729. ;                         non-white space char
  730. ;                         CX<-remaining chars
  731. ;                         (maybe 0)
  732.     call    near ptr push_string        ;Push string onto stack
  733. ;                         (maybe null string)
  734.     jc    @execute_defs_109        ;No room in stack
  735.     call    near ptr cmdsym_separate    ;Push separator onto stack
  736.     jc    @execute_defs_109        ;No room in stack
  737. @execute_defs_99:
  738.     ret
  739.  
  740. @execute_defs_109:
  741. ; Error. No room in macro stack.
  742.     call    near ptr cmdsym_cleanup        ;Clear out partial definition
  743.     call    near ptr disp_noroom    ;Display error
  744.     ret
  745. execute_defs    endp
  746.  
  747.  
  748. ;+
  749. ; FUNCTION : execute_defm
  750. ;
  751. ;    Called to define a multiple line macro. This function will
  752. ;    keep reading from the current input source and storing it in
  753. ;    the macro buffer until an `endm' is seen. The ENDM directive
  754. ;    can be followed by any characters (eg. macro name)
  755. ;
  756. ; Parameters:
  757. ;    SI    -> first char in linebuf following this command
  758. ;    CX    == remaining num chars in the line
  759. ;
  760. ; Returns:
  761. ;    Nothing.
  762. ;
  763. ; Register(s) destroyed:
  764. ;    AX,BX,CX,DX
  765. ;-
  766. execute_defm    proc near
  767.     mov    ax,E_NESTED_MACRO    ;Assume error
  768.     cmp    macro_level,0        ;Expanding a macro ?
  769.     je    @execute_defm_1        ;No, jump
  770.     jmp    near ptr abort_processing ;Yes, nested macro error
  771. @execute_defm_1:
  772.     @save    si,di
  773.     push    bp
  774.     mov    bp,sp
  775.     sub    sp,2
  776. err_flag equ <word ptr [bp-2]>
  777.     mov    bx,offset DGROUP:mac_stk    ;BX->stack descriptor
  778.     mov    err_flag,0            ;Initially no errors
  779.  
  780. ; If expanding a macro, ignore all lines until an endm
  781.     cmp    macro_level,0
  782.     jnz    @execute_defm_5            ;Go set error flag
  783.  
  784. ; Push macro name
  785.     call    near ptr push_word        ;Push first word onto
  786. ;                         the stack. Params :
  787. ;                         SI->string
  788. ;                         CX=num chars
  789. ;                         Returns:
  790. ;                         AX<-status code
  791. ;                         SI->char after word
  792. ;                         CX<-num remaining chars
  793.     cmp    ax,0                ;Check status
  794.     jb    @execute_defm_5            ;No room in stack
  795.     je    @execute_defm_40        ;No errors
  796. ; No name for macro. Push a 0 length macro name. Macro can be called by
  797. ; a blank line
  798.     xor    cx,cx                ;CX<-0 (length of string)
  799.     call    near ptr push_string        ;Push onto stack
  800.     jnc    @execute_defm_40        ;No errors
  801.  
  802. @execute_defm_5:
  803.     mov    err_flag,1            ;Indicate error
  804.     call    near ptr cmdmacro_cleanup    ;Cleanup macro fragments
  805.  
  806. @execute_defm_40:
  807. ; Keep reading lines from the input and store in macro buffer unless an error
  808. ; has been previously seen.
  809.  
  810. ;     Prompt is displayed only if reading from the keyboard.
  811.     cmp    source,offset DGROUP:get_kbd_line
  812.     jne    @execute_defm_42
  813.     @DispStr macro_prompt
  814.     call    near ptr reset_line
  815. @execute_defm_42:
  816.     call    near ptr get_next_line
  817.     mov    si,offset DGROUP:linebuf    ;SI->String to push
  818.     mov    cx,lastchar
  819.     sub    cx,si                ;CX<-length of line
  820.     push    si                ;Remember SI and CX
  821.     push    cx
  822.     call    near ptr skip_whitespace    ;SI->first non-white
  823.                         ;CX<-num remaining chars
  824.     jcxz    @execute_defm_50        ;Blank line so go store it
  825.     mov    di,si                ;DI->start of word
  826.     call    near ptr skip_nonwhite
  827.     mov    cx,si
  828.     sub    cx,di                ;CX<-length of word
  829.     xor    ah,ah
  830.     mov    si,offset DGROUP:endm_cmd
  831.     lodsb                    ;AX<-length of endm command
  832.     cmp    cx,ax
  833.     jne    @execute_defm_50        ;Not ENDM
  834.     call    near ptr stre_cmp        ;Compare strings
  835.     jnz    @execute_defm_50
  836. ; ENDM seen.
  837.     cmp    err_flag,1            ;Had we seen an error ?
  838.     jne    @execute_defm_45        ;No
  839.     call    near ptr disp_noroom    ;Display error
  840.     jmp    short @execute_defm_99
  841. @execute_defm_45:
  842. ; End of macro seen. Store macro separator
  843.     call    near ptr cmdmacro_separate
  844.     jmp    short @execute_defm_99
  845.  
  846. @execute_defm_50:
  847. ; Store line in macro buffer
  848.     pop    cx                ;CX<-length of line
  849.     pop    si                ;SI->linebuf
  850.     mov    bx,offset DGROUP:mac_stk    ;BX->stack descriptor
  851.     call    near ptr push_string
  852.     jnc    @execute_defm_40        ;No error
  853.     jmp    short @execute_defm_5        ;Indicate error
  854.  
  855. @execute_defm_99:
  856.  
  857.     mov    sp,bp
  858.     pop    bp
  859.     @restore
  860.     ret
  861. execute_defm endp
  862.  
  863.  
  864.  
  865.  
  866. ;+
  867. ; FUNCTION : cmdmacro_separate,cmdsym_separate,separate
  868. ;
  869. ;    Pushes a separator string onto the stack.
  870. ;
  871. ; Parameters:
  872. ;    None for cmdmacro_separate and cmdsym_separate
  873. ;    BX->buffer descriptor for routine separate
  874. ; Returns:
  875. ;    CF    = 1 if no room in stack
  876. ;          0 otherwise
  877. ;
  878. ; Register(s) destroyed:
  879. ;    AX,BX,CX,DX
  880. ;-
  881. cmdmacro_separate proc near
  882.     mov    bx,offset DGROUP:mac_stk    ;BX->stack descriptor
  883.     jmp    short separate
  884. cmdsym_separate LABEL near
  885.     mov    bx,offset DGROUP:sym_stk    ;BX->stack descriptor
  886. separate    LABEL near
  887.     mov    ax,sep_len            ;AX<-length of separaot string
  888.     mov    dx,offset DGROUP:separator    ;DX->separator string
  889.     mov    cx,1                ;Force push onto stack
  890.     call    near ptr strstk_push        ;Returns status in CF
  891.     ret
  892. cmdmacro_separate endp
  893.  
  894.  
  895.  
  896.  
  897. ;+
  898. ; FUNCTION : cmdmacro_cleanup,cmdsym_cleanup
  899. ;
  900. ;    This function is called to clean up the top of the macro or symbol
  901. ;    stacks when a complete definition cannot be pushed onto the
  902. ;    stack due to lack of space. The routine keeps deleting strings
  903. ;    from the top of the macro stack until it finds a separator string.
  904. ;
  905. ; Parameters:
  906. ;    None.
  907. ;
  908. ; Returns:
  909. ;    Nothing.
  910. ;
  911. ; Register(s) destroyed:
  912. ;    AX,BX,CX,DX
  913. ;-
  914. cmdmacro_cleanup proc near
  915.     mov    bx,offset DGROUP:mac_stk    ;BX->stack descriptor
  916.     jmp    short @cleanup_5
  917. cmdsym_cleanup    LABEL near
  918.     mov    bx,offset DGROUP:sym_stk    ;BX->stack descriptor
  919. @cleanup_5:
  920.     call    near ptr strstk_settop        ;Reset cur pointer
  921. ;                         BX unchanged
  922. ; While the top of stack is not a separator, keep killing strings.
  923. @cleanup_10:
  924.     call    near ptr check_separator
  925. ;    mov    ax,offset DGROUP:separator    ;AX->separator string
  926. ;    mov    cx,sep_len            ;CX<-length of separator
  927. ;    call    near ptr strstk_compare        ;Is this a separator ?
  928.     je    @cleanup_99            ;Yes, then all done
  929.     call    near ptr strstk_kill        ;Kill string
  930.     jmp    short @cleanup_10        ;Keep going
  931. @cleanup_99:
  932.     ret
  933. cmdmacro_cleanup endp
  934.  
  935.  
  936.  
  937.  
  938. ;+
  939. ; FUNCTION : disp_noroom
  940. ;
  941. ;    Displays a message saying there is no room in the macro buffer.
  942. ;
  943. ; Parameters:
  944. ;    None.
  945. ;
  946. ; Returns:
  947. ;    Nothing.
  948. ; Register(s) destroyed:
  949. ;    AX,DX
  950. ;-
  951. disp_noroom proc near
  952.     @DispStr macro_noroom_msg
  953.     ret
  954. disp_noroom endp
  955.  
  956.  
  957.  
  958.  
  959.  
  960.  
  961.  
  962. ;+
  963. ; FUNCTION : search_variable
  964. ;
  965. ;    Called to search the passed string for a variable. A variable is a
  966. ;    sequence of characters starting with the VAR_MARKER character
  967. ;    followed by another VAR_MARKER character. Two marker characters in
  968. ;    succession are left untouched. (This is so that this routine can be
  969. ;    called again to check for more variables.)
  970. ;
  971. ; Parameters:
  972. ;    SI    - points to the string.
  973. ;    AX    - length of the string.
  974. ;
  975. ; Returns:
  976. ;    SI    - Points to first marker character of variable if AX is not 0
  977. ;          else points to end of string.
  978. ;    AX    - length of variable (including marker chars) if
  979. ;          variable is present, else 0
  980. ; Register(s) destroyed:
  981. ;    CX
  982. ;-
  983. search_variable proc near
  984.     @save    di
  985.     mov    cx,ax
  986.     
  987. @search_variable_10:
  988.     mov    di,si                ;DI->string to search
  989. ;                         CX is number of chars
  990. ; Note we are OK if length of line is already 0.    
  991.     mov    al,VAR_MARKER
  992.     repne scasb                ;Search for marker
  993. ; If marker not found or found in last position, CX will be 0.
  994.     jcxz    @search_variable_99
  995. ; DI->char after first marker, CX is remaining number of characters
  996.     mov    si,di
  997. @search_variable_20:
  998.     lodsb                    ;AL<-next char
  999.     dec    cx
  1000.     jcxz    @search_variable_99        ;If there was only one char
  1001. ;                         after the marker, variable
  1002. ;                         not possible
  1003.     cmp    al,VAR_MARKER            ;Is it a marker ?
  1004.     je    @search_variable_10        ;Yes, found a marker, just
  1005. ;                         ignore the pair and keep
  1006. ;                         looking
  1007. ; We have found the start of a variable. Look for its end.
  1008.     inc    di                ;Move past first char of var
  1009.     mov    al,VAR_MARKER
  1010.     repne    scasb                ;Look for it
  1011.     jne    @search_variable_99        ;Not found (CX is 0)
  1012. ; Found a variable
  1013.     dec    si
  1014.     dec    si                ;SI->start marker of var
  1015.     mov    cx,di
  1016.     sub    cx,si                ;CX<-length of var
  1017.  
  1018. @search_variable_99:
  1019.     xchg    ax,cx                ;AX<-length of var
  1020.     @restore
  1021.     ret
  1022. search_variable    endp
  1023.  
  1024.  
  1025.  
  1026.  
  1027. ;+
  1028. ; FUNCTION : get_symbol
  1029. ;
  1030. ;    Hunts through the symbol stack looking for a match for the passed
  1031. ;    symbol. If found, it is returned in passed buffer.
  1032. ;
  1033. ; Parameters:
  1034. ;    SI    - points to the symbol
  1035. ;    AX    - length of symbol
  1036. ;    DI    - points to the expansion buffer
  1037. ;    DX    - size of buffer
  1038. ;
  1039. ; Returns:
  1040. ;    CF    - 1 symbol not found (AX set to 0) or [DI] buffer to small
  1041. ;          to hold expansion (AX contains actual length of expansion).
  1042. ;          0 symbol found. Its expansion is stored in [DI] with
  1043. ;          length in AX.
  1044. ; Register(s) destroyed:
  1045. ;    AX,BX,CX,DX
  1046. ;-
  1047. get_symbol proc near
  1048.     @save    si
  1049.     mov    bx,offset DGROUP:sym_stk    ;BX->symbol stack
  1050.     push    dx                ;Save DX
  1051.     call    near ptr ismacsym        ;Is it a symbol ?
  1052.     pop    cx                ;CX<-size of buffer
  1053.     jnc    @get_symbol_20            ;Yes, go on
  1054.     xor    ax,ax                ;No, not a symbol
  1055.     stc                    ;Set CF to indicate error
  1056.     jmp    short @get_symbol_99
  1057.  
  1058. @get_symbol_20:
  1059. ; Symbol has been found. The current symbol stack pointer has been set to
  1060. ; its expansion string.
  1061.     mov    ax,di                ;AX->buffer
  1062.     call    near ptr strstk_copy        ;Params
  1063. ;                         BX->stack descriptor
  1064. ;                         AX->buffer
  1065. ;                         CX->size of buffer
  1066. ;                         Returns CF set if
  1067. ;                         truncation error.
  1068. ;                         AX always set to length of
  1069. ;                         actual expansion.
  1070. ;    CF set/reset by strstk_copy    
  1071.  
  1072. @get_symbol_99:
  1073.     @restore
  1074.     ret
  1075. get_symbol endp
  1076.  
  1077.  
  1078.  
  1079.  
  1080.  
  1081. ;+
  1082. ; FUNCTION : get_dosenv
  1083. ;
  1084. ;    Hunts through the DOS environment looking for a match for the passed
  1085. ;    string. If found, it is returned in passed buffer.
  1086. ;
  1087. ;    Assumes passed string does NOT contain a NULL byte.
  1088. ;
  1089. ; Parameters:
  1090. ;    SI    - points to the string
  1091. ;    AX    - length of string
  1092. ;    DI    - points to the expansion buffer
  1093. ;    DX    - size of buffer
  1094. ;
  1095. ; Returns:
  1096. ;    CF    - 1 string not found (AX set to 0) or [DI] buffer too small
  1097. ;          to hold expansion (AX contains actual length of expansion).
  1098. ;          0 symbol found. Its expansion is stored in [DI] with
  1099. ;          length in AX.
  1100. ; Register(s) destroyed:
  1101. ;    AX,BX,CX,DX
  1102. ;-
  1103. get_dosenv proc near
  1104.     @save    si,di,es
  1105.     push    bp
  1106.     mov    bp,sp
  1107.     sub    sp,6
  1108. var_len equ <word ptr [bp-2]>
  1109. exp_buf equ <word ptr [bp-4]>
  1110. exp_buf_len equ <word ptr [bp-6]>
  1111.     mov    var_len,ax            ;Save length of string
  1112.     mov    exp_buf,di            ;Save expansion buf address
  1113.     mov    exp_buf_len,dx            ;Save expansion buf length
  1114.  
  1115.     call    near ptr locate_dosenv        ;AX->DOS environment segment
  1116.     or    ax,ax                ;Is it 0?
  1117.     je    @get_dosenv_90            ;Yes, we do not know DOS env
  1118.  
  1119. ; We have the DOS segment
  1120.     mov    es,ax                ;ES->environment segment
  1121.     xor    di,di                ;ES:DI->base of environment
  1122.     mov    cx,8000h            ;Max possible environ size
  1123. ;                         is 32K 
  1124.  
  1125. @get_dosenv_10:
  1126. ; Top of env search loop. DI contains offset into environment. SI points to
  1127. ; string. DI actually points to start of an env var name if any. CX is
  1128. ; remaining bytes in environment (> 0)
  1129.     cmp    byte ptr es:[di],0        ;End of environment?
  1130.     je    @get_dosenv_89            ;Yes
  1131.  
  1132. ; We are just going to call stre_cmp to compare strings. Does not matter if
  1133. ; the environment or environment var ends before then since the comparison
  1134. ; will fail since we are assuming a null byte does not occur in the string
  1135. ; that was passed to us.
  1136.     push    cx                ;Remember how much
  1137. ;                         environment left
  1138.     mov    cx,var_len            ;CX<-length to compare
  1139.     call    near ptr stre_cmp        ;Params DS:SI, ES:DI, CX
  1140.     pop    cx                ;CX<-remaining environment size
  1141.     je    @get_dosenv_50            ;Match so far
  1142.  
  1143. ; No match. Hunt for the next null byte or end of environment.
  1144. @get_dosenv_40:
  1145.     xor    al,al
  1146.     repne    scasb
  1147.     jcxz    @get_dosenv_89            ;If no match for null or
  1148. ;                         match in last byte, exit
  1149.     jmp    short @get_dosenv_10        ;Keep looking
  1150.  
  1151. @get_dosenv_50:
  1152. ; We have a match so far. The next char in the environ must be a '='.
  1153.     mov    ax,var_len
  1154.     add    di,ax
  1155.     sub    cx,ax
  1156.     cmp    byte ptr es:[di],'='
  1157.     jne    @get_dosenv_40            ;No match
  1158.  
  1159. ; We have a match.
  1160.     inc    di                ;DI->start of env expansion
  1161.     push    di                ;Save DI
  1162.     dec    cx                ;CX<-remaining length of env
  1163.     xor    ax,ax
  1164.     repne    scasb                ;Hunt for expansion end
  1165.     mov    ax,di
  1166.     pop    di                ;DI->start of expansion
  1167.     sub    ax,di
  1168.     dec    ax                ;AX<-length of expansion
  1169.     mov    cx,exp_buf_len            ;CX<-size of expansion buffer
  1170.     cmp    cx,ax                ;Is it too small?
  1171.     pushf                    ;Remember flags
  1172.     jb    @get_dosenv_60            ;Copy only that many bytes
  1173.     mov    cx,ax                ;Copy all bytes of expansion
  1174. @get_dosenv_60:
  1175. ; AX contains number of bytes so do not change it after this.
  1176.     push    ds                ;Save DS
  1177.     push    es
  1178.     pop    ds
  1179.     pop    es
  1180.     mov    si,di                ;DS:SI->expansion of env var
  1181.     mov    di,exp_buf            ;ES:DI->expansion buffer
  1182.     rep    movsb
  1183.     push    es
  1184.     pop    ds                ;Restore DS. ES is restored
  1185. ;                         at end of routine.
  1186.     popf                    ;Restore status in CF.
  1187. ;                         AX already contains
  1188. ;                         expansion actual byte count
  1189.     jmp    short @get_dosenv_99
  1190. @get_dosenv_89:
  1191. ; Not found in environment
  1192.     xor    ax,ax
  1193. @get_dosenv_90:
  1194. ; Error. AX must have been already set appropriately.
  1195.     stc
  1196. @get_dosenv_99:
  1197.     mov    sp,bp
  1198.     pop    bp
  1199.     @restore    
  1200.     ret
  1201. get_dosenv endp
  1202.  
  1203.  
  1204.  
  1205. ;+
  1206. ; FUNCTION: replace_var_markers
  1207. ;
  1208. ;    Replaces all double VAR_MARKER characters with single VAR_MARKERs.
  1209. ;    Any solo VAR_MARKERs are deleted (as in DOS batch files).
  1210. ;
  1211. ; Parameters:
  1212. ;    DI    - points to string
  1213. ;    CX    - length of string
  1214. ;
  1215. ; Returns:
  1216. ;    AX - new length of string
  1217. ;
  1218. ; Registers destroyed:
  1219. ;    AX,CX,DX
  1220. ;-
  1221. replace_var_markers proc near
  1222.     @save    si,di
  1223.     mov    dx,cx
  1224.     mov    al,VAR_MARKER
  1225. @replace_var_markers_9:
  1226.     jcxz    @replace_var_markers_99
  1227. @replace_var_markers_10:
  1228. ; CX must not be 0 at this point! Else calculation of DX goes wrong.
  1229.     repne    scasb                ;Look for marker
  1230.     jne    @replace_var_markers_99        ;No more markers in string
  1231. ; Found a marker. Move remaining characters up by 1.
  1232. ; DI->first char to be moved up, CX is number of chars to move up
  1233.     dec    dx                ;Deleted one char
  1234.     jcxz    @replace_var_markers_99        ;No more bytes
  1235.     push    cx                ;Sace byte count
  1236.     push    di                ;Save di
  1237.     mov    si,di
  1238.     dec    di
  1239.     rep    movsb                ;Move chars up
  1240.     pop    di                ;DI->next pos to start hunt
  1241.     pop    cx                ;remaining bytes
  1242.     dec    cx                ;jump over character that
  1243. ;                         now occupies the original
  1244. ;                         marker position.
  1245.     jmp    short @replace_var_markers_9
  1246.  
  1247. @replace_var_markers_99:
  1248.     mov    ax,dx                ;AX<-new string length
  1249.     @restore
  1250.     ret
  1251. replace_var_markers endp
  1252.  
  1253.  
  1254.  
  1255.  
  1256.  
  1257. ;+
  1258. ; FUNCTION : expand_var
  1259. ;
  1260. ;    Expands the variables (if any) present in the current line. A
  1261. ;    variable consists of a series of non-delimiter characters
  1262. ;    between two 'VAR_MARKER' characters. The DOS environment block is
  1263. ;    first checked for the presence of the first such variable in the
  1264. ;    line and if it is present, it is replaced with its value. Otherwise
  1265. ;    the CMDEDIT symbols are checked for match. If there is a match, it
  1266. ;    is replaced with the value of the symbol else, it is replaced by a
  1267. ;    null string. This procedure is repeated until there are no symbols
  1268. ;    in the line. Note that the replacements may themselves contain
  1269. ;    variables which will be replaced in turn. 
  1270. ;
  1271. ; Parameters:
  1272. ;    None.
  1273. ;
  1274. ; Returns:
  1275. ;    CF = 0 if no errors.
  1276. ;    CF = 1 if errors (line too long). AX contains error code.
  1277. ; Register(s) destroyed:
  1278. ;    AX,BX,CX,DX
  1279. ;-
  1280. expand_var proc near
  1281.     @save    si,di
  1282.     push    bp
  1283.     mov    bp,sp
  1284.     sub    sp,LINEBUF_SIZE
  1285. var_exp equ <byte ptr [bp-LINEBUF_SIZE]>
  1286.  
  1287. @expand_var_10:
  1288.     mov    si,offset DGROUP:linebuf    ;SI->current line
  1289.     mov    ax,lastchar
  1290.     sub    ax,si                ;AX<-length of line
  1291.     call    near ptr search_variable    ;Look for a var
  1292. ;                         AX<-length of var
  1293. ;                         SI-> var
  1294.     or    ax,ax                ;AX is length of var
  1295.     je    @expand_var_60            ;No more vars,
  1296. ; First check the DOS environment for the presence of this variable.
  1297.     mov    var_len,ax            ;Store var len in var_len
  1298.     inc    si                ;Move SI past the VAR_MARKER
  1299.     dec    ax
  1300.     dec    ax                ;Do not count the two
  1301. ;                         VAR_MARKER characters
  1302.     lea    di,var_exp            ;DI->buffer
  1303.     mov    dx,LINEBUF_SIZE            ;DX<-size of expansion buffer
  1304.     call    near ptr get_dosenv        ;Params     SI->var, AX length
  1305. ;                             DI->var_exp
  1306. ;                         Returns expansion in [DI]
  1307. ;                         AX length of expansion
  1308.     or    ax,ax                ;Any expansion ?
  1309.     jne    @expand_var_50            ;Yes - go store in line
  1310. ; DOS environment did not have the var, now try the CMDEDIT symbol stack.
  1311.     mov    ax,var_len
  1312.     dec    ax
  1313.     dec    ax                ;Do not count the two
  1314.     mov    dx,LINEBUF_SIZE            ;DX<-size of expansion buffer
  1315.     call    near ptr get_symbol        ;Params SI,AX,DI,DX
  1316. ;                         Returns expansion in [DI],
  1317. ;                         length in AX
  1318. ;    Even if expansion was null, fall thru and replace var with the
  1319. ;    expansion (possibly null)
  1320.  
  1321. @expand_var_50:
  1322. ; Replace the var pointed to by SI of length var_len by the expansion given
  1323. ; in var_exp (with length in AX).
  1324.     dec    si                ;SI->points to marker
  1325.     mov    di,ax                ;Store AX in DI
  1326.     mov    ax,var_len
  1327.     call    near ptr remove_chars        ;SI,AX parameters
  1328.     mov    ax,di                ;Restore length of expansion
  1329.     mov    di,si                ;DI->destination
  1330.     lea    si,var_exp
  1331. ;    push    dot                ;Save position
  1332.     call    near ptr insert_chars        ;SI,DI,AX parameters
  1333. ;    pop    dot
  1334.     jnc    @expand_var_10            ;No error
  1335.     mov    ax,E_TRUNCATE            ;Truncation error
  1336.     jmp    short @expand_var_99        ;Exit, CF already has status
  1337.  
  1338.  
  1339. @expand_var_60:
  1340. ; All variables have been replaced. Now change all double marker characters
  1341. ; to single and delete all solo markers (similar to DOS)
  1342.     mov    di,offset DGROUP:linebuf    ;DI->string
  1343.     mov    cx,lastchar
  1344.     sub    cx,di                ;CX<-length of string
  1345.     call    near ptr replace_var_markers
  1346.     add    ax,di
  1347.     mov    lastchar,ax
  1348.     cmp    dot,ax
  1349.     jb    @expand_var_80
  1350.     mov    dot,ax
  1351. @expand_var_80:
  1352.     mov    dx,offset DGROUP:linebuf
  1353.     call    near ptr set_disp_marks        ;Assume all chars changed.
  1354.     clc                    ;CF<-0 (no errors)
  1355.  
  1356. @expand_var_99:
  1357. ;    CF has status
  1358.     mov    sp,bp
  1359.     pop    bp
  1360.     @restore
  1361.     ret
  1362. expand_var endp
  1363.  
  1364.  
  1365.  
  1366. ;+
  1367. ; FUNCTION: disp_until_sep
  1368. ;
  1369. ;    This function outputs the contents of the specified stack until the
  1370. ;    separator is seen. Each element of the stack is followed by a
  1371. ;    CR-LF. The current pointer is left pointing to the separator.
  1372. ;
  1373. ; Parameters:
  1374. ;    BX    - pointer to stack descriptor
  1375. ;-
  1376. disp_until_sep proc near
  1377.     @save    si,di
  1378.     push    bp
  1379.     mov    bp,sp
  1380.     sub    sp,LINEBUF_SIZE+2
  1381. display_buf equ <byte ptr [bp-LINEBUF_SIZE]>
  1382. sym_kluge equ    <byte ptr [bp-LINEBUF_SIZE-2]> ;Kluge to not output extra
  1383. ;                        space after defs line
  1384.     mov    sym_kluge,1
  1385.  
  1386. @disp_until_sep_10:
  1387. ; Start of loop. Check if the current entry is a separator. If so exit.
  1388. ; Else display the line.
  1389.     call    near ptr check_separator
  1390.     je    @disp_until_sep_99        ;Yes, all done
  1391. ; Copy the string into the display buffer and display it.
  1392.     lea    ax,display_buf
  1393.     mov    cx,LINEBUF_SIZE
  1394.     call    near ptr strstk_copy
  1395.     xchg    cx,ax                ;CX<-length of string
  1396.     push    bx                ;Save BX
  1397.     lea    dx,display_buf            ;DX->string, CX is count
  1398.     call    near ptr output_counted_string
  1399.     pop    bx
  1400.     cmp    bx,offset DGROUP:mac_stk
  1401.     jne    @disp_until_sep_30
  1402.     call    near ptr output_newline
  1403.     jmp    short @disp_until_sep_32
  1404. @disp_until_sep_30:
  1405.     cmp    sym_kluge,0
  1406.     je    @disp_until_sep_32
  1407.     @DispCh    ' '
  1408. @disp_until_sep_32:
  1409.     xor    cx,cx
  1410.     call    near ptr strstk_fwd_match
  1411.     mov    sym_kluge,0
  1412.     jmp    short @disp_until_sep_10
  1413.  
  1414. @disp_until_sep_99:
  1415.     mov    sp,bp
  1416.     pop    bp
  1417.     @restore
  1418.     ret
  1419. disp_until_sep endp
  1420.  
  1421.  
  1422.  
  1423. ;+
  1424. ; FUNCTION: output_symbols,output_macros
  1425. ;
  1426. ;    Outputs to standard output the current buffer contents of
  1427. ;    the symbol/macro buffer.
  1428. ;
  1429. ; Parameters:
  1430. ;    None.
  1431. ;-
  1432. output_macsym proc near
  1433. output_symbols LABEL near
  1434.     mov    bx,offset DGROUP:sym_stk
  1435.     mov    ax,offset DGROUP:defs
  1436.     jmp    short @output_macsym_5
  1437. output_macros LABEL near
  1438.     mov    bx,offset DGROUP:mac_stk
  1439.     mov    ax,offset DGROUP:defm
  1440. @output_macsym_5:
  1441.     @save    si,di
  1442.     mov    di,ax                ;DI->'defs' or 'defm'
  1443.     call    near ptr strstk_save_cur    ;Save current pointer.
  1444. ;                         Probably not necessary for
  1445. ;                         symbols, but do anyway
  1446.     call    near ptr strstk_setbot        ;Go to bottom of stack
  1447.  
  1448.     call    near ptr output_newline
  1449.  
  1450. @output_macsym_10:
  1451. ; Start of loop. One entire macro or symbol has been processed so far.
  1452. ; The current stack pointer is at the separator. First skip over the separator.
  1453.     xor    cx,cx
  1454.     call    near ptr strstk_fwd_match
  1455. ; Try to move one more to make sure there is another string
  1456.     xor    cx,cx
  1457.     call    near ptr strstk_fwd_match
  1458.     jc    @output_macsym_99        ;End of buffer
  1459. ; OK move back to get the name of the symbol / macro.
  1460.     xor    cx,cx
  1461.     call    near ptr strstk_bck_match
  1462. ; Now output either 'defs' or 'defm'
  1463.     mov    dx,di
  1464.     inc    dx                ;Point to string
  1465.     xor    cx,cx
  1466.     mov    cl,[di]                ;Length of string
  1467.     push    bx                ;Save BX
  1468.     call    near ptr output_counted_string    ;Show command string
  1469.     @DispCh    ' '                ;followed by a space
  1470.     pop    bx
  1471. ;    Now display all strings until a separator is seen
  1472.     call    near ptr disp_until_sep
  1473.  
  1474. ; Now if we are showing a macro, output the 'endm', else a newline
  1475.     cmp    bx,offset DGROUP:mac_stk    ;Macro ?
  1476.     jne    @output_macsym_70        ;No, repeat loop
  1477. ;    Output the 'endm'
  1478.     push    bx
  1479.     mov    bx,offset DGROUP:endm_cmd
  1480.     xor    cx,cx
  1481.     mov    cl,[bx]
  1482.     mov    dx,bx
  1483.     inc    dx
  1484.     call    near ptr output_counted_string    ;AX,BX,CX,DX destroyed
  1485.     pop    bx
  1486.     call    near ptr output_newline        ;Separate each macro with a
  1487. ;                         newline 
  1488.  
  1489. @output_macsym_70:
  1490.     call    near ptr output_newline
  1491.     jmp    @output_macsym_10
  1492.  
  1493. @output_macsym_99:
  1494.     call    near ptr strstk_restore_cur    ;Restore the current pointer
  1495.     @restore
  1496.     ret
  1497. output_macsym endp
  1498.  
  1499.  
  1500.  
  1501. ;+
  1502. ; FUNCTION: execute_cmdstat
  1503. ;
  1504. ;    Displays the current macro and symbol buffer contents.
  1505. ;
  1506. ;
  1507. ; Parameters:
  1508. ;    SI    -> first char in linebuf following this command
  1509. ;    CX    == remaining num chars in the line
  1510. ;
  1511. ; Returns:
  1512. ;    Nothing.
  1513. ;
  1514. ; Register(s) destroyed:
  1515. ;    AX,BX,CX,DX
  1516. ;-
  1517. execute_cmdstat proc near
  1518.     call    near ptr output_symbols
  1519.     call    near ptr output_macros
  1520.     call    near ptr output_newline
  1521.     ret
  1522. execute_cmdstat endp
  1523.  
  1524.  
  1525. CSEG    ENDS
  1526.  
  1527.     END
  1528.  
  1529.