\ >STRING-EXECUTE >BUFFER-EXECUTE
\
\ Copyright (C) 2011 Free Software Foundation, Inc.

\ This file is part of Gforth.

\ Gforth is free software; you can redistribute it and/or
\ modify it under the terms of the GNU General Public License
\ as published by the Free Software Foundation, either version 3
\ of the License, or (at your option) any later version.

\ This program is distributed in the hope that it will be useful,
\ but WITHOUT ANY WARRANTY; without even the implied warranty of
\ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
\ GNU General Public License for more details.

\ You should have received a copy of the GNU General Public License
\ along with this program. If not, see http://www.gnu.org/licenses/.

32 constant >string-initial-buflen

2variable >string-buffer \ buffer
variable  >string-len    \ actual string length

: >string-type { c-addr u -- }
    >string-len @ { str-len }
    str-len u + { new-str-len }
    >string-buffer 2@
    begin { buf-addr buf-size }
	new-str-len buf-size > while
	    buf-size 2* buf-addr over resize throw swap
	    2dup >string-buffer 2!
    repeat 
    c-addr buf-addr str-len + u move
    new-str-len >string-len ! ;

: >string-emit { c^ c -- }
    c 1 >string-type ;

: >string-execute ( ... xt -- ... addr u )
    \G execute xt while the standard output (TYPE, EMIT, and everything
    \G that uses them) is redirected to a string.  The resulting string
    \G is addr u, which is in ALLOCATEd memory; it is the
    \G responsibility of the caller of >STRING-EXECUTE to FREE this
    \G string.
    >string-buffer 2@ >string-len @
    action-of type action-of emit    { d: oldbuf oldlen oldtype oldemit }
    try
	>string-initial-buflen dup allocate throw swap >string-buffer 2!
	0 >string-len !
	['] >string-type is type
	['] >string-emit is emit
	execute
	>string-buffer 2@ drop >string-len @ tuck resize throw swap
	0 \ throw ball
    restore
	oldbuf >string-buffer 2!
	oldlen >string-len !
	oldtype is type
	oldemit is emit
    endtry
    throw ;

\ altenative interface (for systems without memory allocation wordset):

\ >buffer-execute ( ... c-addr u1 xt -- ... u2 ) execute xt while the
\ standard output (TYPE, EMIT, and everything that uses them) is
\ redirected to the buffer c-addr u.  u2 is the number of characters
\ that were output with TYPE or EMIT.  If u2<=u1, then the string
\ c-addr u2 contains the output, otherwise c-addr u1 contains the
\ first u1 characters of the output, and the other characters are not
\ stored.

\ You can emulate >STRING-EXECUTE with >BUFFER-EXECUTE like this:
\ Instead of

\ ... ['] foo >string-execute ( c-addr u ) ...

\ where FOO has the stack effect ( x1 x2 -- x3 ), write

\ ... 2dup 2>r (or whatever is necessary to save FOO's input operands)
\ pad 0 ['] foo >buffer-execute >r drop ( throw away result of FOO )
\ 2r> ( restore FOO's input operands )
\ r@ allocate throw r> 2dup 2>r ['] foo >buffer-execute drop 2r>


0 [if]
\ tests
5 ' . >string-execute dump
5 5 ' .r >string-execute dump

: test 0 swap 0 do i . i + loop ;

cr  20 ' test >string-execute .s cr 2dup type drop free throw .
cr 120 ' test >string-execute .s cr 2dup type drop free throw .
cr
[endif]
