home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / magazine / pcmagazi / 1990 / 16 / strstack.asm < prev    next >
Assembly Source File  |  1990-04-29  |  27KB  |  982 lines

  1. ; STRSTACK.ASM
  2. ; (c) 1989, 1990 Ashok P. Nadkarni
  3. ;
  4. ; Functions to implement the string stack object for CMDEDIT. Small Model only.
  5. ;
  6.  
  7.     INCLUDE common.inc
  8.     INCLUDE general.inc
  9. STRSTACK_ASM    EQU    1
  10.  
  11.  
  12. CSEG    SEGMENT PARA PUBLIC 'CODE'
  13. CSEG    ENDS
  14.  
  15. DGROUP    GROUP    CSEG
  16.  
  17.     INCLUDE    buffers.inc
  18.  
  19. CSEG    SEGMENT PARA PUBLIC 'CODE'
  20.  
  21.     ASSUME    CS:DGROUP, DS:DGROUP, SS:DGROUP, ES:DGROUP
  22.  
  23.     EXTRN    stre_cmp:PROC
  24.  
  25. ;+
  26. ; FUNCTION : strstk_init
  27. ;
  28. ;    Initializes a $STRING_STACK descriptor. Note that buffer will contain
  29. ;    overhead as well as the actual data itself.
  30. ;
  31. ; Parameters:
  32. ;    BX := address of descriptor
  33. ;    AX := address of buffer
  34. ;    CX := buffer size
  35. ;
  36. ; Returns:
  37. ;    AX = 0 if success
  38. ;    else -1
  39. ;
  40. ; Register CX destroyed.
  41. ;-
  42.  
  43. strstk_init proc near
  44.  
  45.     cmp    cx,2        ;Need at least 2 bytes in buffer (sentinel)
  46.     jb    @strstk_init_90    ;
  47.     cmp    cx,32767    ; and LESS than 32767 (Need to represent
  48.     jnc    @strstk_init_90 ; + and - differences between first location
  49.                 ; and first location beyond buffer in 16 bits)
  50.     mov    [bx].low_end,ax    ;starting address
  51.     add    cx,ax        ;Location after last addressable byte of buffer
  52.                 ;Various functions assume byte beyond last
  53.                 ;buffer location is addressable without
  54.                 ;segment wraparound.
  55.     jc    @strstk_init_90    ;Segment wraparound.
  56.     dec    cx        ;CX->Last byte in buffer
  57.     mov    [bx].high_end,cx
  58.     call    near ptr strstk_reset
  59.     xor    ax,ax        ;success
  60.     jmp    short @strstk_init_99
  61. @strstk_init_90:         ;Buffer too small or 
  62.     mov    ax,-1         ; overflow (add ax,cx instruction)
  63. @strstk_init_99:
  64.     ret
  65.  
  66. strstk_init endp
  67.  
  68.  
  69.  
  70. ;+
  71. ; FUNCTION : strstk_reset
  72. ;
  73. ;    Resets a specified stack to its initial state. The sentinel at the
  74. ;    bottom of the stack is assumed to be already there.
  75. ;
  76. ; Parameters :
  77. ;    BX := address of buffer descriptor
  78. ;
  79. ; Returns :
  80. ;    Nothing.
  81. ;
  82. ; Registers destroyed:
  83. ;    AX
  84. ;-
  85. strstk_reset proc near
  86.     mov    ax,[bx].low_end
  87.     xchg    ax,bx
  88.     mov    word ptr [bx],0    ;header & trailer for sentinel null string
  89.     xchg    ax,bx
  90.     inc    ax        ;
  91.     mov    [bx].top,ax    ;Stack top (sentinel null)
  92.     mov    [bx].cur,ax    ;Current string (sentinel null)
  93.     mov    [bx].savecur,ax
  94.     mov    ax,[bx].high_end        ;AX->last byte of buffer
  95.     inc    ax                ;AX->first byte beyond buffer
  96.     mov    [bx].topmark,ax            ;No markers
  97.     ret
  98. strstk_reset endp
  99.  
  100.  
  101. ;+
  102. ; FUNCTION: strstk_save_cur
  103. ;
  104. ;    This function saves the cur pointer in the cursave field of the
  105. ;    descriptor structure. It can then be restored with the
  106. ;    strstk_restore_cur call. It is the caller's responsibility to
  107. ;    ensure that the stack does not change in the meanwhile in such a
  108. ;    way as to invalidate the pointer into it. Generally do not do
  109. ;    anything except move the cur pointer around to access different
  110. ;    strings.
  111. ;
  112. ; Parameters :
  113. ;    BX := address of buffer descriptor
  114. ;
  115. ; Returns :
  116. ;    Nothing.
  117. ;
  118. ; Registers destroyed:
  119. ;    AX
  120. strstk_save_cur proc near
  121.     mov    ax,[bx].cur
  122.     mov    [bx].savecur,ax
  123.     ret
  124. strstk_save_cur endp
  125.  
  126. ;+
  127. ; FUNCTION: strstk_restore_cur
  128. ;
  129. ;    This function restores the cur pointer from the cursave field of the
  130. ;    descriptor structure where it was stored through the
  131. ;    strstk_save_cur call. It is the caller's responsibility to
  132. ;    ensure that the stack does not change in the meanwhile in such a
  133. ;    way as to invalidate the pointer into it. Generally do not do
  134. ;    anything except move the cur pointer around to access different
  135. ;    strings. Also, make sure you do a strstk_save_cur before every
  136. ;    strstk_restore_cur.
  137. ;
  138. ; Parameters :
  139. ;    BX := address of buffer descriptor
  140. ;
  141. ; Returns :
  142. ;    Nothing.
  143. ;
  144. ; Registers destroyed:
  145. ;    AX
  146. strstk_restore_cur proc near
  147.     mov    ax,[bx].savecur
  148.     mov    [bx].cur,ax
  149.     ret
  150. strstk_restore_cur endp
  151.  
  152.  
  153.  
  154. ;+
  155. ; FUNCTION : strstk_space
  156. ;
  157. ;    Returns the available space in the buffer. This is 2 less than the
  158. ;    actual number of bytes remaining since we need space for a header
  159. ;    and a trailer for at least one string.
  160. ;
  161. ; Parameters:
  162. ;    BX := address of buffer descriptor
  163. ;
  164. ; Returns:
  165. ;    CF :=    1 if no space in stack even for a header or trailer
  166. ;        0 otherwise
  167. ;    AX :=    Available space (length of max string that can be fitted)
  168. ;        if CF = 0, if CF = 1, AX is indeterminate
  169. ;-
  170. strstk_space proc near
  171.     mov    ax,[bx].topmark    ;Points BEYOND last usable byte
  172.     sub    ax,[bx].top    ;actual space in buffer + 1
  173.                 ;Need to deduct 1+2(for header/trailer)
  174.     sub    ax,3        ;Don't use multiple 'dec ax' here
  175.                 ; since it does not set CF.
  176.     ret
  177. strstk_space endp
  178.  
  179.  
  180.  
  181.  
  182. ;+
  183. ; FUNCTION : strstk_settop
  184. ;
  185. ;    Sets the current string pointer to point to the newest (top
  186. ;    of stack) string.
  187. ;
  188. ; Parameters:
  189. ;    BX :=    address of buffer descriptor 
  190. ;
  191. ; Returns:
  192. ;    Nothing.
  193. ;
  194. ; Register AX destroyed.
  195. ;-
  196. strstk_settop proc near
  197.     mov    ax,[bx].top
  198.     mov    [bx].cur,ax
  199.     ret
  200. strstk_settop endp
  201.  
  202.  
  203.  
  204. ;+
  205. ; FUNCTION : strstk_setbot
  206. ;
  207. ;    Sets the current string pointer to point to the oldest (bottom
  208. ;    of stack) string. If the stack is not empty, the null string at
  209. ;    the bottom is ignored.
  210. ;
  211. ; Parameters:
  212. ;    BX :=    address of buffer descriptor 
  213. ;
  214. ; Returns:
  215. ;    Nothing.
  216. ;
  217. ; Registers AX,DX destroyed.
  218. ;-
  219. strstk_setbot proc near
  220.     mov    ax,[bx].low_end
  221.     inc    ax
  222.     cmp    ax,[bx].top    ;Is stack empty ?
  223.     je    @strstk_setbot_99 ;Yes
  224.     inc    ax        ;Point ax to header of oldest string
  225.     xchg    ax,si        ;Store in SI
  226.     mov    dl,[si]        ;get length of string
  227.     xor    dh,dh
  228.     add    si,dx        ;Point SI to last byte in string
  229.     inc    si        ;Point SI at trailer
  230.     xchg    ax,si        ;Restore SI and set AX to point to trailer
  231. @strstk_setbot_99:
  232.     mov    [bx].cur,ax    ;Set current pointer
  233.     ret
  234. strstk_setbot endp
  235.  
  236.  
  237.  
  238. ;+
  239. ; FUNCTION : strstk_kill
  240. ;
  241. ;    Deletes the "current" string from the stack. All strings above it
  242. ;    are moved up. "current" string is updated to point to the string
  243. ;    above the deleted string unless the topmost string was deleted
  244. ;    in which case it is set to top of stack. 
  245. ;
  246. ;    Any markers pointing at the deleted string are updated to
  247. ;    point to the new "current" string. Any markers pointing above
  248. ;    the deleted string updated to keep pointing to their
  249. ;    respective strings even after the latter are moved down.
  250. ;    Naturally, the marks below the deleted string do not change.
  251. ;
  252. ; Parameters:
  253. ;    BX :=    address of buffer descriptor
  254. ;
  255. ; Returns:
  256. ;    Nothing
  257. ;
  258. ; Registers AX,CX,DX destroyed
  259. ;-
  260. strstk_kill proc near
  261.     @save    si,di
  262.  
  263.     mov    si,[bx].cur
  264.     mov    ax,[bx].low_end    ;When current points to the sentinel string,
  265.     inc    ax        ; exit without deleting it
  266.     cmp    ax,si        ;Sentinel current ?
  267.     je    @strstk_kill_99 ;Yes, exit
  268.  
  269.     mov    cx,[bx].top    ;topmost occupied location
  270.     mov    dx,cx        ;remember it
  271.     sub    cx,si        ;Num bytes to be moved into vacated positions
  272.  
  273.     xor    ax,ax
  274.     mov    di,si        ;di will be used to point to the header of
  275.                 ; the deleted string.
  276.     lodsb            ;Get the string length into AX.
  277.                 ;At the same time SI now points to header
  278.                 ;of the string following the condemned string.
  279.     inc    ax
  280.     sub    di,ax        ;di now points to the condemned string header.
  281.     inc    ax
  282.     push    ax        ;Remember how many bytes are to be removed
  283.     sub    dx,ax        ;Top of stack is now (length of string + 2)
  284.     mov    [bx].top,dx    ; below the original top
  285.  
  286.     jcxz    @strstk_kill_50 ;If # bytes to be moved is 0, then the
  287.                 ;deleted string was at the top of the stack.
  288.     push    di        ;remember header position
  289.     rep    movsb        ;Copy CX bytes down into vacated positions
  290.     pop    si        ;Header position of new "current" string
  291.                 ; (same location as header of old current)
  292.     lodsb            ;SI points to first byte of string
  293.     xor    ah,ah        ; and AL = length of new current string.
  294.     add    ax,si        ;AX now points to trailer of new "current"
  295.     jmp    short @strstk_kill_90 ;exit
  296.  
  297. @strstk_kill_50:        ;Deleted element was top of stack
  298.     xchg    ax,dx        ;ax = top, we want cur to be == top
  299.  
  300. @strstk_kill_90:
  301.     xchg    ax,[bx].cur    ;cur = top, ax = old current
  302.     pop    cx        ;Restore the removed byte count
  303.                 ; (counterpart of the 'push ax' above)
  304.     IF    WANT_MARKERS
  305.     call    strstk_update_marks ;Update marks for strings that were moved
  306.                     ;  ax == old cur, cx == displacement
  307.     ENDIF
  308. @strstk_kill_99:
  309.     @restore
  310.     ret
  311. strstk_kill endp
  312.  
  313.  
  314.  
  315.  
  316. ;+
  317. ; FUNCTION : strstk_push
  318. ;
  319. ;    Pushes a string onto the top of the stack. If the force flag parameter
  320. ;    is set to any value other than 0, one or more strings at the bottom
  321. ;    of the stack are deleted to make room fir the new string. If the force
  322. ;    flag is 0, then an error is returned if there is not sufficient room
  323. ;    in the stack. An error is also returned if the string is bigger than
  324. ;    stack size. The stack is left unaltered for both error conditions.
  325. ;
  326. ; Parameters:
  327. ;    BX :=    address of buffer stack descriptor
  328. ;    AL :=    Length of string
  329. ;    CX :=    force flag
  330. ;    DX :=    address of string to be pushed
  331. ;
  332. ; Returns:
  333. ;    Carry flag is set if error (not enough stack space), else it is clear.
  334. ;
  335. ; Registers AX,CX,DX destroyed.
  336. ;-
  337. strstk_push proc near
  338.     @save    si,di
  339.     xor    ah,ah        ;Clear high byte of length.
  340.     mov    si,dx        ;SI := source string
  341.     push    ax        ;save length
  342.     jcxz    @strstk_push_10    ;Jump if force flag is 0
  343.     call    strstk_makespace ;Make sure enough space, else make space
  344.     pop    cx        ;restore length
  345.     jc    @strstk_push_99    ;Error return by strstk_makespace
  346.     jmp    short @strstk_push_20 ;Everything OK, go push string
  347. @strstk_push_10:
  348.     call    near ptr strstk_space    ;Find out how much space is left
  349.     pop    cx        ;Restore string length
  350.     jb    @strstk_push_99        ;Not even enough for header/trailer,
  351. ;                     error  return
  352.     cmp    ax,cx        ;Enough space on stack ?
  353.     jb    @strstk_push_99    ;Nope, error return
  354. @strstk_push_20:        ;OK, copy string onto stack
  355.     mov    di,[bx].top    ;DI := last occupied location on stack
  356.     inc    di        ;DI := address of header for new string
  357.     mov    ax,cx        ;Get length into AL
  358.     stosb            ;Store length in header
  359.      rep    movsb        ;Copy string
  360.     mov    [bx].top,di    ;New top of stack is trailer of topmost string
  361.     mov    [bx].cur,di    ;Ditto for current string
  362.     stosb            ;Store length in trailer
  363.     clc            ;Success return
  364. @strstk_push_99:
  365.     @restore
  366.     ret
  367. strstk_push endp
  368.  
  369.  
  370. ;+
  371. ; FUNCTION : strstk_fwd_match
  372. ;
  373. ;    Searches towards the top of the stack, starting from the string
  374. ;    above the current string looking for a string that has the specified
  375. ;    pattern as a prefix.  If the pattern length is 0, then the match is
  376. ;    universal and the new current string is simply the one immediately
  377. ;    above the current one.  The function can thus be used to move the
  378. ;    cur pointer up the stack one string at a time. If the current
  379. ;    string is at the top of the stack, the cur pointer remains
  380. ;    unchanged.
  381. ;
  382. ; Parameters:
  383. ;    BX :=    address of buffer stack descriptor
  384. ;    AX :=    Address of pattern
  385. ;    CX :=    Length of pattern 
  386. ; Returns:
  387. ;    CF  = 1 if no match or if at top of stack
  388. ;        = 0 if success
  389. ;
  390. ; Registers AX,CX destroyed.
  391. ;-
  392. strstk_fwd_match proc near
  393.     @save    si,di
  394.     mov    si,[bx].cur    ;SI:=current ptr
  395.     mov    di,AX        ;Pattern address
  396.     
  397. @strstk_fwd_match_10:
  398.     cmp    si,[bx].top    ;Are we at top of stack ?
  399.     je    @strstk_fwd_match_90 ;Yes, error exit
  400.     inc    si        ;SI:=point to next header
  401.     lodsb
  402.     xor    ah,ah        ;AX := string length
  403.     cmp    ax,cx        ;Is the pattern longer than string ?
  404.     jnb    @strstk_fwd_match_70 ;If not go try a match
  405.                 ;Yes, then try next string
  406.     add    si,ax        ;Point to trailer
  407.     jmp    short @strstk_fwd_match_10 ;and loop back    
  408. @strstk_fwd_match_70:        ;OK, see if pattern is the string prefix
  409.     push    cx        ;Remember pattern length
  410.     push    ax        ;Remember string length
  411.     call    near ptr stre_cmp ;Check if pattern is a prefix
  412.     pop    ax        ;Restore string length
  413.     mov    cx,ax        ;Temp storage
  414.     lahf            ;Save value returned by stre_cmp
  415.     add    si,cx        ;SI := trailer
  416.     pop    cx        ;Restore pattern length
  417.     sahf            ;Restore stre_cmp result
  418.     jne    @strstk_fwd_match_10 ;No match, loop back
  419.     clc            ;No errors
  420.     jmp    short @strstk_fwd_match_99
  421.  
  422. @strstk_fwd_match_90:
  423.     stc            ;Error return
  424. @strstk_fwd_match_99:
  425.     mov    [bx].cur,si
  426.     @restore
  427.     ret
  428. strstk_fwd_match endp
  429.  
  430.  
  431. ;+
  432. ; FUNCTION : strstk_bck_match
  433. ;
  434. ;    Searches towards the bottom of the stack, starting from the string
  435. ;    below the current string looking for a string that has the specified
  436. ;    pattern as a prefix.  If the pattern length is 0, then the match is
  437. ;    universal and the new current string is simply the one immediately
  438. ;    below the current one.  The function can thus be used to move the
  439. ;    cur pointer down the stack one string at a time. If the current
  440. ;    string is at the bottom of the stack, the cur pointer remains
  441. ;    unchanged.
  442. ;
  443. ; Parameters:
  444. ;    BX :=    address of buffer stack descriptor
  445. ;    AX :=    Address of pattern
  446. ;    CX :=    Length of pattern 
  447. ; Returns:
  448. ;    CF  = 1 if no match or if at bottom of stack
  449. ;        = 0 if success
  450. ;
  451. ; Registers AX,CX destroyed.
  452. ;-
  453. strstk_bck_match proc near
  454.     @save    si,di
  455.     push    bp
  456.     mov    bp,sp
  457.     sub    sp,2
  458. sentinel EQU <word ptr [bp-2]>
  459.     push    ax        ;remember pattern address
  460.     mov    di,[bx].low_end    ;Buffer bottom
  461.     inc    di
  462.     mov    si,[bx].cur    ;SI:=current pointer
  463.     cmp    si,di        ;At stack bottom ? ( low_end + 1 == cur)
  464.     je    @strstk_bck_match_90 ;If so exit,
  465.                 ; (the 'push AX' is cleaned up by unlink)
  466.     mov    sentinel,di    ;remember sentinel value
  467.  
  468.     pop    di        ;Restore pattern address
  469.  
  470. ; Prime for loop below
  471.     xor    ah,ah
  472.     mov    al,[si]        ;AX<-length of current string
  473.     sub    si,ax        ;SI->start of string
  474.  
  475. @strstk_bck_match_9:
  476. ; Loop begin. SI points to the first byte of string last compared. This
  477. ; cannot be the sentinel string.
  478.     dec    si        ;SI->header of current string
  479.     dec    si        ;SI->trailer of previous string
  480.  
  481. @strstk_bck_match_10:
  482. ; At this point SI points to the trailer of string to try'n'match
  483.     cmp    si,sentinel    ;Are we at bottom ?
  484.     je    @strstk_bck_match_90 ;Yes, exit
  485.     mov    al,[si]        ;String length
  486.     xor    ah,ah
  487.     sub    si,ax        ;Point to first byte of string
  488.     cmp    ax,cx        ;Is the pattern longer than string ?
  489.     jb    @strstk_bck_match_9 ;Yes, then try next string
  490.                 ;OK, try see if pattern is the string prefix
  491.     push    cx        ;Remember pattern length
  492.     call    near ptr stre_cmp ;Check if pattern is a prefix
  493.     pop    cx
  494.     jne    @strstk_bck_match_9 ;Not a prefix, go try next one
  495.     xor    ax,ax
  496.     mov    al,-1[si]    ;AX<-length of matched string
  497.     add    si,ax        ;Point SI to trailer of matched string
  498. ;     Successful match, return with CF clear.
  499. ;    Carry flag is guaranteed clear since no o'flow is possible in
  500. ;    the add si,ax instruction. Hence comment out the following clc.
  501. ;    clc
  502.     jmp    short @strstk_bck_match_99
  503. @strstk_bck_match_90:
  504.     stc            ;Error return
  505.  
  506. @strstk_bck_match_99:
  507.     mov    [bx].cur,si    ;Update current string pointer
  508.     mov    sp,bp
  509.     pop    bp
  510.     @restore
  511.     ret
  512. strstk_bck_match endp
  513.  
  514.  
  515.  
  516.  
  517. ;+
  518. ; FUNCTION : strstk_fwd_find,strstk_bck_find
  519. ;
  520. ;    Searches towards the top/bottom of the stack, starting from the string
  521. ;    above/below the current string looking for a string that is the same
  522. ;    as the specified one. If the current string is at the top/bottom
  523. ;    the stack, the cur pointer remains unchanged.
  524. ;
  525. ; Parameters:
  526. ;    BX :=    address of buffer stack descriptor
  527. ;    AX :=    Address of string
  528. ;    CX :=    Length of string
  529. ; Returns:
  530. ;    CF  = 1 if no match or if at top/bottom of stack
  531. ;        = 0 if success
  532. ;
  533. ; Registers AX,CX,DX destroyed.
  534. ;-
  535. strstk_find proc near
  536. strstk_fwd_find LABEL near
  537.     push    di            ;Save di
  538.     push    si            ;and si
  539.     mov    di,offset CSEG:strstk_fwd_match
  540.     jmp    short @strstk_find_8
  541. strstk_bck_find LABEL near
  542.     push    di            ;Save di
  543.     push    si            ;and si
  544.     mov    di,offset CSEG:strstk_bck_match
  545. @strstk_find_8:
  546.     mov    si,ax            ;Save string address in SI
  547. @strstk_find_10:
  548. ; Loop start
  549.     mov    ax,si            ;AX->string
  550.     push    cx            ;Save string length
  551.     call    di             ;Look for prefix
  552.     pop    cx            ;Restore string length
  553.     jc    @strstk_find_99        ;Not found
  554.     push    cx
  555.     xor    cx,cx            ;Don't want to copy, just need size
  556.     call    near ptr strstk_copy    ;AX<-length of current string
  557.     pop    cx            ;Restore passed length
  558.     cmp    ax,cx            ;Strings match ?
  559.     jne    @strstk_find_10        ;Lengths not same, keep looking
  560.     clc                ;Strings same
  561. @strstk_find_99:
  562.     pop    si
  563.     pop    di
  564.     ret
  565. strstk_find endp
  566.  
  567.  
  568.  
  569. ;+
  570. ; FUNCTION : strstk_makespace
  571. ;
  572. ;    Deletes enough strings from the bottom of the stack to make room
  573. ;    for a string of the length specified in AX. If the stack size is
  574. ;    smaller than the requested size, the stack is left unchanged and
  575. ;    an error indication returned. If there is already enough room in
  576. ;    the stack, the stack is left unchanged. In both cases the current
  577. ;    string ptr is updated to point to the top of the stack. Note that
  578. ;    any marks, if present, reduce the space that is available. 
  579. ;
  580. ;    The routine is not very efficient since it keeps calling strstk_kill
  581. ;    rather than deleting the required number in one shot. But it is
  582. ;    more compact.
  583. ; Parameters:
  584. ;    BX :=    pointer to buffer descriptor
  585. ;    AX :=    requested length
  586. ; Returns:
  587. ;    CF    = 1 if error (requested length to large for stack)
  588. ;        = 0 success
  589. ;     Also changes current string pointer to point to top of stack.
  590. ; Registers AX,CX,DX destroyed.
  591. ;-
  592. strstk_makespace proc near
  593.     @save    si,di
  594.     xchg    ax,si        ;Save requested length in SI
  595.     call    strstk_size    ;AX := size of largest string that can
  596.                 ; fit in an empty stack
  597.     cmp    ax,si        ;Smaller than requested length ?
  598.     jb    @strstk_makespace_99 ;Yes, error exit
  599.     call    strstk_setbot    ;Set current string to bottommost
  600.  
  601. ;Now keep iterating until enough strings have been deleted. Since we 
  602. ;have already checked that the stack is large enough, the loop below
  603. ;is guaranteed to terminate.
  604.  
  605. @strstk_makespace_10:
  606. ; Note that the strstk_space routine returns a 0 if there are less than
  607. ; the 2 bytes required for header/trailer. The carry flag must be
  608. ; checked to distinguish this from the situation where there is room
  609. ; for a null string (exactly 2 bytes available)
  610.     call    near ptr strstk_space    ;AX := available space
  611.     jnc    @strstk_makespace_70    ;Jump if > 2 bytes available
  612. @strstk_makespace_65:
  613.     call    strstk_kill        ;If not delete one more
  614.     jmp    short @strstk_makespace_10 ;And keep trying
  615. @strstk_makespace_70:
  616.     cmp    ax,si            ;Enough space available ?
  617.     jb    @strstk_makespace_65    ;Yes, exit
  618. @strstk_makespace_99:
  619.     call    near ptr strstk_settop
  620.     @restore
  621.     ret
  622. strstk_makespace endp
  623.  
  624.  
  625. ;+
  626. ; FUNCTION : strstk_copy
  627. ;
  628. ;    Returns a copy of the current stack entry. Stack is unchanged.
  629. ;    If the stack is empty, a 0 length string is returned.
  630. ;    If the user buffer is not large enough, as many characters as
  631. ;    possible are copied into it. AX reflects the length of the actual
  632. ;    string and not just the copied part and the carry flag is set.
  633. ;    Thus this routine can also be used to find the length of the current
  634. ;    string by passing it a zero length buffer.
  635. ;
  636. ; Parameters:
  637. ;    BX    := pointer to buffer descriptor
  638. ;    AX    := starting address of location into which the string
  639. ;            is to be copied
  640. ;    CX    := size of buffer
  641. ;
  642. ; Returns:
  643. ;    AX    = length of current string (ACTUAL LENGTH, NOT COPIED LENGTH)
  644. ;    CF    = 0 if the user buffer is large enough
  645. ;        = 1 if user buffer too small
  646. ;          As many chars as possible are returned in the use buffer.
  647. ; Register CX destroyed.
  648. ;-
  649. strstk_copy proc near
  650.     @save    si,di
  651.     mov    di,ax        ;Destination buffer
  652.     mov    si,[bx].cur    ;SI := trailer of current string
  653.     mov    al,[si]
  654.     xor    ah,ah        ;AX:=length of current string
  655.     sub    si,ax        ;SI->first byte of string
  656.     cmp    cx,ax        ;User buffer large enough ?
  657.     jb    @strstk_copy_5    ;No, error return
  658.                 ;Note empty stack case automatically handled.
  659.     mov    cx,ax        ;CX<-length of string to copy
  660. @strstk_copy_5:
  661.     pushf            ;Save carry flags
  662. ;    CX == number of bytes to copy
  663.     rep    movsb
  664.     popf            ;Restore CF
  665.     @restore
  666. ;    Return AX = length of actual string
  667.     ret
  668. strstk_copy endp
  669.  
  670.  
  671.  
  672. ;+
  673. ; FUNCTION : strstk_size
  674. ;
  675. ;    Returns the maximum size string that can fit in the buffer if all
  676. ;    strings are deleted (but not markers).
  677. ;
  678. ; Parameters:
  679. ;    BX    := buffer descriptor address
  680. ;
  681. ; Returns:
  682. ;    AX    := Max string size for buffer if all strings are deleted
  683. ;
  684. ; All registers (except AX)  are preserved.
  685. ;-
  686. strstk_size proc near
  687.     mov    ax,[bx].topmark        ;Address beyond last available
  688.                     ; for strings
  689.     sub    ax,[bx].low_end        ;bottom of buffer (sentinel header)
  690.     sub    ax,4            ;Need 2 for header trailer + 2 for
  691.                     ; sentinel
  692.     jnb    @strstk_size_99        ;
  693.     xor    ax,ax            ;No space !
  694. @strstk_size_99:
  695.     ret
  696. strstk_size endp
  697.  
  698.  
  699.  
  700.  
  701.  
  702. ;+
  703. ; FUNCTION : strstk_prefix
  704. ;
  705. ;    Compares the passed string to check if it is a prefix of the
  706. ;    current string in the stack.
  707. ;
  708. ; Parameters:
  709. ;    BX    := buffer descriptor address
  710. ;    AX    := address of string
  711. ;    CX    := length of string
  712. ;
  713. ; Returns:
  714. ;    ZF    = 1 if string is prefix
  715. ;          0 if not
  716. ;
  717. ; Register(s) destroyed:
  718. ;-
  719. strstk_prefix    proc near
  720.     @save    si,di
  721.     xchg    di,ax            ;DI->string
  722.     mov    si,[bx].cur        ;SI->trailer byte of stack element
  723.     xor    ah,ah            ;Clear high byte
  724.     mov    al,[si]            ;AX<-length of string
  725.     cmp    ax,cx            ;Passed string longer ?
  726.     jb    @strstk_prefix_90    ;No, return `not prefix'
  727.     sub    si,ax            ;SI->start of string
  728.     call    near ptr stre_cmp
  729. ;    ZF is set if match occurs, 0 otherwise
  730.     jmp    short @strstk_prefix_99
  731. @strstk_prefix_90:
  732.     inc    al            ;Set ZF=0 (no match)
  733. @strstk_prefix_99:
  734.     @restore
  735.     ret
  736. strstk_prefix endp
  737.  
  738.  
  739.  
  740.  
  741. ;+
  742. ; FUNCTION : strstk_compare
  743. ;
  744. ;    Compares the passed string against the current string in the stack.
  745. ;
  746. ; Parameters:
  747. ;    BX    := buffer descriptor address
  748. ;    AX    := address of string
  749. ;    CX    := length of string
  750. ;
  751. ; Returns:
  752. ;    ZF    = 1 if strings equal
  753. ;          0 if not equal.
  754. ;
  755. ; Register(s) destroyed:
  756. ;-
  757. strstk_compare    proc near
  758.     @save    si,di
  759.     xchg    di,ax            ;DI->string
  760.     mov    si,[bx].cur        ;SI->trailer byte of stack element
  761.     xor    ah,ah            ;Clear high byte
  762.     mov    al,[si]            ;AX<-length of string
  763.     cmp    ax,cx            ;Same length?
  764.     jne    @strstk_compare_99    ;No, return `not equal'
  765.     jcxz    @strstk_compare_99    ;Equal (zero length), ZF
  766. ;                     already set
  767.     sub    si,ax            ;SI->start of string
  768.     call    near ptr stre_cmp    ;Case-insensitive compare
  769. ;    ZF is set if match occurs, 0 otherwise
  770. @strstk_compare_99:
  771.     @restore
  772.     ret
  773. strstk_compare endp
  774.  
  775.  
  776.  
  777.     IF    WANT_MARKERS
  778. ;+
  779. ; FUNCTION : strstk_update_markers
  780. ;
  781. ;    This routine is called to update the markers that point to various
  782. ;    strings in the stack. This is necessary when strings are deleted
  783. ;    causing the string positions to change.
  784. ;
  785. ; Parameters:
  786. ;    BX    := address of stack descriptor
  787. ;    AX    := The address of the old (deleted) current string
  788. ;    CX    := Number of bytes by which the strings were displaced.
  789. ;
  790. ; Returns:
  791. ;    Nothing.
  792. ; Registers AX is destroyed.
  793. ;-
  794. strstk_update_marks proc near
  795.     @save    si,di
  796.     xchg    ax,si        ;Store old current address in SI
  797.     mov    di,[bx].topmark    ;DI will iterate through the marks
  798. @strstk_update_marks_20:
  799.     cmp    di,[bx].high_end
  800.     jnc    @strstk_update_marks_99 ;All done
  801.     mov    ax,[di]        ;Old address of marked string
  802.     cmp    si,ax        ;Compare with address of deleted string
  803.     jne    @strstk_update_marks_40    ;
  804.     mov    ax,[bx].cur    ;Mark pointed to deleted string so
  805.     jmp    short @strstk_update_marks_50    ;point it to new current
  806. @strstk_update_marks_40:
  807.     jg    @strstk_update_marks_50    ;If mark pointed BELOW, no change
  808.     sub    ax,cx        ;Else subtract the displacement
  809. @strstk_update_marks_50:
  810.     stosw            ;Store new value back, increment DI to point
  811.     jmp    short @strstk_update_marks_20 ; to next mark and loop
  812.  
  813. @strstk_update_marks_99:
  814.     @restore
  815.     ret
  816. strstk_update_marks endp
  817.  
  818.  
  819.  
  820. ;+
  821. ; FUNCTION : strstk_mark_cur
  822. ;
  823. ;    Changes the topmost mark to point to the current string. If no marks
  824. ;    exist, one is created.
  825. ;
  826. ; Parameters:
  827. ;    BX    := address of descriptor.
  828. ;
  829. ; Returns:
  830. ;    CF = 1 if error (no room), else 0.
  831. ; Register AX is destroyed.
  832. ;-
  833. strstk_mark_cur proc near
  834.     call    strstk_kill_mark
  835.     call    strstk_push_mark
  836.     ret
  837. strstk_mark_cur endp
  838.  
  839.  
  840. ;+
  841. ; FUNCTION : strstk_kill_mark
  842. ;
  843. ;    Deletes the topmost mark on the stack.
  844. ;
  845. ; Parameters:
  846. ;    BX    := address of buffer descriptor
  847. ;
  848. ; Returns:
  849. ;    CF = 1 if stack was empty, else 0.
  850. ; Register AX is destroyed.
  851. ;-
  852. strstk_kill_mark proc near
  853.     mov    ax,[bx].high_end    ;Highest location of buffer
  854.     cmp    ax,[bx].topmark    ;Marker stack empty ?
  855.     jb    @strstk_kill_mark_99 ;Yes
  856.     dec    [bx].topmark    ;Remove topmost mark by updating
  857.     dec    [bx].topmark    ; top of stack ptr
  858. @strstk_kill_mark_99:
  859.     ret
  860. strstk_kill_mark endp
  861.  
  862.  
  863. ;+
  864. ; FUNCTION : strstk_push_mark
  865. ;
  866. ;    Pushes a marker to the current string onto the marker stack.
  867. ;
  868. ; Parameters:
  869. ;    BX    := address of descriptor
  870. ;
  871. ; Returns:
  872. ;    CF = 1 if error (no room), else 0.
  873. ; Register AX is destroyed.
  874. ;-
  875. strstk_push_mark proc near
  876.     @save    di
  877.     mov    di,[bx].topmark    ;Point to topmost mark
  878.     dec    di
  879.     dec    di        ;Next location to push mark
  880.     cmp    di,[bx].top    ;Enough room ?
  881.     jle    @strstk_push_mark_90 ;Sorry, return error
  882.     mov    [bx].topmark,di    ;New stack top
  883.     mov    ax,[bx].cur    ;Address of current string
  884.     stosw            ;Store in top marker
  885.     ;Carry is clear
  886.     jmp    short @strstk_push_mark_99
  887. @strstk_push_mark_90:
  888.     stc            ;Set error
  889. @strstk_push_mark_99:
  890.     @restore
  891. strstk_push_mark endp
  892.  
  893.  
  894. ;+
  895. ; FUNCTION : strstk_pop_mark
  896. ;
  897. ;    Pops the topmost mark off the stack and set the current ptr
  898. ;    to point to its associated string.
  899. ;
  900. ; Parameters:
  901. ;    BX    := address of buffer descriptor
  902. ;
  903. ; Returns:
  904. ;    CF = 1 if no marks, else 0.
  905. ; Register AX is destroyed.
  906. ;-
  907. strstk_pop_mark proc near
  908.     call    strstk_goto_mark
  909.     call    strstk_kill_mark
  910.     ret
  911. strstk_pop_mark endp
  912.  
  913.  
  914.  
  915. ;+
  916. ; FUNCTION : strstk_goto_mark
  917. ;
  918. ;    Sets the current string pointer to the string associated with the
  919. ;    topmost marker on the marker stack.
  920. ;
  921. ; Parameters:
  922. ;    BX    := address of buffer descriptor
  923. ;
  924. ; Returns:
  925. ;    CF = 1 if no markers, else 0.
  926. ; Register AX is destroyed.
  927. ;-
  928. strstk_goto_mark proc near
  929.     mov    ax,[bx].high_end
  930.     cmp    ax,[bx].topmark
  931.     jb    @strstk_goto_mark_99    ;No marks
  932.     mov    ax,[bx].topmark
  933.     mov    [bx].cur,ax        ;Update current string ptr
  934. @strstk_goto_mark_99:
  935.     ret
  936. strstk_goto_mark endp
  937.  
  938.  
  939. ;+
  940. ; FUNCTION : strstk_purge_marks
  941. ;
  942. ;    Deletes all marks that point to the sentinel string. The marker
  943. ;    stack is compacted.
  944. ;
  945. ; Parameters:
  946. ;    BX    := address of buffer descriptor
  947. ;
  948. ; Returns:
  949. ;    Nothing.
  950. ; Register(s)  AX,CX are destroyed.
  951. ;-
  952. strstk_purge_marks proc near
  953.     @save    si,di
  954.     mov    cx,[bx].low_end
  955.     inc    cx        ;CX = sentinel string trailer
  956.     mov    di,[bx].high_end
  957.     inc    di
  958.     mov    si,di
  959. @strstk_purge_marks_10:        ;SI = last marker location examined
  960.                 ;DI = last marker location stored
  961.     cmp    si,[bx].topmark    ;Checked all markers ?
  962.     je    @strstk_purge_marks_99 ;Yes, exit
  963.     dec    si
  964.     dec    si        ;Next location to check
  965.     mov    ax,[si]        ;Marker value
  966.     cmp    ax,cx        ;Points to sentinel ?
  967.     jg    @strstk_purge_marks_10 ;No, go check next
  968.     dec    di
  969.     dec    di        ;DI = next location to store
  970.     mov    [di],ax        ;Store marker
  971.     jmp    short @strstk_purge_marks_10
  972. @strstk_purge_marks_99:
  973.     mov    [bx].topmark,di    ;Store new top of marker stack
  974.     @restore
  975.     ret
  976. strstk_purge_marks endp
  977.  
  978.     ENDIF                ;WANT_MARKERS
  979.  
  980. CSEG    ENDS
  981.  
  982.     END