mac-rom/Toolbox/ScriptMgr/ScriptMgrTruncRepl.a
Elliot Nunn 4325cdcc78 Bring in CubeE sources
Resource forks are included only for .rsrc files. These are DeRezzed into their data fork. 'ckid' resources, from the Projector VCS, are not included.

The Tools directory, containing mostly junk, is also excluded.
2017-12-26 09:52:23 +08:00

775 lines
27 KiB
Plaintext

;
; File: ScriptMgrTruncRepl.a
;
; Contains: New routines for text truncation and replacement.
;
; Written by: PKE Peter Edberg
;
; Copyright: © 1989-1992 by Apple Computer, Inc. All rights reserved.
;
; Change History (most recent first):
;
; <SM3> 11/6/92 SWC Changed PackMacs.a->Packages.a.
; <3> 4/30/92 FM Get rid of conditional smgrSys7Extensions
; <2> 5/18/90 PKE Adapt to new changes in 'itl4' resource: get truncation marker
; for TruncText from unToken table instead of special truncMarker
; table.
; <1> 4/10/90 PKE New today - extracted from ScriptMgrExtTail.a and
; conditionalized so it can be used with ROM builds too.
;
; Relevant recent comments from ScriptMgrExtTail.a below
; <x7> 3/5/90 PKE In TruncString/TruncText, changed NTruncX selectors to TruncX.
; <x6> 3/2/90 PKE Use new name nItl4Rec for 7.0 extended version of Itl4Rec.
; <1.7> 11/8/89 PKE Modify TruncString and TruncText to support both new and old
; calling conventions (with and without truncWhere parameter).
; Implement handling of center truncation (in TruncText).
; <1.4> 9/17/89 PKE Better error reporting in TruncText, ReplaceText.
; <1.3> 9/15/89 PKE Add and install framework code for TruncString, TruncText,
; ReplaceText. Stuff correct version number in SMgrRecord.
;
LOAD 'StandardEqu.d'
include 'ScriptPriv.a'
include 'Packages.a'
blanks on
string asis
; ----------------------------------------------------------------------- <1.3>
; TruncString and TruncText routines.
;
; TruncCode = Integer;
;
; FUNCTION TruncString(width: Integer; VAR theString: Str255;
; truncWhere: TruncCode): Integer;
;
; FUNCTION TruncText(width: Integer; textPtr: Ptr; VAR length: Integer;
; truncWhere: TruncCode): Integer;
;
; These routines will ensure that a text string fits into the pixel length
; specified by width, truncating the string if necessary in a way that depends
; on the script associated with the font of the current grafPort. This includes
; proper handling of double-byte characters and insertion of a truncation
; indicator (such as an ellipsis in English). The truncation indicator is
; obtained from a string in the current script's 'itl4' resource. In the first
; form (TruncString), the string is supplied as a Str255. In the second form
; of the call (TruncText), the string is defined by a pointer and byte length;
; the length will be updated if necessary. In either form, the length will
; not increase. The truncWhere argument is a constant specifying where
; where truncation should occur. At present, only two values are defined:
; smTruncEnd (0) and smTruncMiddle ($4000, although any non-zero value will
; currently work).
;
; Result codes:
; 0 No truncation was necessary.
; 1 Truncation was performed.
; (negative=>error:)
; -1 General error: truncation is necessary, but the truncation
; indicator is too big - longer than the length parameter or
; wider than the width parameter. In this case, the returned
; length will be 0.
; resNotFound Can't get itl4 resource, or it's not in current format
;
; Note that TruncString is basically just glue that converts a Str255 to
; pointer and length, calls TruncText, and then converts back again.
;
; Also note that we are still supporting an older form of each call, without
; the truncWhere parameter. We can tell from the selector which form we were
; called with. For the old form, we truncate at the end.
; -----------------------------------------------------------------------------
proc
import StdUnlink
export TruncString,TruncText ; <1>
; --------------------------------------
; TruncString
; stack setup at entry for old-style call:
;
; (high address)
; result ds.w 1 ; result code
; width ds.w 1 ; width for truncated text.
; theString ds.l 1 ; pointer to Str255 to be truncated
; selector ds.l 1 ; selector
; return ds.l 1 ; return address.
; (low address)
;
; stack frame for new-style call:
ntsRecord record {a6link},decr ; <1.7>
result ds.w 1 ; result code
ntsArgs equ *-8 ; size of arguments.
width ds.w 1 ; width for truncated text.
theString ds.l 1 ; pointer to Str255 to be truncated
truncWhere ds.w 1 ; truncation location code <1.7>
selector ds.l 1 ; selector
return ds.l 1 ; return address.
a6link ds.l 1 ; old a6 register.
lenWord ds.w 1 ; word-size length to modify
ntsLocals equ * ; size of local variables.
endr
TruncString ; <1>
; If this is an old-style TruncString call, fake a new-style call with the
; truncWhere parameter set up to truncate at the end, which is what the <1.7>
; old call did.
move.l 4(sp),d0 ; get selector
cmp.l #smSelTruncString,d0 ; is it the new form?
beq.s nTruncString ; if yes, no stack tweaking
move.l (sp)+,d1 ; pop return addr
addq.l #4,sp ; kill selector on stack; it's in d0
move.w #smTruncEnd,-(sp) ; add TruncWhere parameter
move.l d0,-(sp) ; push selector again
move.l d1,-(sp) ; push return address again
; now we are set up like a call to the new TruncString
nTruncString ; <1.7>
with ntsRecord ; <1.7>
link a6,#ntsLocals ; link the stack. <1.7>
move.l theString(a6),a0 ; get theString
clr.w d0 ; for wordizing
move.b (a0)+,d0 ; get length, make text pointer
move.w d0,lenWord(a6) ; word-size length to modify
subq #2,sp ; allocate space for TruncText result
move.w width(a6),-(sp) ; push width
move.l a0,-(sp) ; push textPtr
pea lenWord(a6) ; push addr of length
move.w truncWhere(a6),-(sp) ; push truncWhere <1.7>
move.l #smSelTruncText,-(sp) ; now it's a new TruncText call <1.7>
bsr.s nTruncText ; <1.7>
move.w (sp)+,result(a6) ; result code
move.w lenWord(a6),d0 ; new length
move.l theString(a6),a0 ; get addr of Str255
move.b d0,(a0) ; update length
move.w #ntsArgs,d0 ; set up for StdUnlink <1.7>
bra StdUnlink
endwith
; --------------------------------------
; TruncText
; stack setup at entry for old-style call:
;
; (high address)
; result ds.w 1 ; result code
; width ds.w 1 ; width for truncated text.
; textPtr ds.l 1 ; pointer to text to be truncated
; lengthPtr ds.l 1 ; pointer to length of text (VAR)
; selector ds.l 1 ; selector
; return ds.l 1 ; return address.
; (low address)
;
; stack frame for new-style call:
nttRecord record {a6link},decr ; <1.7>
result ds.w 1 ; result code
nttArgs equ *-8 ; size of arguments.
width ds.w 1 ; width for truncated text.
textPtr ds.l 1 ; pointer to text to be truncated
lengthPtr ds.l 1 ; pointer to length of text (VAR)
truncWhere ds.w 1 ; truncation location code <1.7>
selector ds.l 1 ; selector
return ds.l 1 ; return address.
a6link ds.l 1 ; old a6 register.
headWidth ds.w 1 ; current width for head string <1.7>
tailWidth ds.w 1 ; current width for tail string <1.7>
nttLocals equ * ; size of local variables.
endr
; register usage:
; a4 length ptr
; a3 SMgrRecord ptr/trunc marker ptr
; a2 textPtr
; d7 length of trunc marker (bytes)
; d6 current length of truncated string without marker (bytes)
; d5 desired width less trunc marker width
; d4 size of space for trunc marker on stack <1.7>
; d3 length of text (bytes)
;
TruncText ; <1>
; If this is an old-style TruncText call, fake a new-style call with the
; truncWhere parameter set up to truncate at the end, which is what the <1.7>
; old call did.
move.l 4(sp),d0 ; get selector
cmp.l #smSelTruncText,d0 ; is it the new form?
beq.s nTruncText ; if yes, no stack tweaking
move.l (sp)+,d1 ; pop return addr
addq.l #4,sp ; kill selector on stack; it's in d0
move.w #smTruncEnd,-(sp) ; add TruncWhere parameter
move.l d0,-(sp) ; push selector again
move.l d1,-(sp) ; push return address again
; now we are set up like a call to the new TruncString
nTruncText ; <1.7>
with nttRecord ; <1.7>
link a6,#nttLocals ; link the stack.
movem.l a2-a4/d3-d7,-(sp) ; save registers
clr.w result(a6) ; initially assume no truncation, no err
; Get text, see if it already fits in width
move.l textPtr(a6),a2 ; save textPtr in a2
move.l lengthPtr(a6),a4 ; pointer to length
move.w (a4),d3 ; save length of text
subq #2,sp ; allocate space for TextWidth return
move.l a2,-(sp) ; push pointer to text
clr.w -(sp) ; first byte is 0
move.w d3,-(sp) ; push length of text
_TextWidth
move.w (sp)+,d0 ; get text width
cmp.w width(a6),d0 ;
ble ttExit ; if already ok, we're done
; Get truncMarker from itl4
with SMgrRecord,nItl4Rec ; <x6>
move.w #resNotFound,result(a6) ; now assume bad itl4
GetSMgrCore a3 ; get pointer to SMgrRecord
move.b smgrIntlForce(a3),-(sp) ; save IntlForce
clr.b smgrIntlForce(a3) ; now clear it for GetIntl
clr.l -(sp) ; space for returned handle
move.w #4,-(sp) ; select itl4
_IUGetIntl ; may trash a0,a1,d0-d2
move.l (sp)+,a0 ; store itl4 handle
move.b (sp)+,smgrIntlForce(a3) ; restore IntlForce
move.l a0,d0 ; nil handle?
beq ttExit ; bail if so
move.l (a0),a0 ; dereference
move.l unTokenOffset(a0),d0 ; check offset <2>
beq ttExit ; if 0, bail <2>
add.l d0,a0 ; point to unToken table <2>
cmp.w #tokenEllipsis,2(a0) ; does it include ellipsis? <2>
blt ttExit ; if not, bail <2>
add.w 4+(2*tokenEllipsis)(a0),a0 ; point to ellipsis string <2>
endwith ;SMgrRecord,nItl4Rec ;
; check truncation marker length
move.w #-1,result(a6) ; now assume general error
clr.w (a4) ; and set length to 0 <1.7>
moveq #0,d7 ; for word/longizing
move.b (a0)+,d7 ; get byte length of trunc marker
move.w d3,d6 ; copy string length
sub.w d7,d6 ; subtract trunc marker length
blt ttExit ; bail if trunc marker longer than string
; copy trunc marker to stack (instead of to end of string as before) <1.7>
move.w d7,d4 ; copy marker length
addq.w #1,d4 ;
andi.w #$FFFE,d4 ; length is rounded up to even number
sub.w d4,sp ; allocate space on stack for marker
move.l sp,a3 ; save trunc marker pointer
move.l a3,a1 ; copy dest ptr (a0 is src ptr)
move.w d7,d0 ; copy trunc marker length
beq.s @noCopy ; skip copy if 0 <1.7>
subq.w #1,d0 ; for dbra
@1 move.b (a0)+,(a1)+ ; copy
dbra d0,@1 ;
@noCopy ; <1.7>
; subtract width of trunc marker from desired width; moved up here <1.7>
move.w width(a6),d5 ; get desired width
subq #2,sp ; allocate space for return value
move.l a3,-(sp) ; push pointer to trunc marker
clr.w -(sp) ; first byte is 0
move.w d7,-(sp) ; push length of trunc marker
_TextWidth
sub.w (sp)+,d5 ; subtract width of trunc marker
bmi ttCleanUp ; bail if trunc marker too wide -
; should we trunc without marker instead?
; select type of truncation
tst.w truncWhere(a6) ; where do we truncate? <1.7>
bne.s ttDoMiddle ; if not end, assume middle <1.7>
; --------------------------------------
; Truncate at end: while width of remaining text too big, subtract a char
; (NOT necessarily a byte!!).
;; bra.s @doTextWidth ; (no longer want this) <1.7>
addq.w #1,d6 ; compensate for initial decrement <1.7>
@loop
subq.w #1,d6 ; decrement current string count
bsr DoCharByteHead ; adjust d6 to char boundary <1.7>
bsr DoTextWidthHead ; put head width in d0.w <1.7>
cmp.w d0,d5 ; compare desired width to current
blt.s @loop
; move trunc marker to end of truncated string
lea 0(a2,d6.w),a1 ; dest ptr for trunc marker
move.w d7,d0 ; copy trunc marker length
beq.s @noMarker ; skip if 0 <1.7>
subq.w #1,d0 ; for dbra
@2 move.b (a3)+,(a1)+ ; copy
dbra d0,@2 ;
@noMarker ; <1.7>
; set length & result (eventually, do fancier stuff here like check to see
; if string + marker is too long and continue reducing length if so)
add.w d7,d6 ; total truncated length with marker
move.w d6,(a4) ; set new length
bra.s ttTruncOk ; all done, go clean up and exit <1.7>
; --------------------------------------
; Truncate in middle. Basically, we take the bytes remaining in the string
; after subtracting those needed for the truncMarker, divide them by two,
; and start with head and tail strings of this roughly this length (adjusted
; for character boundaries, etc.). While the sum of the widths of the head
; and tail strings is too big, we remove characters from one or the other of
; the strings (the head string if it is longer, otherwise the tail string).
ttDoMiddle
lsr.w #1,d6 ; half of remaining length for head
bsr DoCharByteHead ; adjust d6 to char boundary
move.l a2,a3 ; now make pointer to back half:
add.w d6,a3 ; advance past first halfÉ
add.w d7,a3 ; Éand trunc marker.
sub.w d6,d3 ; get length: subtract first halfÉ
sub.w d7,d3 ; and trunc marker.
bsr DoCharByteTail ; adjust a3/d3 for char boundary
; Registers at this point:
; a4 length ptr
; a3 pointer to back string
; a2 pointer to front string
; d7 length of trunc marker (bytes)
; d6 current length of front string
; d5 desired width less trunc marker width
; d4 size of space for trunc marker on stack
; d3 current length of back string
bsr DoTextWidthHead ; put head width in d0.w
move.w d0,headWidth(a6) ; save head width
bsr DoTextWidthTail ; put tail width in d0.w
move.w d0,tailWidth(a6) ; save tail width
bra.s @loopTest
@loop
cmp.w d6,d3 ; who has more bytes: head or tail?
bge.s @shortenTail ; shorten tail if longer or same
@shortenHead
subq.w #1,d6
bsr DoCharByteHead ; adjust d6 to char boundary
bsr DoTextWidthHead ; put head width in d0.w
move.w d0,headWidth(a6) ; save head width
bra.s @loopTest
@shortenTail
subq.w #1,d3
addq #1,a3
bsr DoCharByteTail ; adjust a3/d3 for char boundary
bsr DoTextWidthTail ; put tail width in d0.w
move.w d0,tailWidth(a6) ; save tail width
@loopTest
move.w headWidth(a6),d0
add.w tailWidth(a6),d0
cmp.w d0,d5 ; compare desired width to current
blt.s @loop
; assemble string (note that sp points to trunc marker)
move.l sp,a0 ; reset trunc marker pointer
lea 0(a2,d6.w),a1 ; dest ptr for trunc marker
move.w d7,d0 ; copy trunc marker length
beq.s @noMarker ;
subq.w #1,d0 ; for dbra
@1 move.b (a0)+,(a1)+ ; copy
dbra d0,@1 ;
@noMarker
move.w d3,d0 ; copy tail string length
beq.s @noTail ;
subq.w #1,d0 ; for dbra
@2 move.b (a3)+,(a1)+ ; copy
dbra d0,@2 ;
@noTail
; set length & result (eventually, do fancier stuff here like check to see
; if string + marker is too long and continue reducing length if so)
add.w d7,d6 ; add trunc marker length to head length
add.w d3,d6 ; now add in tail length
move.w d6,(a4) ; set new length
; --------------------------------------
; clean up and return
ttTruncOk ; <1.7>
move.w #1,result(a6) ; we truncated it
ttCleanUp ; <1.7>
add.w d4,sp ; kill trunc marker space on stack <1.7>
ttExit
movem.l (sp)+,a2-a4/d3-d7 ; restore registers
move.w #nttArgs,d0 ; set up for StdUnlink <1.7>
bra StdUnlink
endwith
; --------------------------------------
; little subroutines for code savings:
; DoCharByteHead: a2 is pointer to text, d6.w is length; will decrement
; d6.w by one character (one or two bytes).
DoCharByteHead
subq #2,sp ; allocate space for CharByte return
move.l a2,-(sp) ; push text ptr
move.w d6,-(sp) ; push offset
_CharByte
tst.w (sp)+ ; are we at 2nd byte of 2-byte char?
ble.s @1 ; skip decrement if not
subq.w #1,d6 ; decrement length to char boundary
@1 rts
; DoCharByteTail: a3 is pointer to text, d3.w is length; will increment
; a3, and decrement d3.w, by one character (one or two bytes).
DoCharByteTail
subq #2,sp ; allocate space for CharByte return
move.l a3,-(sp) ; push text ptr
move.w d3,-(sp) ; push offset
_CharByte
tst.w (sp)+ ; are we at 2nd byte of 2-byte char?
ble.s @1 ; skip increment if not
addq #1,a3 ; advance to beginning of next charÉ
addq.w #1,d3 ; and update length.
@1 rts
; DoTextWidthHead: a2 is pointer to text, d6.w is length; will return
; TextWidth value in d0.w.
DoTextWidthHead
subq #2,sp ; allocate space for return value
move.l a2,-(sp) ; push pointer to text
clr.w -(sp) ; first byte is 0
move.w d6,-(sp) ; push remaining length of text
_TextWidth
move.w (sp)+,d0 ; save width
rts
; DoTextWidthTail: a3 is pointer to text, d3.w is length; will return
; TextWidth value in d0.w.
DoTextWidthTail
subq #2,sp ; allocate space for return value
move.l a3,-(sp) ; push pointer to text
clr.w -(sp) ; first byte is 0
move.w d3,-(sp) ; push remaining length of text
_TextWidth
move.w (sp)+,d0 ; save width
rts
endproc
; ----------------------------------------------------------------------- <1.3>
; ReplaceText routine.
;
; FUNCTION ReplaceText(baseText: Handle; substitutionText: Handle;
; key: Str15): Integer;
;
; The key parameter contains a string to be used as the substitution marker.
; The routine will search through the text indicated by baseText for instances
; of this string, and replace each instance with the text indicated by
; substitutionText. The contents of substitutionText will not affect the
; substitution process: the substitution text may contain the key string,
; which will be inserted verbatim into the base text.
;
; Result codes:
; 0 or positive Number of substitutions performed.
; (negative=>error:)
; nilHandleErr GetHandleSize fails on baseText or substitutionText
; memWZErr GetHandleSize fails on baseText or substitutionText
; memFullErr SetHandleSize fails on baseText
; -----------------------------------------------------------------------------
proc
import StdUnlink
export ReplaceText ; <1>
rtRecord record {a6link},decr
result ds.w 1 ; integer result
rtArgs equ *-8 ; size of arguments.
baseText ds.l 1 ; Handle for base text.
subsText ds.l 1 ; Handle for substitution text
key ds.l 1 ; pointer to Str15, substitution key
selector ds.l 1 ; selector
return ds.l 1 ; return address.
a6link ds.l 1 ; old a6 register.
rtLocals equ * ; size of local variables.
endr
; register usage:
; a4 baseText pointer
; a3 subsText pointer
; a2 key pointer
; d7 baseText length
; d6 subsText length - key length
; d5 key length
; d4 substitution count * (subsText length - key length)
; d3 substitution count
;
ReplaceText ; <1>
with SMgrRecord,rtRecord
link a6,#rtLocals ; link the stack.
movem.l a2-a4/d3-d7,-(sp) ; save registers
; get pointers and sizes
clr.w d0 ; assume no substitution
move.l key(a6),a2 ; pointer to Pascal string
moveq #0,d5 ; for longizing
move.b (a2)+,d5 ; key length, now a2 pts to key text
beq rtBail ; bail if no key
move.l baseText(a6),a4 ; baseText handle
move.l a4,a0 ; copy
_GetHandleSize
tst.l d0 ; need tst.l, not dispatcher's tst.w
bmi rtBail
move.l d0,d7 ; save baseText length
move.l (a4),a4 ; baseText ptr
move.l subsText(a6),a3 ; subsText handle
move.l a3,a0 ; copy
_GetHandleSize
tst.l d0 ; need tst.l, not dispatcher's tst.w
bmi rtBail
move.l d0,d6 ; subsText length
move.l (a3),a3 ; subsText ptr
sub.w d5,d6 ; subsText length - key length
; set up for search loop. Additional register usage:
; a1 working key pointer
; a0 working baseText pointer
; d2 last basetext offset at which to try match
; d1 working baseText offset
; d0 working key length count for dbne
;
moveq #0,d4 ; init accumulator
moveq #0,d3 ; init substitution count
moveq #0,d1 ; working offset in baseText
move.w d7,d2 ;
sub.w d5,d2 ; last start offset for key search
subq.w #1,d5 ; temporary fix for dbne
bra.s @loopProcess ; skip first increment
; outer loop, which increments position in baseText
@loopAdvance
addq.w #1,d1 ; next baseText offset
; make sure we're at beginning of 2-byte char (need to optimize this!!)
movem.w d1-d2,-(sp) ; save important regs
subq #2,sp ; allocate space for CharByte return
move.l a4,-(sp) ; push text ptr
move.w d1,-(sp) ; push offset
_CharByte
tst.w (sp)+ ; are we at 2nd byte of 2-byte char?
movem.w (sp)+,d1-d2 ; restore important regs
ble.s @loopProcess ; skip increment if not
addq.w #1,d1 ; now we're at beginning of char
; test for exit, set up for inner loop
@loopProcess
cmp.w d2,d1 ; past last start offset?
bgt.s @doneSearch ; if so, we're done
lea 0(a4,d1.w),a0 ; working pointer in baseText
move.l a2,a1 ; key pointer
move.w d5,d0 ; key length fixed for dbne
; inner loop does comparison with key
@loopMatch
cmpm.b (a0)+,(a1)+ ; do baseText bytes match key?
dbne d0,@loopMatch ; break out if no match or end of key
; after inner loop - check for match
tst.w d0 ; did we go negative?
bpl.s @loopAdvance ; if not, there was no match
; We found a match: save location on stack, increment count, and advance
; baseText offset after key. Note that key length in d5 has been decremented
; by one, so after adding it here we still need to do an addq.w #1 to get to
; the right position.
move.w d1,-(sp) ; add offset to list
addq.w #1,d3 ; increment match count
add.w d6,d4 ; accumulate length difference
add.w d5,d1 ; skip matched key
addq.w #1,d1 ; finish skipping matched key
bra.s @loopProcess ; continue with next baseText position
; When we finish the search, we have pushed a word on the stack for each match.
; A match count is in d3; we need this later for fixing the stack. Also note
; that we no longer need a2 as a key pointer.
@doneSearch
addq.w #1,d5 ; restore real key length
; Now we go through and do substitutions in place. The order in which we do
; this depends on the difference in length between the key and the substitution
; text: if the substitution text is longer, we do it from the end, otherwise
; we do it from the beginning.
tst.w d3 ; any substitutions?
beq rtSetResult ; if no, quit.
tst.w d6 ; check subsText length - key length
bgt.s rtFromEnd ; if >0, substitute from end.
; Substitute from beginning
; register usage after setup
; a4 baseText source pointer
; a3 subsText base pointer
; a2 temp stack pointer for traversing marker location list
; a1 baseText dest pointer
; a0 marker pointer / subsText source pointer
; d7.l orig baseText length
; d6 subsText length
; d5 key length
; d4 substitution count * (subsText length - key length)
; d3 substitution count
; d2.l baseText base pointer
; d1.l last baseText addr
; d0 subsText count for dbra
;
move.l sp,a2 ; copy stack ptr
add.w d3,a2 ; only halfway thereÉ
add.w d3,a2 ; point of beginning of marker list
move.l a4,d1 ; copy baseText pointer
add.l d7,d1 ; addr past end of baseText
add.w d5,d6 ; make subsText length again
move.l a4,d2 ; save baseText address
add.w -(a2),a4 ; starting baseText source pointer
move.l a4,a1 ; starting baseText dest pointer
bra.s @copySubs
@copyBase
cmp.l a4,d1 ; end of text?
bls.s @copyDone ; quit if so
cmp.l a4,a0 ; next marker location?
beq.s @copySubs ; if so, go copy substitution text
move.b (a4)+,(a1)+ ; copy the byte
bra.s @copyBase
@copySubs
move.l a3,a0 ; source pointer
move.w d6,d0 ; length for dbra
bra.s @copySubsEnter ; handle case of subsText len=0
@copySubsLoop
move.b (a0)+,(a1)+ ; copy the substitution byte
@copySubsEnter
dbra d0,@copySubsLoop ; until done
add.w d5,a4 ; move baseText source pointer after key
cmp.l sp,a2 ; are we after last sub?
bls.s @copyBase ; if so, don't get an invalid marker
move.l d2,a0 ; copy baseText addr
add.w -(a2),a0 ; make next marker pointer
bra.s @copyBase
@copyDone
; Now reduce handle size if necessary.
tst.w d4 ; is it 0?
beq.s rtSetResult ; if so, no resizing necessary
move.l baseText(a6),a0 ; reload baseText handle
move.w d4,d0 ; length reduction (d4 is < 0)
ext.l d0
add.l d7,d0 ; add original length, get new length
_SetHandleSize
bmi.s rtFixStack
bra.s rtSetResult
; Substitute from end. First we need to allocate more space.
rtFromEnd
move.l baseText(a6),a4 ; reload baseText handle
move.l a4,a0 ; copy
move.w d4,d0 ; extra length needed
ext.l d0
add.l d7,d0 ; add original length, get new length
_SetHandleSize
bmi.s rtFixStack
move.l (a4),a4 ; baseText ptr
move.l subsText(a6),a3 ; reload subsText handle
move.l (a3),a3 ; subsText ptr
; Now do substitutionÉ
; register usage after setup
; a4 baseText source pointer
; a3 subsText end pointer
; a2 temp stack pointer for traversing marker location list
; a1 baseText dest pointer
; a0 marker pointer / subsText source pointer
; d7.l -(baseText length)
; d6 subsText length
; d5 key length
; d4 substitution count * (subsText length - key length)
; d3 substitution count
; d2.l baseText base pointer + key length
; d1 remaining subs
; d0 subsText count for dbra
;
move.l sp,a2 ; copy stack ptr
move.l a4,a0 ; copy baseText addr
add.w d7,a4 ; point after end of baseText (source ptr)
add.w d5,d6 ; make subsText length again
move.l a4,a1 ; copy pointer after end of baseText
add.w d4,a1 ; move past end of expanded space
add.w d6,a3 ; point after end of subsText
add.w d5,a0 ; adjust so we point to end of marker
move.l a0,d2 ; save adjusted base pointer
add.w (a2)+,a0 ; make first marker pointer
move.w d3,d1 ; remaining subs
@copyBase
; cmp.l a4,d2 ; end of text?
; bhi.s @copyDone ; quit if so
cmp.l a4,a0 ; next marker location?
beq.s @copySubs ; if so, go copy substitution text
move.b -(a4),-(a1) ; copy the byte
bra.s @copyBase
@copySubs
move.l a3,a0 ; source pointer
move.w d6,d0 ; length for dbra
bra.s @copySubsEnter ; handle case of subsText len=0
@copySubsLoop
move.b -(a0),-(a1) ; copy the substitution byte
@copySubsEnter
dbra d0,@copySubsLoop ; until done
subq.w #1,d1 ; used up a sub
ble.s @copyDone ; quit if last sub
move.l d2,a0 ; copy baseText addr
add.w (a2)+,a0 ; make next marker pointer
sub.w d5,a4 ; move baseText source pointer after key
bra.s @copyBase
@copyDone
; clean up and return
rtSetResult
move.w d3,d0 ; set up result
rtFixStack
add.w d3,d3 ; turn count into word offset
add.w d3,sp ; kill temp storage
rtBail
move.w d0,result(a6)
movem.l (sp)+,a2-a4/d3-d7 ; restore registers
move.w #rtArgs,d0 ; set up for StdUnlink
bra StdUnlink
endwith
endproc
end