inweb-bootstrap/docs/foundation-module/2-str.html
2020-04-10 21:29:28 +01:00

1297 lines
173 KiB
HTML

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Streams</title>
<meta name="viewport" content="width=device-width initial-scale=1">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="Content-Language" content="en-gb">
<link href="../inweb.css" rel="stylesheet" rev="stylesheet" type="text/css">
</head>
<body>
<nav role="navigation">
<h1><a href="../webs.html">Sources</a></h1>
<ul>
<li><a href="../inweb/index.html">inweb</a></li>
</ul>
<h2>Foundation</h2>
<ul>
<li><a href="../foundation-module/index.html">foundation-module</a></li>
<li><a href="../foundation-test/index.html">foundation-test</a></li>
</ul>
</nav>
<main role="main">
<!--Weave of 'Streams' generated by 7-->
<ul class="crumbs"><li><a href="../webs.html">Source</a></li><li><a href="index.html">foundation</a></li><li><a href="index.html#2">Chapter 2: Memory, Streams and Collections</a></li><li><b>Streams</b></li></ul><p class="purpose">Support for writing structured textual output, perhaps to the screen, to a file, or to a flexible-sized wide string.</p>
<ul class="toc"><li><a href="#SP1">&#167;1. About streams</a></li><li><a href="#SP19">&#167;19. Initialising the stream structure</a></li><li><a href="#SP21">&#167;21. Logging</a></li><li><a href="#SP22">&#167;22. Standard I/O wrappers</a></li><li><a href="#SP24">&#167;24. Creating file streams</a></li><li><a href="#SP26">&#167;26. Creating memory streams</a></li><li><a href="#SP28">&#167;28. Converting from C strings</a></li><li><a href="#SP29">&#167;29. Converting to C strings</a></li><li><a href="#SP32">&#167;32. Locale versions</a></li><li><a href="#SP33">&#167;33. Flush and close</a></li><li><a href="#SP35">&#167;35. Writing</a></li><li><a href="#SP39">&#167;39. Memory-stream-only functions</a></li><li><a href="#SP43">&#167;43. Writer</a></li></ul><hr class="tocbar">
<p class="inwebparagraph"><a id="SP1"></a><b>&#167;1. About streams. </b>The Inform tools produce textual output in many formats (HTML, EPS, plain
text, Inform 6 code, XML, and so on), writing to a variety of files, and
often need to juggle and rearrange partially written segments. These
texts tend to be structured with running indentation, and may eventually
need to be written to a disc file with either ISO Latin-1 or UTF-8
encodings, perhaps escaped for XML.
</p>
<p class="inwebparagraph">"Streams" are an abstraction to make it easy to handle all of this. The
writer to a stream never needs to know where the text will come out, or how
it should be indented or encoded.
</p>
<p class="inwebparagraph">Moreover, streams unify text files and strings, and can hold arbitrary
Unicode text. This text is encoded internally as a sequence of 32-bit Unicode
code points, in a form we might call semi-composed, in that all possible
compositions of ISO Latin-1 characters are made: e.g., E plus acute accent
is composed to a single code point as E-acute.
</p>
<p class="inwebparagraph">We give just one character value a non-Unicode meaning:
</p>
<pre class="definitions">
<span class="definitionkeyword">define</span> <span class="constant">NEWLINE_IN_STRING</span><span class="plain"> ((</span><span class="reserved">char</span><span class="plain">) </span><span class="constant">0x7f</span><span class="plain">) </span><span class="comment"> Within quoted text, all newlines are converted to this</span>
</pre>
<p class="inwebparagraph"><a id="SP2"></a><b>&#167;2. </b>The <code class="display"><span class="extract">text_stream</span></code> type began as a generalisation of the standard C library's
<code class="display"><span class="extract">FILE</span></code>, and it is used in mostly similar ways. The user &mdash; the whole
program outside of this section &mdash; deals only with <code class="display"><span class="extract">text_stream *</span></code> pointers to
represent streams in use.
</p>
<p class="inwebparagraph">All stream handling is defined via macros. While many operations could be
handled by ordinary functions, others cannot. <code class="display"><span class="extract">text_stream</span></code> cannot have exactly
the semantics of <code class="display"><span class="extract">FILE</span></code> since we cannot rely on the host operating system
to allocate and deallocate the structures behind <code class="display"><span class="extract">text_stream *</span></code> pointers; and
we cannot use our own memory system, either, since we need stream handling
to work both before the memory allocator starts and after it has finished.
Our macros allow us to hide all this. Besides that, a macro approach makes it
easier to retrofit new implementations as necessary. (The present
implementation is the second stab at it.)
</p>
<p class="inwebparagraph"><a id="SP3"></a><b>&#167;3. </b>We'll define a few variadic macros here, because there are awkward issues
with code ordering if we leave them until later. They are written in the
old-fashioned way, for compatibility with old copies of GCC, and avoid the
need for comma deletion around empty tokens, as that is a point of
incompatibility between implementations of the C preprocessor <code class="display"><span class="extract">cpp</span></code>. All the
same, if you're porting this code, you may need to rewrite the macro with
<code class="display"><span class="extract">...</span></code> in place of <code class="display"><span class="extract">args...</span></code> in the header, and then <code class="display"><span class="extract">__VA_ARGS__</span></code> in place
of <code class="display"><span class="extract">args</span></code> in the definition: that being the modern way, apparently.
</p>
<p class="inwebparagraph"><code class="display"><span class="extract">WRITE</span></code> is essentially <code class="display"><span class="extract">sprintf</span></code> and <code class="display"><span class="extract">fprintf</span></code> combined, since it prints
formatted text to the current stream, which could be either a string or a
file. <code class="display"><span class="extract">PRINT</span></code> does the same but to <code class="display"><span class="extract">STDOUT</span></code>, and is thus essentially <code class="display"><span class="extract">printf</span></code>.
</p>
<pre class="display">
<span class="plain">#</span><span class="identifier">define</span><span class="plain"> </span><span class="identifier">WRITE</span><span class="plain">(</span><span class="identifier">args</span><span class="plain">...) </span><span class="functiontext">Writers::printf</span><span class="plain">(</span><span class="identifier">OUT</span><span class="plain">, </span><span class="identifier">args</span><span class="plain">)</span>
<span class="plain">#</span><span class="identifier">define</span><span class="plain"> </span><span class="identifier">PRINT</span><span class="plain">(</span><span class="identifier">args</span><span class="plain">...) </span><span class="functiontext">Writers::printf</span><span class="plain">(</span><span class="constant">STDOUT</span><span class="plain">, </span><span class="identifier">args</span><span class="plain">)</span>
<span class="plain">#</span><span class="identifier">define</span><span class="plain"> </span><span class="identifier">WRITE_TO</span><span class="plain">(</span><span class="identifier">stream</span><span class="plain">, </span><span class="identifier">args</span><span class="plain">...) </span><span class="functiontext">Writers::printf</span><span class="plain">(</span><span class="identifier">stream</span><span class="plain">, </span><span class="identifier">args</span><span class="plain">)</span>
<span class="plain">#</span><span class="identifier">define</span><span class="plain"> </span><span class="identifier">LOG</span><span class="plain">(</span><span class="identifier">args</span><span class="plain">...) </span><span class="functiontext">Writers::printf</span><span class="plain">(</span><span class="identifier">DL</span><span class="plain">, </span><span class="identifier">args</span><span class="plain">)</span>
<span class="plain">#</span><span class="identifier">define</span><span class="plain"> </span><span class="identifier">LOGIF</span><span class="plain">(</span><span class="identifier">aspect</span><span class="plain">, </span><span class="identifier">args</span><span class="plain">...) { \</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="functiontext">Log::aspect_switched_on</span><span class="plain">(</span><span class="identifier">aspect</span><span class="plain">##</span><span class="identifier">_DA</span><span class="plain">)) </span><span class="functiontext">Writers::printf</span><span class="plain">(</span><span class="identifier">DL</span><span class="plain">, </span><span class="identifier">args</span><span class="plain">); \</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP4"></a><b>&#167;4. </b>The main purpose of many functions is to write textual material to some
file. Such functions almost always have a special argument in their
prototypes: <code class="display"><span class="extract">OUTPUT_STREAM</span></code>. This tells them where to pipe their output, which
is always to a "current stream" called <code class="display"><span class="extract">OUT</span></code>. What this leads to, and who will
see that it's properly opened and closed, are not their concern.
</p>
<pre class="definitions">
<span class="definitionkeyword">define</span> <span class="constant">OUTPUT_STREAM</span><span class="plain"> </span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">OUT</span><span class="plain"> </span><span class="comment"> used only as a function prototype argument</span>
</pre>
<p class="inwebparagraph"><a id="SP5"></a><b>&#167;5. </b>Three output streams are always open. One is <code class="display"><span class="extract">NULL</span></code>, that is, its value
as a <code class="display"><span class="extract">text_stream *</span></code> pointer is <code class="display"><span class="extract">NULL</span></code>, the generic C null pointer. This represents
an oubliette: it is entirely valid to use it, but output sent to <code class="display"><span class="extract">NULL</span></code> will
never be seen again.
</p>
<p class="inwebparagraph">The others are <code class="display"><span class="extract">STDOUT</span></code> and <code class="display"><span class="extract">STDERR</span></code>. As the names suggest these are wrappers
for <code class="display"><span class="extract">stdout</span></code> and <code class="display"><span class="extract">stderr</span></code>, the standard console output and error messages
"files" provided by the C library.
</p>
<p class="inwebparagraph">We should always use <code class="display"><span class="extract">PRINT(...)</span></code> instead of <code class="display"><span class="extract">printf(...)</span></code> for console output,
so that there are no uses of <code class="display"><span class="extract">printf</span></code> anywhere in the program.
</p>
<pre class="definitions">
<span class="definitionkeyword">define</span> <span class="constant">STDOUT</span><span class="plain"> </span><span class="functiontext">Streams::get_stdout</span><span class="plain">()</span>
<span class="definitionkeyword">define</span> <span class="constant">STDERR</span><span class="plain"> </span><span class="functiontext">Streams::get_stderr</span><span class="plain">()</span>
</pre>
<p class="inwebparagraph"><a id="SP6"></a><b>&#167;6. </b><code class="display"><span class="extract">PUT</span></code> and <code class="display"><span class="extract">PUT_TO</span></code> similarly print single characters, which are
specified as unsigned integer values. In practice, <code class="display"><span class="extract">WRITE_TO</span></code> and
<code class="display"><span class="extract">PUT_TO</span></code> are seldom needed because there is almost always only one
stream of interest at a time &mdash; <code class="display"><span class="extract">OUT</span></code>, the current stream.
</p>
<pre class="definitions">
<span class="definitionkeyword">define</span> <span class="identifier">PUT</span><span class="plain">(</span><span class="identifier">c</span><span class="plain">) </span><span class="functiontext">Streams::putc</span><span class="plain">(</span><span class="identifier">c</span><span class="plain">, </span><span class="identifier">OUT</span><span class="plain">)</span>
<span class="definitionkeyword">define</span> <span class="identifier">PUT_TO</span><span class="plain">(</span><span class="identifier">stream</span><span class="plain">, </span><span class="identifier">c</span><span class="plain">) </span><span class="functiontext">Streams::putc</span><span class="plain">(</span><span class="identifier">c</span><span class="plain">, </span><span class="identifier">stream</span><span class="plain">)</span>
</pre>
<p class="inwebparagraph"><a id="SP7"></a><b>&#167;7. </b>Each stream has a current indentation level, initially 0. Lines of text
will be indented by one tab stop for each level; it's an error for the level
to become negative.
</p>
<pre class="definitions">
<span class="definitionkeyword">define</span> <span class="constant">INDENT</span><span class="plain"> </span><span class="functiontext">Streams::indent</span><span class="plain">(</span><span class="identifier">OUT</span><span class="plain">);</span>
<span class="definitionkeyword">define</span> <span class="identifier">STREAM_INDENT</span><span class="plain">(</span><span class="identifier">x</span><span class="plain">) </span><span class="functiontext">Streams::indent</span><span class="plain">(</span><span class="identifier">x</span><span class="plain">);</span>
<span class="definitionkeyword">define</span> <span class="constant">OUTDENT</span><span class="plain"> </span><span class="functiontext">Streams::outdent</span><span class="plain">(</span><span class="identifier">OUT</span><span class="plain">);</span>
<span class="definitionkeyword">define</span> <span class="identifier">STREAM_OUTDENT</span><span class="plain">(</span><span class="identifier">x</span><span class="plain">) </span><span class="functiontext">Streams::outdent</span><span class="plain">(</span><span class="identifier">x</span><span class="plain">);</span>
<span class="definitionkeyword">define</span> <span class="identifier">SET_INDENT</span><span class="plain">(</span><span class="identifier">N</span><span class="plain">) </span><span class="functiontext">Streams::set_indentation</span><span class="plain">(</span><span class="identifier">OUT</span><span class="plain">, </span><span class="identifier">N</span><span class="plain">);</span>
</pre>
<p class="inwebparagraph"><a id="SP8"></a><b>&#167;8. </b>Other streams only exist when explicitly created, or "opened". A function
is only allowed to open a new stream if it can be proved that this stream will
always subsequently be "closed". (Except for the possibility of the tool
halting with an internal error, and therefore an <code class="display"><span class="extract">exit(1)</span></code>, while the stream
is still open.) A stream can be opened and closed only once, and outside that
time its state is undefined: it must not be used at all.
</p>
<p class="inwebparagraph">The simplest way is to make a temporary stream, which can be used as a sort
of clipboard. For instance, suppose we have to compile X before Y, but have to
ensure Y comes before X in the eventual output. We create a temporary stream,
compile X into it, then compile Y to <code class="display"><span class="extract">OUT</span></code>, then copy the temporary stream
into <code class="display"><span class="extract">OUT</span></code> and dispose of it.
</p>
<p class="inwebparagraph">Temporary streams are always created in memory, held in C's local stack frame
rather than allocated and freed via <code class="display"><span class="extract">malloc</span></code> and <code class="display"><span class="extract">free</span></code>. It must always be
possible to prove that execution passes from <code class="display"><span class="extract">TEMPORARY_TEXT</span></code> to
<code class="display"><span class="extract">DISCARD_TEXT</span></code>, unless the program makes a fatal exit in between. The stream,
let's call it <code class="display"><span class="extract">TEMP</span></code>, exists only between those macros. We can legitimately
create a temporary stream many times in one function (for instance inside a
loop body) because each time <code class="display"><span class="extract">TEMP</span></code> is created as a new stream, overwriting the
old one. <code class="display"><span class="extract">TEMP</span></code> is a different stream each time it is created, so it does
not violate the rule that every stream is opened and closed once only.
</p>
<pre class="definitions">
<span class="definitionkeyword">define</span> <span class="identifier">TEMPORARY_TEXT</span><span class="plain">(</span><span class="identifier">T</span><span class="plain">)</span>
<span class="identifier">wchar_t</span><span class="plain"> </span><span class="identifier">T</span><span class="plain">##</span><span class="identifier">_dest</span><span class="plain">[2048];</span>
<span class="reserved">text_stream</span><span class="plain"> </span><span class="identifier">T</span><span class="plain">##</span><span class="identifier">_stream_structure</span><span class="plain"> = </span><span class="functiontext">Streams::new_buffer</span><span class="plain">(2048, </span><span class="identifier">T</span><span class="plain">##</span><span class="identifier">_dest</span><span class="plain">);</span>
<span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">T</span><span class="plain"> = &amp;</span><span class="identifier">T</span><span class="plain">##</span><span class="identifier">_stream_structure</span><span class="plain">;</span>
<span class="definitionkeyword">define</span> <span class="identifier">DISCARD_TEXT</span><span class="plain">(</span><span class="identifier">T</span><span class="plain">)</span>
<span class="identifier">STREAM_CLOSE</span><span class="plain">(</span><span class="identifier">T</span><span class="plain">);</span>
</pre>
<p class="inwebparagraph"><a id="SP9"></a><b>&#167;9. </b>Otherwise we can create new globally existing streams, provided we take on
the responsibility for seeing that they are properly closed. There are two
choices: a stream in memory, allocated via <code class="display"><span class="extract">malloc</span></code> and freed by <code class="display"><span class="extract">free</span></code> when
the stream is closed; or a file written to disc, opened via <code class="display"><span class="extract">fopen</span></code> and
later closed by <code class="display"><span class="extract">fclose</span></code>. Files are always written in text mode, that is,
<code class="display"><span class="extract">"w"</span></code> not <code class="display"><span class="extract">"wb"</span></code>, for those platforms where this makes a difference.
</p>
<p class="inwebparagraph">We use streams to handle all of our text file output, so there should be no
calls to <code class="display"><span class="extract">fprintf</span></code> anywhere in the program except for binary files.
</p>
<pre class="definitions">
<span class="definitionkeyword">define</span> <span class="identifier">STREAM_OPEN_TO_FILE</span><span class="plain">(</span><span class="identifier">new</span><span class="plain">, </span><span class="identifier">fn</span><span class="plain">, </span><span class="identifier">enc</span><span class="plain">) </span><span class="functiontext">Streams::open_to_file</span><span class="plain">(</span><span class="identifier">new</span><span class="plain">, </span><span class="identifier">fn</span><span class="plain">, </span><span class="identifier">enc</span><span class="plain">)</span>
<span class="definitionkeyword">define</span> <span class="identifier">STREAM_OPEN_TO_FILE_APPEND</span><span class="plain">(</span><span class="identifier">new</span><span class="plain">, </span><span class="identifier">fn</span><span class="plain">, </span><span class="identifier">enc</span><span class="plain">) </span><span class="functiontext">Streams::open_to_file_append</span><span class="plain">(</span><span class="identifier">new</span><span class="plain">, </span><span class="identifier">fn</span><span class="plain">, </span><span class="identifier">enc</span><span class="plain">)</span>
<span class="definitionkeyword">define</span> <span class="identifier">STREAM_OPEN_IN_MEMORY</span><span class="plain">(</span><span class="identifier">new</span><span class="plain">) </span><span class="functiontext">Streams::open_to_memory</span><span class="plain">(</span><span class="identifier">new</span><span class="plain">, </span><span class="constant">20480</span><span class="plain">)</span>
<span class="definitionkeyword">define</span> <span class="identifier">STREAM_CLOSE</span><span class="plain">(</span><span class="identifier">stream</span><span class="plain">) </span><span class="functiontext">Streams::close</span><span class="plain">(</span><span class="identifier">stream</span><span class="plain">)</span>
</pre>
<p class="inwebparagraph"><a id="SP10"></a><b>&#167;10. </b>The following operation is equivalent to <code class="display"><span class="extract">fflush</span></code> and makes it more likely
(I put it no higher) that the text written to a stream has all actually been
copied onto the disc, rather than sitting in some operating system buffer.
This helps ensure that any debugging log is up to the minute, in case of
a crash, but its absence wouldn't hurt our normal function. Flushing
a memory stream is legal but does nothing.
</p>
<pre class="definitions">
<span class="definitionkeyword">define</span> <span class="identifier">STREAM_FLUSH</span><span class="plain">(</span><span class="identifier">stream</span><span class="plain">) </span><span class="functiontext">Streams::flush</span><span class="plain">(</span><span class="identifier">stream</span><span class="plain">)</span>
</pre>
<p class="inwebparagraph"><a id="SP11"></a><b>&#167;11. </b>A piece of information we can read for any stream is the number of characters
written to it: its "extent". In fact, UTF-8 multi-byte encoding schemes,
together with differing platform interpretations of C's <code class="display"><span class="extract">'\n'</span></code>, mean that this
extent is not necessarily either the final file size in bytes or the final
number of human-readable characters. We will only actually use it to detect
whether text has, or has not, been written to a stream between two points in
time, by seeing whether or not it has increased.
</p>
<pre class="definitions">
<span class="definitionkeyword">define</span> <span class="identifier">STREAM_EXTENT</span><span class="plain">(</span><span class="identifier">x</span><span class="plain">) </span><span class="functiontext">Streams::get_position</span><span class="plain">(</span><span class="identifier">x</span><span class="plain">)</span>
</pre>
<p class="inwebparagraph"><a id="SP12"></a><b>&#167;12. </b>The remaining operations are available only for streams in memory (well, and
for <code class="display"><span class="extract">NULL</span></code>, but of course they do nothing when applied to that). While they
could be provided for file streams, this would be so inefficient that we will
pretend it is impossible. Any function which might need to use one
of these operations should open with the following sentinel macro:
</p>
<pre class="definitions">
<span class="definitionkeyword">define</span> <span class="identifier">STREAM_MUST_BE_IN_MEMORY</span><span class="plain">(</span><span class="identifier">x</span><span class="plain">)</span>
<span class="reserved">if</span><span class="plain"> ((</span><span class="identifier">x</span><span class="plain"> != </span><span class="identifier">NULL</span><span class="plain">) &amp;&amp; (</span><span class="identifier">x</span><span class="plain">-&gt;</span><span class="element">write_to_memory</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">))</span>
<span class="identifier">internal_error</span><span class="plain">(</span><span class="string">"text_stream not in memory"</span><span class="plain">);</span>
</pre>
<p class="inwebparagraph"><a id="SP13"></a><b>&#167;13. </b>First, we can erase one or more recently written characters:
</p>
<pre class="definitions">
<span class="definitionkeyword">define</span> <span class="identifier">STREAM_BACKSPACE</span><span class="plain">(</span><span class="identifier">x</span><span class="plain">) </span><span class="functiontext">Streams::set_position</span><span class="plain">(</span><span class="identifier">x</span><span class="plain">, </span><span class="functiontext">Streams::get_position</span><span class="plain">(</span><span class="identifier">x</span><span class="plain">) - </span><span class="constant">1</span><span class="plain">)</span>
<span class="definitionkeyword">define</span> <span class="identifier">STREAM_ERASE_BACK_TO</span><span class="plain">(</span><span class="identifier">start_position</span><span class="plain">) </span><span class="functiontext">Streams::set_position</span><span class="plain">(</span><span class="identifier">OUT</span><span class="plain">, </span><span class="identifier">start_position</span><span class="plain">)</span>
</pre>
<p class="inwebparagraph"><a id="SP14"></a><b>&#167;14. </b>Second, we can look at the text written. The minimal form is to look at
just the most recent character, but we can also copy one entire memory
stream into another stream (where the target can be either a memory or file
stream).
</p>
<pre class="definitions">
<span class="definitionkeyword">define</span> <span class="identifier">STREAM_MOST_RECENT_CHAR</span><span class="plain">(</span><span class="identifier">x</span><span class="plain">) </span><span class="functiontext">Streams::latest</span><span class="plain">(</span><span class="identifier">x</span><span class="plain">)</span>
<span class="definitionkeyword">define</span> <span class="identifier">STREAM_COPY</span><span class="plain">(</span><span class="identifier">to</span><span class="plain">, </span><span class="identifier">from</span><span class="plain">) </span><span class="functiontext">Streams::copy</span><span class="plain">(</span><span class="identifier">to</span><span class="plain">, </span><span class="identifier">from</span><span class="plain">)</span>
</pre>
<p class="inwebparagraph"><a id="SP15"></a><b>&#167;15. </b>So much for the definition; now the implementation. Here is the <code class="display"><span class="extract">text_stream</span></code>
structure. Open memory streams are represented by structures where
<code class="display"><span class="extract">write_to_memory</span></code> is valid, open file streams by those where <code class="display"><span class="extract">write_to_file</span></code>
is valid. That counts every open stream except <code class="display"><span class="extract">NULL</span></code>, which of course
doesn't point to a <code class="display"><span class="extract">text_stream</span></code> structure at all.
</p>
<p class="inwebparagraph">Any stream can have <code class="display"><span class="extract">USES_XML_ESCAPES_STRF</span></code> set or cleared. When this is set, the
XML (and HTML) escapes of <code class="display"><span class="extract">&amp;amp;</span></code> for ampersand, and <code class="display"><span class="extract">&amp;lt;</span></code> and <code class="display"><span class="extract">&amp;gt;</span></code> for
angle brackets, will be used automatically on writing. By default this flag
is clear, that is, no conversion is made.
</p>
<pre class="definitions">
<span class="definitionkeyword">define</span> <span class="constant">MALLOCED_STRF</span><span class="plain"> </span><span class="constant">0x00000001</span><span class="plain"> </span><span class="comment"> was the <code class="display"><span class="extract">write_to_memory</span></code> pointer claimed by <code class="display"><span class="extract">malloc</span></code>?</span>
<span class="definitionkeyword">define</span> <span class="constant">USES_XML_ESCAPES_STRF</span><span class="plain"> </span><span class="constant">0x00000002</span><span class="plain"> </span><span class="comment"> see above</span>
<span class="definitionkeyword">define</span> <span class="constant">USES_LOG_ESCAPES_STRF</span><span class="plain"> </span><span class="constant">0x00000004</span><span class="plain"> </span><span class="comment"> <code class="display"><span class="extract">WRITE</span></code> to this stream supports <code class="display"><span class="extract">$</span></code> escapes</span>
<span class="definitionkeyword">define</span> <span class="constant">INDENT_PENDING_STRF</span><span class="plain"> </span><span class="constant">0x00000008</span><span class="plain"> </span><span class="comment"> we have just ended a line, so further text should indent</span>
<span class="definitionkeyword">define</span> <span class="constant">FILE_ENCODING_ISO_STRF</span><span class="plain"> </span><span class="constant">0x00000010</span><span class="plain"> </span><span class="comment"> relevant only for file streams</span>
<span class="definitionkeyword">define</span> <span class="constant">FILE_ENCODING_UTF8_STRF</span><span class="plain"> </span><span class="constant">0x00000020</span><span class="plain"> </span><span class="comment"> relevant only for file streams</span>
<span class="definitionkeyword">define</span> <span class="constant">FOR_RE_STRF</span><span class="plain"> </span><span class="constant">0x00000100</span><span class="plain"> </span><span class="comment"> for debugging only</span>
<span class="definitionkeyword">define</span> <span class="constant">FOR_TT_STRF</span><span class="plain"> </span><span class="constant">0x00000200</span><span class="plain"> </span><span class="comment"> for debugging only</span>
<span class="definitionkeyword">define</span> <span class="constant">FOR_CO_STRF</span><span class="plain"> </span><span class="constant">0x00000400</span><span class="plain"> </span><span class="comment"> for debugging only</span>
<span class="definitionkeyword">define</span> <span class="constant">FOR_FI_STRF</span><span class="plain"> </span><span class="constant">0x00000800</span><span class="plain"> </span><span class="comment"> for debugging only</span>
<span class="definitionkeyword">define</span> <span class="constant">FOR_OM_STRF</span><span class="plain"> </span><span class="constant">0x00001000</span><span class="plain"> </span><span class="comment"> for debugging only</span>
<span class="definitionkeyword">define</span> <span class="constant">USES_I6_ESCAPES_STRF</span><span class="plain"> </span><span class="constant">0x00002000</span><span class="plain"> </span><span class="comment"> as if an Inform 6 string</span>
<span class="definitionkeyword">define</span> <span class="constant">READ_ONLY_STRF</span><span class="plain"> </span><span class="constant">0x00008000</span>
<span class="definitionkeyword">define</span> <span class="constant">INDENTATION_BASE_STRF</span><span class="plain"> </span><span class="constant">0x00010000</span><span class="plain"> </span><span class="comment"> number of tab stops in from the left margin</span>
<span class="definitionkeyword">define</span> <span class="constant">INDENTATION_MASK_STRF</span><span class="plain"> </span><span class="constant">0x0FFF0000</span><span class="plain"> </span><span class="comment"> (held in these bits)</span>
</pre>
<pre class="display">
<span class="reserved">typedef</span><span class="plain"> </span><span class="reserved">struct</span><span class="plain"> </span><span class="reserved">text_stream</span><span class="plain"> {</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">stream_flags</span><span class="plain">; </span><span class="comment"> bitmap of the <code class="display"><span class="extract">*_STRF</span></code> values above</span>
<span class="reserved">FILE</span><span class="plain"> *</span><span class="identifier">write_to_file</span><span class="plain">; </span><span class="comment"> for an open stream, exactly one of these is <code class="display"><span class="extract">NULL</span></code></span>
<span class="reserved">struct</span><span class="plain"> </span><span class="reserved">HTML_file_state</span><span class="plain"> *</span><span class="identifier">as_HTML</span><span class="plain">; </span><span class="comment"> relevant only to the <code class="display"><span class="extract">HTML::</span></code> section</span>
<span class="identifier">wchar_t</span><span class="plain"> *</span><span class="identifier">write_to_memory</span><span class="plain">;</span>
<span class="reserved">struct</span><span class="plain"> </span><span class="reserved">filename</span><span class="plain"> *</span><span class="identifier">file_written</span><span class="plain">; </span><span class="comment"> ditto</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">chars_written</span><span class="plain">; </span><span class="comment"> number of characters sent, counting <code class="display"><span class="extract">\n</span></code> as 1</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">chars_capacity</span><span class="plain">; </span><span class="comment"> maximum number the stream can accept without claiming more resources</span>
<span class="reserved">struct</span><span class="plain"> </span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">stream_continues</span><span class="plain">; </span><span class="comment"> if one memory stream is extended by another</span>
<span class="plain">} </span><span class="reserved">text_stream</span><span class="plain">;</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The structure text_stream is accessed in 2/wal, 4/pm and here.</p>
<p class="inwebparagraph"><a id="SP16"></a><b>&#167;16. </b>A theological question: what is the text encoding for the null stream?
</p>
<pre class="definitions">
<span class="definitionkeyword">define</span> <span class="identifier">STREAM_USES_UTF8</span><span class="plain">(</span><span class="identifier">x</span><span class="plain">) ((</span><span class="identifier">x</span><span class="plain">)?((</span><span class="identifier">x</span><span class="plain">-&gt;</span><span class="element">stream_flags</span><span class="plain">) &amp; </span><span class="constant">FILE_ENCODING_UTF8_STRF</span><span class="plain">):</span><span class="constant">FALSE</span><span class="plain">)</span>
</pre>
<p class="inwebparagraph"><a id="SP17"></a><b>&#167;17. </b>When text is stored at <code class="display"><span class="extract">write_to_memory</span></code>, it is kept as a zero-terminated C
wide string, with one word per Unicode code point. It turns out to be
efficient to preserve a small margin of clear space at the end of the space,
so out of the <code class="display"><span class="extract">chars_capacity</span></code> space, the following amount will be kept clear:
</p>
<pre class="definitions">
<span class="definitionkeyword">define</span> <span class="constant">SPACE_AT_END_OF_STREAM</span><span class="plain"> </span><span class="constant">6</span>
</pre>
<p class="inwebparagraph"><a id="SP18"></a><b>&#167;18. </b>A statistic we keep, since it costs little:
</p>
<pre class="display">
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">total_file_writes</span><span class="plain"> = </span><span class="constant">0</span><span class="plain">; </span><span class="comment"> number of text files opened for writing during the run</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP19"></a><b>&#167;19. Initialising the stream structure. </b>Note that the following fills in sensible defaults for every field, but the
result is not a valid open stream; it's a blank form ready to be filled in.
</p>
<p class="inwebparagraph">By default the upper limit on file size is 2 GB. It's very hard to see this
ever being approached for any tool associated with the Inform project, where
text files have a size which is proportionate to the result of human writing.
The only output file with a sorceror's-apprentice-like ability to grow and
grow is the debugging file, and if it should reach 2 GB then it deserves to be
truncated and we will shed no tears.
</p>
<pre class="display">
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Streams::initialise</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">stream</span><span class="plain">, </span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">from</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) </span><span class="identifier">internal_error</span><span class="plain">(</span><span class="string">"tried to initialise NULL stream"</span><span class="plain">);</span>
<span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_flags</span><span class="plain"> = </span><span class="identifier">from</span><span class="plain">;</span>
<span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">write_to_file</span><span class="plain"> = </span><span class="identifier">NULL</span><span class="plain">;</span>
<span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">write_to_memory</span><span class="plain"> = </span><span class="identifier">NULL</span><span class="plain">;</span>
<span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">chars_written</span><span class="plain"> = </span><span class="constant">0</span><span class="plain">;</span>
<span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">chars_capacity</span><span class="plain"> = </span><span class="constant">2147483647</span><span class="plain">;</span>
<span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_continues</span><span class="plain"> = </span><span class="identifier">NULL</span><span class="plain">;</span>
<span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">as_HTML</span><span class="plain"> = </span><span class="identifier">NULL</span><span class="plain">;</span>
<span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">file_written</span><span class="plain"> = </span><span class="identifier">NULL</span><span class="plain">;</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Streams::initialise is used in <a href="#SP22">&#167;22</a>, <a href="#SP23">&#167;23</a>, <a href="#SP24">&#167;24</a>, <a href="#SP25">&#167;25</a>, <a href="#SP26">&#167;26</a>, <a href="#SP27">&#167;27</a>, <a href="#SP35_3">&#167;35.3</a>.</p>
<p class="inwebparagraph"><a id="SP20"></a><b>&#167;20. </b>Any stream can have the following flag set or cleared. When this is set, the
XML (and HTML) escapes of <code class="display"><span class="extract">&amp;amp;</span></code> for ampersand, and <code class="display"><span class="extract">&amp;lt;</span></code> and <code class="display"><span class="extract">&amp;gt;</span></code> for
angle brackets, will be used automatically on writing. By default this flag
is clear, that is, no conversion is made.
</p>
<pre class="display">
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Streams::enable_XML_escapes</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">stream</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain">) </span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_flags</span><span class="plain"> |= </span><span class="constant">USES_XML_ESCAPES_STRF</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Streams::disable_XML_escapes</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">stream</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> ((</span><span class="identifier">stream</span><span class="plain">) &amp;&amp; (</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_flags</span><span class="plain"> &amp; </span><span class="constant">USES_XML_ESCAPES_STRF</span><span class="plain">))</span>
<span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_flags</span><span class="plain"> -= </span><span class="constant">USES_XML_ESCAPES_STRF</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">I6_escapes_globally_enabled</span><span class="plain"> = </span><span class="constant">FALSE</span><span class="plain">;</span>
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Streams::enable_I6_escapes</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">stream</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain">) </span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_flags</span><span class="plain"> |= </span><span class="constant">USES_I6_ESCAPES_STRF</span><span class="plain">;</span>
<span class="identifier">I6_escapes_globally_enabled</span><span class="plain"> = </span><span class="constant">TRUE</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Streams::disable_I6_escapes</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">stream</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> ((</span><span class="identifier">stream</span><span class="plain">) &amp;&amp; (</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_flags</span><span class="plain"> &amp; </span><span class="constant">USES_I6_ESCAPES_STRF</span><span class="plain">))</span>
<span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_flags</span><span class="plain"> -= </span><span class="constant">USES_I6_ESCAPES_STRF</span><span class="plain">;</span>
<span class="identifier">I6_escapes_globally_enabled</span><span class="plain"> = </span><span class="constant">FALSE</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">int</span><span class="plain"> </span><span class="functiontext">Streams::I6_escapes_enabled</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">stream</span><span class="plain">) {</span>
<span class="reserved">return</span><span class="plain"> </span><span class="identifier">I6_escapes_globally_enabled</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Streams::enable_debugging</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">stream</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain">) </span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_flags</span><span class="plain"> |= </span><span class="constant">USES_LOG_ESCAPES_STRF</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Streams::disable_debugging</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">stream</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> ((</span><span class="identifier">stream</span><span class="plain">) &amp;&amp; (</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_flags</span><span class="plain"> &amp; </span><span class="constant">USES_LOG_ESCAPES_STRF</span><span class="plain">))</span>
<span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_flags</span><span class="plain"> -= </span><span class="constant">USES_LOG_ESCAPES_STRF</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Streams::mark_as_read_only</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">stream</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain">) </span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_flags</span><span class="plain"> |= </span><span class="constant">READ_ONLY_STRF</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Streams::declare_as_HTML</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">stream</span><span class="plain">, </span><span class="reserved">HTML_file_state</span><span class="plain"> *</span><span class="identifier">hs</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain">) </span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">as_HTML</span><span class="plain"> = </span><span class="identifier">hs</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">HTML_file_state</span><span class="plain"> *</span><span class="functiontext">Streams::get_HTML_file_state</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">stream</span><span class="plain">) {</span>
<span class="reserved">return</span><span class="plain"> </span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">as_HTML</span><span class="plain">;</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Streams::enable_XML_escapes appears nowhere else.</p>
<p class="endnote">The function Streams::disable_XML_escapes appears nowhere else.</p>
<p class="endnote">The function Streams::enable_I6_escapes appears nowhere else.</p>
<p class="endnote">The function Streams::disable_I6_escapes appears nowhere else.</p>
<p class="endnote">The function Streams::I6_escapes_enabled appears nowhere else.</p>
<p class="endnote">The function Streams::enable_debugging is used in 2/dl (<a href="2-dl.html#SP5">&#167;5</a>).</p>
<p class="endnote">The function Streams::disable_debugging appears nowhere else.</p>
<p class="endnote">The function Streams::mark_as_read_only is used in 4/sm (<a href="4-sm.html#SP26_1">&#167;26.1</a>).</p>
<p class="endnote">The function Streams::declare_as_HTML is used in 5/htm (<a href="5-htm.html#SP2">&#167;2</a>).</p>
<p class="endnote">The function Streams::get_HTML_file_state is used in 5/htm (<a href="5-htm.html#SP3">&#167;3</a>, <a href="5-htm.html#SP4">&#167;4</a>, <a href="5-htm.html#SP5">&#167;5</a>, <a href="5-htm.html#SP7">&#167;7</a>, <a href="5-htm.html#SP8">&#167;8</a>, <a href="5-htm.html#SP10">&#167;10</a>).</p>
<p class="inwebparagraph"><a id="SP21"></a><b>&#167;21. Logging. </b></p>
<pre class="display">
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Streams::log</span><span class="plain">(</span><span class="constant">OUTPUT_STREAM</span><span class="plain">, </span><span class="reserved">void</span><span class="plain"> *</span><span class="identifier">vS</span><span class="plain">) {</span>
<span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">stream</span><span class="plain"> = (</span><span class="reserved">text_stream</span><span class="plain"> *) </span><span class="identifier">vS</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) {</span>
<span class="identifier">WRITE</span><span class="plain">(</span><span class="string">"NULL"</span><span class="plain">);</span>
<span class="plain">} </span><span class="reserved">else</span><span class="plain"> </span><span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">write_to_file</span><span class="plain">) {</span>
<span class="identifier">WRITE</span><span class="plain">(</span><span class="string">"F'%f'(%d)"</span><span class="plain">, </span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">file_written</span><span class="plain">, </span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">chars_written</span><span class="plain">);</span>
<span class="plain">} </span><span class="reserved">else</span><span class="plain"> {</span>
<span class="identifier">WRITE</span><span class="plain">(</span><span class="string">"S%x("</span><span class="plain">, (</span><span class="reserved">long</span><span class="plain"> </span><span class="reserved">int</span><span class="plain">) </span><span class="identifier">stream</span><span class="plain">);</span>
<span class="reserved">while</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain">) {</span>
<span class="identifier">WRITE</span><span class="plain">(</span><span class="string">"%d/%d"</span><span class="plain">, </span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">chars_written</span><span class="plain">, </span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">chars_capacity</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_continues</span><span class="plain">) </span><span class="identifier">WRITE</span><span class="plain">(</span><span class="string">"+"</span><span class="plain">);</span>
<span class="identifier">stream</span><span class="plain"> = </span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_continues</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="identifier">WRITE</span><span class="plain">(</span><span class="string">")"</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Streams::log is used in 1/fm (<a href="1-fm.html#SP8_3">&#167;8.3</a>).</p>
<p class="inwebparagraph"><a id="SP22"></a><b>&#167;22. Standard I/O wrappers. </b>The first call to <code class="display"><span class="extract">Streams::get_stdout()</span></code> creates a suitable wrapper for <code class="display"><span class="extract">stdout</span></code>
and returns a <code class="display"><span class="extract">text_stream *</span></code> pointer to it; subsequent calls just return this wrapper.
</p>
<pre class="display">
<span class="reserved">text_stream</span><span class="plain"> </span><span class="identifier">STDOUT_struct</span><span class="plain">; </span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">stdout_wrapper_initialised</span><span class="plain"> = </span><span class="constant">FALSE</span><span class="plain">;</span>
<span class="reserved">text_stream</span><span class="plain"> *</span><span class="functiontext">Streams::get_stdout</span><span class="plain">(</span><span class="reserved">void</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stdout_wrapper_initialised</span><span class="plain"> == </span><span class="constant">FALSE</span><span class="plain">) {</span>
<span class="functiontext">Streams::initialise</span><span class="plain">(&amp;</span><span class="identifier">STDOUT_struct</span><span class="plain">, </span><span class="constant">0</span><span class="plain">); </span><span class="identifier">STDOUT_struct</span><span class="plain">.</span><span class="element">write_to_file</span><span class="plain"> = </span><span class="identifier">stdout</span><span class="plain">;</span>
<span class="identifier">stdout_wrapper_initialised</span><span class="plain"> = </span><span class="constant">TRUE</span><span class="plain">;</span>
<span class="plain">#</span><span class="identifier">ifdef</span><span class="plain"> </span><span class="constant">LOCALE_IS_ISO</span>
<span class="identifier">STDOUT_struct</span><span class="plain">.</span><span class="element">stream_flags</span><span class="plain"> |= </span><span class="constant">FILE_ENCODING_ISO_STRF</span><span class="plain">;</span>
<span class="plain">#</span><span class="identifier">endif</span>
<span class="plain">#</span><span class="identifier">ifdef</span><span class="plain"> </span><span class="identifier">LOCALE_IS_UTF8</span>
<span class="identifier">STDOUT_struct</span><span class="plain">.</span><span class="element">stream_flags</span><span class="plain"> |= </span><span class="constant">FILE_ENCODING_UTF8_STRF</span><span class="plain">;</span>
<span class="plain">#</span><span class="identifier">endif</span>
<span class="plain">}</span>
<span class="reserved">return</span><span class="plain"> &amp;</span><span class="identifier">STDOUT_struct</span><span class="plain">;</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Streams::get_stdout is used in <a href="#SP5">&#167;5</a>.</p>
<p class="inwebparagraph"><a id="SP23"></a><b>&#167;23. </b>And similarly for the standard error file.
</p>
<pre class="display">
<span class="reserved">text_stream</span><span class="plain"> </span><span class="identifier">STDERR_struct</span><span class="plain">; </span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">stderr_wrapper_initialised</span><span class="plain"> = </span><span class="constant">FALSE</span><span class="plain">;</span>
<span class="reserved">text_stream</span><span class="plain"> *</span><span class="functiontext">Streams::get_stderr</span><span class="plain">(</span><span class="reserved">void</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stderr_wrapper_initialised</span><span class="plain"> == </span><span class="constant">FALSE</span><span class="plain">) {</span>
<span class="functiontext">Streams::initialise</span><span class="plain">(&amp;</span><span class="identifier">STDERR_struct</span><span class="plain">, </span><span class="constant">0</span><span class="plain">); </span><span class="identifier">STDERR_struct</span><span class="plain">.</span><span class="element">write_to_file</span><span class="plain"> = </span><span class="identifier">stderr</span><span class="plain">;</span>
<span class="identifier">stderr_wrapper_initialised</span><span class="plain"> = </span><span class="constant">TRUE</span><span class="plain">;</span>
<span class="plain">#</span><span class="identifier">ifdef</span><span class="plain"> </span><span class="constant">LOCALE_IS_ISO</span>
<span class="identifier">STDERR_struct</span><span class="plain">.</span><span class="element">stream_flags</span><span class="plain"> |= </span><span class="constant">FILE_ENCODING_ISO_STRF</span><span class="plain">;</span>
<span class="plain">#</span><span class="identifier">endif</span>
<span class="plain">#</span><span class="identifier">ifdef</span><span class="plain"> </span><span class="identifier">LOCALE_IS_UTF8</span>
<span class="identifier">STDERR_struct</span><span class="plain">.</span><span class="element">stream_flags</span><span class="plain"> |= </span><span class="constant">FILE_ENCODING_UTF8_STRF</span><span class="plain">;</span>
<span class="plain">#</span><span class="identifier">endif</span>
<span class="plain">}</span>
<span class="reserved">return</span><span class="plain"> &amp;</span><span class="identifier">STDERR_struct</span><span class="plain">;</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Streams::get_stderr is used in <a href="#SP5">&#167;5</a>.</p>
<p class="inwebparagraph"><a id="SP24"></a><b>&#167;24. Creating file streams. </b>Note that this can fail, if the host filing system refuses to open the file,
so we return <code class="display"><span class="extract">TRUE</span></code> if and only if successful.
</p>
<pre class="display">
<span class="reserved">int</span><span class="plain"> </span><span class="functiontext">Streams::open_to_file</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">stream</span><span class="plain">, </span><span class="reserved">filename</span><span class="plain"> *</span><span class="identifier">name</span><span class="plain">, </span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">encoding</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) </span><span class="identifier">internal_error</span><span class="plain">(</span><span class="string">"tried to open NULL stream"</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">name</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) </span><span class="identifier">internal_error</span><span class="plain">(</span><span class="string">"stream_open_to_file on null filename"</span><span class="plain">);</span>
<span class="functiontext">Streams::initialise</span><span class="plain">(</span><span class="identifier">stream</span><span class="plain">, </span><span class="constant">FOR_FI_STRF</span><span class="plain">);</span>
<span class="reserved">switch</span><span class="plain">(</span><span class="identifier">encoding</span><span class="plain">) {</span>
<span class="reserved">case</span><span class="plain"> </span><span class="identifier">UTF8_ENC:</span><span class="plain"> </span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_flags</span><span class="plain"> |= </span><span class="constant">FILE_ENCODING_UTF8_STRF</span><span class="plain">; </span><span class="reserved">break</span><span class="plain">;</span>
<span class="reserved">case</span><span class="plain"> </span><span class="identifier">ISO_ENC:</span><span class="plain"> </span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_flags</span><span class="plain"> |= </span><span class="constant">FILE_ENCODING_ISO_STRF</span><span class="plain">; </span><span class="reserved">break</span><span class="plain">;</span>
<span class="identifier">default:</span><span class="plain"> </span><span class="identifier">internal_error</span><span class="plain">(</span><span class="string">"stream has unknown text encoding"</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">write_to_file</span><span class="plain"> = </span><span class="functiontext">Filenames::fopen</span><span class="plain">(</span><span class="identifier">name</span><span class="plain">, </span><span class="string">"w"</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">write_to_file</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) </span><span class="reserved">return</span><span class="plain"> </span><span class="constant">FALSE</span><span class="plain">;</span>
<span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">file_written</span><span class="plain"> = </span><span class="identifier">name</span><span class="plain">;</span>
<span class="identifier">total_file_writes</span><span class="plain">++;</span>
<span class="reserved">return</span><span class="plain"> </span><span class="constant">TRUE</span><span class="plain">;</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Streams::open_to_file is used in <a href="#SP9">&#167;9</a>, 8/bf (<a href="8-bf.html#SP4">&#167;4</a>).</p>
<p class="inwebparagraph"><a id="SP25"></a><b>&#167;25. </b>Similarly for appending:
</p>
<pre class="display">
<span class="reserved">int</span><span class="plain"> </span><span class="functiontext">Streams::open_to_file_append</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">stream</span><span class="plain">, </span><span class="reserved">filename</span><span class="plain"> *</span><span class="identifier">name</span><span class="plain">, </span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">encoding</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) </span><span class="identifier">internal_error</span><span class="plain">(</span><span class="string">"tried to open NULL stream"</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">name</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) </span><span class="identifier">internal_error</span><span class="plain">(</span><span class="string">"stream_open_to_file on null filename"</span><span class="plain">);</span>
<span class="functiontext">Streams::initialise</span><span class="plain">(</span><span class="identifier">stream</span><span class="plain">, </span><span class="constant">FOR_FI_STRF</span><span class="plain">);</span>
<span class="reserved">switch</span><span class="plain">(</span><span class="identifier">encoding</span><span class="plain">) {</span>
<span class="reserved">case</span><span class="plain"> </span><span class="identifier">UTF8_ENC:</span><span class="plain"> </span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_flags</span><span class="plain"> |= </span><span class="constant">FILE_ENCODING_UTF8_STRF</span><span class="plain">; </span><span class="reserved">break</span><span class="plain">;</span>
<span class="reserved">case</span><span class="plain"> </span><span class="identifier">ISO_ENC:</span><span class="plain"> </span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_flags</span><span class="plain"> |= </span><span class="constant">FILE_ENCODING_ISO_STRF</span><span class="plain">; </span><span class="reserved">break</span><span class="plain">;</span>
<span class="identifier">default:</span><span class="plain"> </span><span class="identifier">internal_error</span><span class="plain">(</span><span class="string">"stream has unknown text encoding"</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">write_to_file</span><span class="plain"> = </span><span class="functiontext">Filenames::fopen</span><span class="plain">(</span><span class="identifier">name</span><span class="plain">, </span><span class="string">"a"</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">write_to_file</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) </span><span class="reserved">return</span><span class="plain"> </span><span class="constant">FALSE</span><span class="plain">;</span>
<span class="identifier">total_file_writes</span><span class="plain">++;</span>
<span class="reserved">return</span><span class="plain"> </span><span class="constant">TRUE</span><span class="plain">;</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Streams::open_to_file_append is used in <a href="#SP9">&#167;9</a>.</p>
<p class="inwebparagraph"><a id="SP26"></a><b>&#167;26. Creating memory streams. </b>Here we have a choice. One option is to use <code class="display"><span class="extract">malloc</span></code> to allocate memory to hold
the text of the stream; this too can fail for host platform reasons, so again
we return a success code.
</p>
<pre class="display">
<span class="reserved">int</span><span class="plain"> </span><span class="functiontext">Streams::open_to_memory</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">stream</span><span class="plain">, </span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">capacity</span><span class="plain">) {</span>
<span class="identifier">CREATE_MUTEX</span><span class="plain">(</span><span class="identifier">mutex</span><span class="plain">);</span>
<span class="identifier">LOCK_MUTEX</span><span class="plain">(</span><span class="identifier">mutex</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) </span><span class="identifier">internal_error</span><span class="plain">(</span><span class="string">"tried to open NULL stream"</span><span class="plain">);</span>
<span class="identifier">capacity</span><span class="plain"> += </span><span class="constant">SPACE_AT_END_OF_STREAM</span><span class="plain">;</span>
<span class="functiontext">Streams::initialise</span><span class="plain">(</span><span class="identifier">stream</span><span class="plain">, </span><span class="constant">FOR_OM_STRF</span><span class="plain">);</span>
<span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">write_to_memory</span><span class="plain"> = </span><span class="functiontext">Memory::I7_calloc</span><span class="plain">(</span><span class="identifier">capacity</span><span class="plain">, </span><span class="reserved">sizeof</span><span class="plain">(</span><span class="identifier">wchar_t</span><span class="plain">), </span><span class="constant">STREAM_MREASON</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">write_to_memory</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) </span><span class="reserved">return</span><span class="plain"> </span><span class="constant">FALSE</span><span class="plain">;</span>
<span class="plain">(</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">write_to_memory</span><span class="plain">)[0] = </span><span class="constant">0</span><span class="plain">;</span>
<span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_flags</span><span class="plain"> |= </span><span class="constant">MALLOCED_STRF</span><span class="plain">;</span>
<span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">chars_capacity</span><span class="plain"> = </span><span class="identifier">capacity</span><span class="plain">;</span>
<span class="identifier">UNLOCK_MUTEX</span><span class="plain">(</span><span class="identifier">mutex</span><span class="plain">);</span>
<span class="reserved">return</span><span class="plain"> </span><span class="constant">TRUE</span><span class="plain">;</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Streams::open_to_memory is used in <a href="#SP9">&#167;9</a>, <a href="#SP28_3">&#167;28.3</a>, 4/sm (<a href="4-sm.html#SP2">&#167;2</a>, <a href="4-sm.html#SP3">&#167;3</a>).</p>
<p class="inwebparagraph"><a id="SP27"></a><b>&#167;27. </b>The other option avoids <code class="display"><span class="extract">malloc</span></code> by using specific storage already available.
If called validly, this cannot fail.
</p>
<pre class="display">
<span class="reserved">text_stream</span><span class="plain"> </span><span class="functiontext">Streams::new_buffer</span><span class="plain">(</span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">capacity</span><span class="plain">, </span><span class="identifier">wchar_t</span><span class="plain"> *</span><span class="identifier">at</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">at</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) </span><span class="identifier">internal_error</span><span class="plain">(</span><span class="string">"tried to make stream wrapper for NULL string"</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">capacity</span><span class="plain"> &lt; </span><span class="constant">SPACE_AT_END_OF_STREAM</span><span class="plain">)</span>
<span class="identifier">internal_error</span><span class="plain">(</span><span class="string">"memory stream too small"</span><span class="plain">);</span>
<span class="reserved">text_stream</span><span class="plain"> </span><span class="identifier">stream</span><span class="plain">;</span>
<span class="functiontext">Streams::initialise</span><span class="plain">(&amp;</span><span class="identifier">stream</span><span class="plain">, </span><span class="constant">FOR_TT_STRF</span><span class="plain">);</span>
<span class="identifier">stream</span><span class="plain">.</span><span class="element">write_to_memory</span><span class="plain"> = </span><span class="identifier">at</span><span class="plain">;</span>
<span class="plain">(</span><span class="identifier">stream</span><span class="plain">.</span><span class="element">write_to_memory</span><span class="plain">)[0] = </span><span class="constant">0</span><span class="plain">;</span>
<span class="identifier">stream</span><span class="plain">.</span><span class="element">chars_capacity</span><span class="plain"> = </span><span class="identifier">capacity</span><span class="plain"> - </span><span class="constant">SPACE_AT_END_OF_STREAM</span><span class="plain">;</span>
<span class="reserved">return</span><span class="plain"> </span><span class="identifier">stream</span><span class="plain">;</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Streams::new_buffer is used in <a href="#SP8">&#167;8</a>, 4/pm (<a href="4-pm.html#SP10">&#167;10</a>).</p>
<p class="inwebparagraph"><a id="SP28"></a><b>&#167;28. Converting from C strings. </b>We then have three ways to open a stream whose initial contents are given
by a C string. First, a wide string (a sequence of 32-bit Unicode code
points, null terminated):
</p>
<pre class="display">
<span class="reserved">int</span><span class="plain"> </span><span class="functiontext">Streams::open_from_wide_string</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">stream</span><span class="plain">, </span><span class="identifier">wchar_t</span><span class="plain"> *</span><span class="identifier">c_string</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) </span><span class="identifier">internal_error</span><span class="plain">(</span><span class="string">"tried to open NULL stream"</span><span class="plain">);</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">capacity</span><span class="plain"> = (</span><span class="identifier">c_string</span><span class="plain">)?((</span><span class="reserved">int</span><span class="plain">) </span><span class="identifier">wcslen</span><span class="plain">(</span><span class="identifier">c_string</span><span class="plain">)):0;</span>
&lt;<span class="cwebmacro">Ensure a capacity large enough to hold the initial string in one frame</span> <span class="cwebmacronumber">28.3</span>&gt;<span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">c_string</span><span class="plain">) </span><span class="functiontext">Streams::write_wide_string</span><span class="plain">(</span><span class="identifier">stream</span><span class="plain">, </span><span class="identifier">c_string</span><span class="plain">);</span>
<span class="reserved">return</span><span class="plain"> </span><span class="constant">TRUE</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Streams::write_wide_string</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">stream</span><span class="plain">, </span><span class="identifier">wchar_t</span><span class="plain"> *</span><span class="identifier">c_string</span><span class="plain">) {</span>
<span class="reserved">for</span><span class="plain"> (</span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">i</span><span class="plain">=0; </span><span class="identifier">c_string</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">]; </span><span class="identifier">i</span><span class="plain">++) </span><span class="functiontext">Streams::putc</span><span class="plain">(</span><span class="identifier">c_string</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">], </span><span class="identifier">stream</span><span class="plain">);</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Streams::open_from_wide_string is used in 4/sm (<a href="4-sm.html#SP4">&#167;4</a>, <a href="4-sm.html#SP5">&#167;5</a>).</p>
<p class="endnote">The function Streams::write_wide_string is used in 4/sm (<a href="4-sm.html#SP17">&#167;17</a>).</p>
<p class="inwebparagraph"><a id="SP28_1"></a><b>&#167;28.1. </b>Similarly, an ISO string (a sequence of 8-bit code points in the first
page of the Unicode set, null terminated):
</p>
<pre class="display">
<span class="reserved">int</span><span class="plain"> </span><span class="functiontext">Streams::open_from_ISO_string</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">stream</span><span class="plain">, </span><span class="reserved">char</span><span class="plain"> *</span><span class="identifier">c_string</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) </span><span class="identifier">internal_error</span><span class="plain">(</span><span class="string">"tried to open NULL stream"</span><span class="plain">);</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">capacity</span><span class="plain"> = (</span><span class="identifier">c_string</span><span class="plain">)?((</span><span class="reserved">int</span><span class="plain">) </span><span class="identifier">strlen</span><span class="plain">(</span><span class="identifier">c_string</span><span class="plain">)):0;</span>
&lt;<span class="cwebmacro">Ensure a capacity large enough to hold the initial string in one frame</span> <span class="cwebmacronumber">28.3</span>&gt;<span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">c_string</span><span class="plain">) </span><span class="functiontext">Streams::write_ISO_string</span><span class="plain">(</span><span class="identifier">stream</span><span class="plain">, </span><span class="identifier">c_string</span><span class="plain">);</span>
<span class="reserved">return</span><span class="plain"> </span><span class="constant">TRUE</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Streams::write_ISO_string</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">stream</span><span class="plain">, </span><span class="reserved">char</span><span class="plain"> *</span><span class="identifier">c_string</span><span class="plain">) {</span>
<span class="reserved">for</span><span class="plain"> (</span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">i</span><span class="plain">=0; </span><span class="identifier">c_string</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">]; </span><span class="identifier">i</span><span class="plain">++) </span><span class="functiontext">Streams::putc</span><span class="plain">(</span><span class="identifier">c_string</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">], </span><span class="identifier">stream</span><span class="plain">);</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Streams::open_from_ISO_string is used in <a href="#SP32">&#167;32</a>, 4/sm (<a href="4-sm.html#SP4">&#167;4</a>).</p>
<p class="endnote">The function Streams::write_ISO_string is used in <a href="#SP32">&#167;32</a>, 4/sm (<a href="4-sm.html#SP17">&#167;17</a>).</p>
<p class="inwebparagraph"><a id="SP28_2"></a><b>&#167;28.2. </b>Finally, a UTF-8 encoded C string:
</p>
<pre class="display">
<span class="reserved">int</span><span class="plain"> </span><span class="functiontext">Streams::open_from_UTF8_string</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">stream</span><span class="plain">, </span><span class="reserved">char</span><span class="plain"> *</span><span class="identifier">c_string</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) </span><span class="identifier">internal_error</span><span class="plain">(</span><span class="string">"tried to open NULL stream"</span><span class="plain">);</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">capacity</span><span class="plain"> = (</span><span class="identifier">c_string</span><span class="plain">)?((</span><span class="reserved">int</span><span class="plain">) </span><span class="identifier">strlen</span><span class="plain">(</span><span class="identifier">c_string</span><span class="plain">)):0;</span>
&lt;<span class="cwebmacro">Ensure a capacity large enough to hold the initial string in one frame</span> <span class="cwebmacronumber">28.3</span>&gt;<span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">c_string</span><span class="plain">) </span><span class="functiontext">Streams::write_UTF8_string</span><span class="plain">(</span><span class="identifier">stream</span><span class="plain">, </span><span class="identifier">c_string</span><span class="plain">);</span>
<span class="reserved">return</span><span class="plain"> </span><span class="constant">TRUE</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Streams::write_UTF8_string</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">stream</span><span class="plain">, </span><span class="reserved">char</span><span class="plain"> *</span><span class="identifier">c_string</span><span class="plain">) {</span>
<span class="reserved">unicode_file_buffer</span><span class="plain"> </span><span class="identifier">ufb</span><span class="plain"> = </span><span class="functiontext">TextFiles::create_ufb</span><span class="plain">();</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">c</span><span class="plain">;</span>
<span class="reserved">while</span><span class="plain"> ((</span><span class="identifier">c</span><span class="plain"> = </span><span class="functiontext">TextFiles::utf8_fgetc</span><span class="plain">(</span><span class="identifier">NULL</span><span class="plain">, &amp;</span><span class="identifier">c_string</span><span class="plain">, </span><span class="constant">FALSE</span><span class="plain">, &amp;</span><span class="identifier">ufb</span><span class="plain">)) != </span><span class="constant">0</span><span class="plain">)</span>
<span class="functiontext">Streams::putc</span><span class="plain">(</span><span class="identifier">c</span><span class="plain">, </span><span class="identifier">stream</span><span class="plain">);</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Streams::open_from_UTF8_string is used in <a href="#SP32">&#167;32</a>, 4/sm (<a href="4-sm.html#SP4">&#167;4</a>).</p>
<p class="endnote">The function Streams::write_UTF8_string is used in <a href="#SP32">&#167;32</a>, 4/sm (<a href="4-sm.html#SP17">&#167;17</a>).</p>
<p class="inwebparagraph"><a id="SP28_3"></a><b>&#167;28.3. </b>...all of which use:
</p>
<p class="macrodefinition"><code class="display">
&lt;<span class="cwebmacrodefn">Ensure a capacity large enough to hold the initial string in one frame</span> <span class="cwebmacronumber">28.3</span>&gt; =
</code></p>
<pre class="displaydefn">
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">capacity</span><span class="plain"> &lt; </span><span class="constant">8</span><span class="plain">) </span><span class="identifier">capacity</span><span class="plain"> = </span><span class="constant">8</span><span class="plain">;</span>
<span class="identifier">capacity</span><span class="plain"> += </span><span class="constant">1</span><span class="plain">+</span><span class="constant">SPACE_AT_END_OF_STREAM</span><span class="plain">;</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">rv</span><span class="plain"> = </span><span class="functiontext">Streams::open_to_memory</span><span class="plain">(</span><span class="identifier">stream</span><span class="plain">, </span><span class="identifier">capacity</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">rv</span><span class="plain"> == </span><span class="constant">FALSE</span><span class="plain">) </span><span class="reserved">return</span><span class="plain"> </span><span class="constant">FALSE</span><span class="plain">;</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">This code is used in <a href="#SP28">&#167;28</a>, <a href="#SP28_1">&#167;28.1</a>, <a href="#SP28_2">&#167;28.2</a>.</p>
<p class="inwebparagraph"><a id="SP29"></a><b>&#167;29. Converting to C strings. </b>Now for the converse problem.
</p>
<pre class="display">
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Streams::write_as_wide_string</span><span class="plain">(</span><span class="identifier">wchar_t</span><span class="plain"> *</span><span class="identifier">C_string</span><span class="plain">, </span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">stream</span><span class="plain">, </span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">buffer_size</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">buffer_size</span><span class="plain"> == </span><span class="constant">0</span><span class="plain">) </span><span class="reserved">return</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) { </span><span class="identifier">C_string</span><span class="plain">[0] = </span><span class="constant">0</span><span class="plain">; </span><span class="reserved">return</span><span class="plain">; }</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">write_to_file</span><span class="plain">) </span><span class="identifier">internal_error</span><span class="plain">(</span><span class="string">"stream_get_text on file stream"</span><span class="plain">);</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">i</span><span class="plain"> = </span><span class="constant">0</span><span class="plain">;</span>
<span class="reserved">while</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain">) {</span>
<span class="reserved">for</span><span class="plain"> (</span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">j</span><span class="plain">=0; </span><span class="identifier">j</span><span class="plain">&lt;</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">chars_written</span><span class="plain">; </span><span class="identifier">j</span><span class="plain">++) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">i</span><span class="plain"> &gt;= </span><span class="identifier">buffer_size</span><span class="plain">-1) </span><span class="reserved">break</span><span class="plain">;</span>
<span class="identifier">C_string</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">++] = </span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">write_to_memory</span><span class="plain">[</span><span class="identifier">j</span><span class="plain">];</span>
<span class="plain">}</span>
<span class="identifier">stream</span><span class="plain"> = </span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_continues</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="identifier">C_string</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">] = </span><span class="constant">0</span><span class="plain">;</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Streams::write_as_wide_string is used in 4/sm (<a href="4-sm.html#SP6">&#167;6</a>).</p>
<p class="inwebparagraph"><a id="SP30"></a><b>&#167;30. </b>Unicode code points outside the first page are flattened to <code class="display"><span class="extract">'?'</span></code> in an
ISO string:
</p>
<pre class="display">
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Streams::write_as_ISO_string</span><span class="plain">(</span><span class="reserved">char</span><span class="plain"> *</span><span class="identifier">C_string</span><span class="plain">, </span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">stream</span><span class="plain">, </span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">buffer_size</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">buffer_size</span><span class="plain"> == </span><span class="constant">0</span><span class="plain">) </span><span class="reserved">return</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) { </span><span class="identifier">C_string</span><span class="plain">[0] = </span><span class="constant">0</span><span class="plain">; </span><span class="reserved">return</span><span class="plain">; }</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">write_to_file</span><span class="plain">) </span><span class="identifier">internal_error</span><span class="plain">(</span><span class="string">"stream_get_text on file stream"</span><span class="plain">);</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">i</span><span class="plain"> = </span><span class="constant">0</span><span class="plain">;</span>
<span class="reserved">while</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain">) {</span>
<span class="reserved">for</span><span class="plain"> (</span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">j</span><span class="plain">=0; </span><span class="identifier">j</span><span class="plain">&lt;</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">chars_written</span><span class="plain">; </span><span class="identifier">j</span><span class="plain">++) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">i</span><span class="plain"> &gt;= </span><span class="identifier">buffer_size</span><span class="plain">-1) </span><span class="reserved">break</span><span class="plain">;</span>
<span class="identifier">wchar_t</span><span class="plain"> </span><span class="identifier">c</span><span class="plain"> = </span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">write_to_memory</span><span class="plain">[</span><span class="identifier">j</span><span class="plain">];</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">c</span><span class="plain"> &lt; </span><span class="constant">256</span><span class="plain">) </span><span class="identifier">C_string</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">++] = (</span><span class="reserved">char</span><span class="plain">) </span><span class="identifier">c</span><span class="plain">; </span><span class="reserved">else</span><span class="plain"> </span><span class="identifier">C_string</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">++] = </span><span class="character">'?'</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="identifier">stream</span><span class="plain"> = </span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_continues</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="identifier">C_string</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">] = </span><span class="constant">0</span><span class="plain">;</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Streams::write_as_ISO_string is used in <a href="#SP32">&#167;32</a>, 4/sm (<a href="4-sm.html#SP6">&#167;6</a>).</p>
<p class="inwebparagraph"><a id="SP31"></a><b>&#167;31. </b></p>
<pre class="display">
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Streams::write_as_UTF8_string</span><span class="plain">(</span><span class="reserved">char</span><span class="plain"> *</span><span class="identifier">C_string</span><span class="plain">, </span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">stream</span><span class="plain">, </span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">buffer_size</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">buffer_size</span><span class="plain"> == </span><span class="constant">0</span><span class="plain">) </span><span class="reserved">return</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) { </span><span class="identifier">C_string</span><span class="plain">[0] = </span><span class="constant">0</span><span class="plain">; </span><span class="reserved">return</span><span class="plain">; }</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">write_to_file</span><span class="plain">) </span><span class="identifier">internal_error</span><span class="plain">(</span><span class="string">"stream_get_text on file stream"</span><span class="plain">);</span>
<span class="reserved">unsigned</span><span class="plain"> </span><span class="reserved">char</span><span class="plain"> *</span><span class="identifier">to</span><span class="plain"> = (</span><span class="reserved">unsigned</span><span class="plain"> </span><span class="reserved">char</span><span class="plain"> *) </span><span class="identifier">C_string</span><span class="plain">;</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">i</span><span class="plain"> = </span><span class="constant">0</span><span class="plain">;</span>
<span class="reserved">while</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain">) {</span>
<span class="reserved">for</span><span class="plain"> (</span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">j</span><span class="plain">=0; </span><span class="identifier">j</span><span class="plain">&lt;</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">chars_written</span><span class="plain">; </span><span class="identifier">j</span><span class="plain">++) {</span>
<span class="reserved">unsigned</span><span class="plain"> </span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">c</span><span class="plain"> = (</span><span class="reserved">unsigned</span><span class="plain"> </span><span class="reserved">int</span><span class="plain">) </span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">write_to_memory</span><span class="plain">[</span><span class="identifier">j</span><span class="plain">];</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">c</span><span class="plain"> &gt;= </span><span class="constant">0x800</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">i</span><span class="plain"> &gt;= </span><span class="identifier">buffer_size</span><span class="plain">-3) </span><span class="reserved">break</span><span class="plain">;</span>
<span class="identifier">to</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">++] = </span><span class="constant">0xE0</span><span class="plain"> + (</span><span class="reserved">unsigned</span><span class="plain"> </span><span class="reserved">char</span><span class="plain">) (</span><span class="identifier">c</span><span class="plain"> &gt;&gt; </span><span class="constant">12</span><span class="plain">);</span>
<span class="identifier">to</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">++] = </span><span class="constant">0x80</span><span class="plain"> + (</span><span class="reserved">unsigned</span><span class="plain"> </span><span class="reserved">char</span><span class="plain">) ((</span><span class="identifier">c</span><span class="plain"> &gt;&gt; </span><span class="constant">6</span><span class="plain">) &amp; </span><span class="constant">0x3f</span><span class="plain">);</span>
<span class="identifier">to</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">++] = </span><span class="constant">0x80</span><span class="plain"> + (</span><span class="reserved">unsigned</span><span class="plain"> </span><span class="reserved">char</span><span class="plain">) (</span><span class="identifier">c</span><span class="plain"> &amp; </span><span class="constant">0x3f</span><span class="plain">);</span>
<span class="plain">} </span><span class="reserved">else</span><span class="plain"> </span><span class="reserved">if</span><span class="plain"> (</span><span class="identifier">c</span><span class="plain"> &gt;= </span><span class="constant">0x80</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">i</span><span class="plain"> &gt;= </span><span class="identifier">buffer_size</span><span class="plain">-2) </span><span class="reserved">break</span><span class="plain">;</span>
<span class="identifier">to</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">++] = </span><span class="constant">0xC0</span><span class="plain"> + (</span><span class="reserved">unsigned</span><span class="plain"> </span><span class="reserved">char</span><span class="plain">) (</span><span class="identifier">c</span><span class="plain"> &gt;&gt; </span><span class="constant">6</span><span class="plain">);</span>
<span class="identifier">to</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">++] = </span><span class="constant">0x80</span><span class="plain"> + (</span><span class="reserved">unsigned</span><span class="plain"> </span><span class="reserved">char</span><span class="plain">) (</span><span class="identifier">c</span><span class="plain"> &amp; </span><span class="constant">0x3f</span><span class="plain">);</span>
<span class="plain">} </span><span class="reserved">else</span><span class="plain"> {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">i</span><span class="plain"> &gt;= </span><span class="identifier">buffer_size</span><span class="plain">-1) </span><span class="reserved">break</span><span class="plain">;</span>
<span class="identifier">to</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">++] = (</span><span class="reserved">unsigned</span><span class="plain"> </span><span class="reserved">char</span><span class="plain">) </span><span class="identifier">c</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="identifier">stream</span><span class="plain"> = </span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_continues</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="identifier">to</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">] = </span><span class="constant">0</span><span class="plain">;</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Streams::write_as_UTF8_string is used in <a href="#SP32">&#167;32</a>, 4/sm (<a href="4-sm.html#SP6">&#167;6</a>).</p>
<p class="inwebparagraph"><a id="SP32"></a><b>&#167;32. Locale versions. </b></p>
<pre class="display">
<span class="reserved">int</span><span class="plain"> </span><span class="functiontext">Streams::open_from_locale_string</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">stream</span><span class="plain">, </span><span class="reserved">char</span><span class="plain"> *</span><span class="identifier">C_string</span><span class="plain">) {</span>
<span class="plain">#</span><span class="identifier">ifdef</span><span class="plain"> </span><span class="identifier">LOCALE_IS_UTF8</span>
<span class="reserved">return</span><span class="plain"> </span><span class="functiontext">Streams::open_from_UTF8_string</span><span class="plain">(</span><span class="identifier">stream</span><span class="plain">, </span><span class="identifier">C_string</span><span class="plain">);</span>
<span class="plain">#</span><span class="identifier">endif</span>
<span class="plain">#</span><span class="identifier">ifdef</span><span class="plain"> </span><span class="constant">LOCALE_IS_ISO</span>
<span class="reserved">return</span><span class="plain"> </span><span class="functiontext">Streams::open_from_ISO_string</span><span class="plain">(</span><span class="identifier">stream</span><span class="plain">, </span><span class="identifier">C_string</span><span class="plain">);</span>
<span class="plain">#</span><span class="identifier">endif</span>
<span class="plain">}</span>
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Streams::write_as_locale_string</span><span class="plain">(</span><span class="reserved">char</span><span class="plain"> *</span><span class="identifier">C_string</span><span class="plain">, </span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">stream</span><span class="plain">, </span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">buffer_size</span><span class="plain">) {</span>
<span class="plain">#</span><span class="identifier">ifdef</span><span class="plain"> </span><span class="identifier">LOCALE_IS_UTF8</span>
<span class="functiontext">Streams::write_as_UTF8_string</span><span class="plain">(</span><span class="identifier">C_string</span><span class="plain">, </span><span class="identifier">stream</span><span class="plain">, </span><span class="identifier">buffer_size</span><span class="plain">);</span>
<span class="plain">#</span><span class="identifier">endif</span>
<span class="plain">#</span><span class="identifier">ifdef</span><span class="plain"> </span><span class="constant">LOCALE_IS_ISO</span>
<span class="functiontext">Streams::write_as_ISO_string</span><span class="plain">(</span><span class="identifier">C_string</span><span class="plain">, </span><span class="identifier">stream</span><span class="plain">, </span><span class="identifier">buffer_size</span><span class="plain">);</span>
<span class="plain">#</span><span class="identifier">endif</span>
<span class="plain">}</span>
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Streams::write_locale_string</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">stream</span><span class="plain">, </span><span class="reserved">char</span><span class="plain"> *</span><span class="identifier">C_string</span><span class="plain">) {</span>
<span class="plain">#</span><span class="identifier">ifdef</span><span class="plain"> </span><span class="identifier">LOCALE_IS_UTF8</span>
<span class="functiontext">Streams::write_UTF8_string</span><span class="plain">(</span><span class="identifier">stream</span><span class="plain">, </span><span class="identifier">C_string</span><span class="plain">);</span>
<span class="plain">#</span><span class="identifier">endif</span>
<span class="plain">#</span><span class="identifier">ifdef</span><span class="plain"> </span><span class="constant">LOCALE_IS_ISO</span>
<span class="functiontext">Streams::write_ISO_string</span><span class="plain">(</span><span class="identifier">stream</span><span class="plain">, </span><span class="identifier">C_string</span><span class="plain">);</span>
<span class="plain">#</span><span class="identifier">endif</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Streams::open_from_locale_string is used in 4/sm (<a href="4-sm.html#SP4">&#167;4</a>, <a href="4-sm.html#SP5">&#167;5</a>).</p>
<p class="endnote">The function Streams::write_as_locale_string is used in 3/shl (<a href="3-shl.html#SP5">&#167;5</a>), 4/sm (<a href="4-sm.html#SP6">&#167;6</a>).</p>
<p class="endnote">The function Streams::write_locale_string is used in 3/cla (<a href="3-cla.html#SP8">&#167;8</a>), 3/drc (<a href="3-drc.html#SP2">&#167;2</a>).</p>
<p class="inwebparagraph"><a id="SP33"></a><b>&#167;33. Flush and close. </b>Note that flush is an operation which can be performed on any stream, including
<code class="display"><span class="extract">NULL</span></code>:
</p>
<pre class="display">
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Streams::flush</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">stream</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) </span><span class="reserved">return</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">write_to_file</span><span class="plain">) </span><span class="identifier">fflush</span><span class="plain">(</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">write_to_file</span><span class="plain">);</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Streams::flush is used in <a href="#SP10">&#167;10</a>.</p>
<p class="inwebparagraph"><a id="SP34"></a><b>&#167;34. </b>But closing is not allowed for <code class="display"><span class="extract">NULL</span></code> or the standard I/O wrappers:
</p>
<pre class="display">
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Streams::close</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">stream</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) </span><span class="identifier">internal_error</span><span class="plain">(</span><span class="string">"tried to close NULL stream"</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain"> == &amp;</span><span class="identifier">STDOUT_struct</span><span class="plain">) </span><span class="identifier">internal_error</span><span class="plain">(</span><span class="string">"tried to close STDOUT stream"</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain"> == &amp;</span><span class="identifier">STDERR_struct</span><span class="plain">) </span><span class="identifier">internal_error</span><span class="plain">(</span><span class="string">"tried to close STDERR stream"</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">chars_capacity</span><span class="plain"> == -1) </span><span class="identifier">internal_error</span><span class="plain">(</span><span class="string">"stream closed twice"</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_continues</span><span class="plain">) {</span>
<span class="functiontext">Streams::close</span><span class="plain">(</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_continues</span><span class="plain">);</span>
<span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_continues</span><span class="plain"> = </span><span class="identifier">NULL</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">chars_capacity</span><span class="plain"> = -1; </span><span class="comment"> mark as closed</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">write_to_file</span><span class="plain">) </span>&lt;<span class="cwebmacro">Take suitable action to close the file stream</span> <span class="cwebmacronumber">34.1</span>&gt;<span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">write_to_memory</span><span class="plain">) </span>&lt;<span class="cwebmacro">Take suitable action to close the memory stream</span> <span class="cwebmacronumber">34.2</span>&gt;<span class="plain">;</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Streams::close is used in <a href="#SP9">&#167;9</a>, <a href="#SP40">&#167;40</a>, <a href="#SP41">&#167;41</a>, 8/bf (<a href="8-bf.html#SP4">&#167;4</a>).</p>
<p class="inwebparagraph"><a id="SP34_1"></a><b>&#167;34.1. </b>Note that we need do nothing to close a memory stream when the storage
was supplied by our client; it only needs freeing if we were the ones who
allocated it.
</p>
<p class="inwebparagraph">Inscrutably, <code class="display"><span class="extract">fclose</span></code> returns <code class="display"><span class="extract">EOF</span></code> to report any failure.
</p>
<p class="macrodefinition"><code class="display">
&lt;<span class="cwebmacrodefn">Take suitable action to close the file stream</span> <span class="cwebmacronumber">34.1</span>&gt; =
</code></p>
<pre class="displaydefn">
<span class="reserved">if</span><span class="plain"> ((</span><span class="identifier">ferror</span><span class="plain">(</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">write_to_file</span><span class="plain">)) || (</span><span class="identifier">fclose</span><span class="plain">(</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">write_to_file</span><span class="plain">) == </span><span class="identifier">EOF</span><span class="plain">))</span>
<span class="functiontext">Errors::fatal</span><span class="plain">(</span><span class="string">"The host computer reported an error trying to write a text file"</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain"> != </span><span class="identifier">DL</span><span class="plain">)</span>
<span class="identifier">LOGIF</span><span class="plain">(</span><span class="identifier">TEXT_FILES</span><span class="plain">, </span><span class="string">"Text file '%f' (%s): %d characters written\n"</span><span class="plain">,</span>
<span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">file_written</span><span class="plain">,</span>
<span class="plain">(</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_flags</span><span class="plain"> &amp; </span><span class="constant">FILE_ENCODING_UTF8_STRF</span><span class="plain">)?</span><span class="string">"UTF8"</span><span class="plain">:</span><span class="string">"ISO"</span><span class="plain">,</span>
<span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">chars_written</span><span class="plain">);</span>
<span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">write_to_file</span><span class="plain"> = </span><span class="identifier">NULL</span><span class="plain">;</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">This code is used in <a href="#SP34">&#167;34</a>.</p>
<p class="inwebparagraph"><a id="SP34_2"></a><b>&#167;34.2. </b>Note that we need do nothing to close a memory stream when the storage
was supplied by our client; it only needs freeing if we were the ones who
allocated it. <code class="display"><span class="extract">free</span></code> is a void function; in theory it cannot fail, if
supplied a valid argument.
</p>
<p class="inwebparagraph">We have to be very careful once we have called <code class="display"><span class="extract">free</span></code>, because that memory
may well contain the <code class="display"><span class="extract">text_stream</span></code> structure to which <code class="display"><span class="extract">stream</span></code> points &mdash; see
how continuations are made, below.
</p>
<p class="macrodefinition"><code class="display">
&lt;<span class="cwebmacrodefn">Take suitable action to close the memory stream</span> <span class="cwebmacronumber">34.2</span>&gt; =
</code></p>
<pre class="displaydefn">
<span class="reserved">if</span><span class="plain"> ((</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_flags</span><span class="plain">) &amp; </span><span class="constant">MALLOCED_STRF</span><span class="plain">) {</span>
<span class="identifier">wchar_t</span><span class="plain"> *</span><span class="identifier">mem</span><span class="plain"> = </span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">write_to_memory</span><span class="plain">;</span>
<span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">write_to_memory</span><span class="plain"> = </span><span class="identifier">NULL</span><span class="plain">;</span>
<span class="functiontext">Memory::I7_free</span><span class="plain">(</span><span class="identifier">mem</span><span class="plain">, </span><span class="constant">STREAM_MREASON</span><span class="plain">, </span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">chars_capacity</span><span class="plain">*((</span><span class="reserved">int</span><span class="plain">) </span><span class="reserved">sizeof</span><span class="plain">(</span><span class="identifier">wchar_t</span><span class="plain">)));</span>
<span class="identifier">stream</span><span class="plain"> = </span><span class="identifier">NULL</span><span class="plain">;</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">This code is used in <a href="#SP34">&#167;34</a>.</p>
<p class="inwebparagraph"><a id="SP35"></a><b>&#167;35. Writing. </b>Our equivalent of <code class="display"><span class="extract">fputc</span></code> reads:
</p>
<pre class="display">
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Streams::putc</span><span class="plain">(</span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">c_int</span><span class="plain">, </span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">stream</span><span class="plain">) {</span>
<span class="reserved">unsigned</span><span class="plain"> </span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">c</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">c_int</span><span class="plain"> &gt;= </span><span class="constant">0</span><span class="plain">) </span><span class="identifier">c</span><span class="plain"> = (</span><span class="reserved">unsigned</span><span class="plain"> </span><span class="reserved">int</span><span class="plain">) </span><span class="identifier">c_int</span><span class="plain">; </span><span class="reserved">else</span><span class="plain"> </span><span class="identifier">c</span><span class="plain"> = (</span><span class="reserved">unsigned</span><span class="plain"> </span><span class="reserved">int</span><span class="plain">) (</span><span class="identifier">c_int</span><span class="plain"> + </span><span class="constant">256</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) </span><span class="reserved">return</span><span class="plain">;</span>
<span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">first_stream</span><span class="plain"> = </span><span class="identifier">stream</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">c</span><span class="plain"> != </span><span class="character">'\n'</span><span class="plain">) </span>&lt;<span class="cwebmacro">Insert indentation if this is pending</span> <span class="cwebmacronumber">35.2</span>&gt;<span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_flags</span><span class="plain"> &amp; </span><span class="constant">READ_ONLY_STRF</span><span class="plain">) </span><span class="identifier">internal_error</span><span class="plain">(</span><span class="string">"modifying read-only stream"</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> ((</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_flags</span><span class="plain">) &amp; </span><span class="constant">USES_XML_ESCAPES_STRF</span><span class="plain">) {</span>
<span class="reserved">switch</span><span class="plain">(</span><span class="identifier">c</span><span class="plain">) {</span>
<span class="reserved">case</span><span class="plain"> </span><span class="identifier">NEWLINE_IN_STRING:</span><span class="plain"> </span><span class="functiontext">Streams::literal</span><span class="plain">(</span><span class="identifier">stream</span><span class="plain">, </span><span class="string">"&lt;br&gt;"</span><span class="plain">); </span><span class="reserved">return</span><span class="plain">;</span>
<span class="reserved">case</span><span class="plain"> </span><span class="character">'&amp;'</span><span class="plain">: </span><span class="functiontext">Streams::literal</span><span class="plain">(</span><span class="identifier">stream</span><span class="plain">, </span><span class="string">"&amp;amp;"</span><span class="plain">); </span><span class="reserved">return</span><span class="plain">;</span>
<span class="reserved">case</span><span class="plain"> </span><span class="character">'&lt;'</span><span class="plain">: </span><span class="functiontext">Streams::literal</span><span class="plain">(</span><span class="identifier">stream</span><span class="plain">, </span><span class="string">"&amp;lt;"</span><span class="plain">); </span><span class="reserved">return</span><span class="plain">;</span>
<span class="reserved">case</span><span class="plain"> </span><span class="character">'&gt;'</span><span class="plain">: </span><span class="functiontext">Streams::literal</span><span class="plain">(</span><span class="identifier">stream</span><span class="plain">, </span><span class="string">"&amp;gt;"</span><span class="plain">); </span><span class="reserved">return</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="reserved">while</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_continues</span><span class="plain">) </span><span class="identifier">stream</span><span class="plain"> = </span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_continues</span><span class="plain">;</span>
&lt;<span class="cwebmacro">Ensure there is room to expand the escape sequence into</span> <span class="cwebmacronumber">35.3</span>&gt;<span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">write_to_file</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_flags</span><span class="plain"> &amp; </span><span class="constant">FILE_ENCODING_UTF8_STRF</span><span class="plain">)</span>
&lt;<span class="cwebmacro">Put a UTF8-encoded character into the underlying file</span> <span class="cwebmacronumber">35.1</span>&gt;
<span class="reserved">else</span><span class="plain"> </span><span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_flags</span><span class="plain"> &amp; </span><span class="constant">FILE_ENCODING_ISO_STRF</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">c</span><span class="plain"> &gt;= </span><span class="constant">0x100</span><span class="plain">) </span><span class="identifier">c</span><span class="plain"> = </span><span class="character">'?'</span><span class="plain">;</span>
<span class="identifier">fputc</span><span class="plain">((</span><span class="reserved">int</span><span class="plain">) </span><span class="identifier">c</span><span class="plain">, </span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">write_to_file</span><span class="plain">);</span>
<span class="plain">} </span><span class="reserved">else</span><span class="plain"> </span><span class="identifier">internal_error</span><span class="plain">(</span><span class="string">"stream has unknown text encoding"</span><span class="plain">);</span>
<span class="plain">} </span><span class="reserved">else</span><span class="plain"> </span><span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">write_to_memory</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> ((</span><span class="identifier">c</span><span class="plain"> &gt;= </span><span class="constant">0x0300</span><span class="plain">) &amp;&amp; (</span><span class="identifier">c</span><span class="plain"> &lt;= </span><span class="constant">0x036F</span><span class="plain">) &amp;&amp; (</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">chars_written</span><span class="plain"> &gt; </span><span class="constant">0</span><span class="plain">)) {</span>
<span class="identifier">c</span><span class="plain"> = (</span><span class="reserved">unsigned</span><span class="plain"> </span><span class="reserved">int</span><span class="plain">) </span><span class="functiontext">Characters::combine_accent</span><span class="plain">(</span>
<span class="plain">(</span><span class="reserved">int</span><span class="plain">) </span><span class="identifier">c</span><span class="plain">, (</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">write_to_memory</span><span class="plain">)[</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">chars_written</span><span class="plain"> - </span><span class="constant">1</span><span class="plain">]);</span>
<span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">chars_written</span><span class="plain">--;</span>
<span class="plain">}</span>
<span class="plain">(</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">write_to_memory</span><span class="plain">)[</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">chars_written</span><span class="plain">] = (</span><span class="identifier">wchar_t</span><span class="plain">) </span><span class="identifier">c</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">c</span><span class="plain"> == </span><span class="character">'\n'</span><span class="plain">) </span><span class="identifier">first_stream</span><span class="plain">-&gt;</span><span class="element">stream_flags</span><span class="plain"> |= </span><span class="constant">INDENT_PENDING_STRF</span><span class="plain">;</span>
<span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">chars_written</span><span class="plain">++;</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Streams::putc is used in <a href="#SP6">&#167;6</a>, <a href="#SP28">&#167;28</a>, <a href="#SP28_1">&#167;28.1</a>, <a href="#SP28_2">&#167;28.2</a>, <a href="#SP35_2">&#167;35.2</a>, <a href="#SP36">&#167;36</a>, <a href="#SP42">&#167;42</a>, 2/wal (<a href="2-wal.html#SP6">&#167;6</a>, <a href="2-wal.html#SP6_1_1">&#167;6.1.1</a>).</p>
<p class="inwebparagraph"><a id="SP35_1"></a><b>&#167;35.1. </b>Where we pack large character values, up to 65535, as follows.
</p>
<p class="macrodefinition"><code class="display">
&lt;<span class="cwebmacrodefn">Put a UTF8-encoded character into the underlying file</span> <span class="cwebmacronumber">35.1</span>&gt; =
</code></p>
<pre class="displaydefn">
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">c</span><span class="plain"> &gt;= </span><span class="constant">0x800</span><span class="plain">) {</span>
<span class="identifier">fputc</span><span class="plain">(</span><span class="constant">0xE0</span><span class="plain"> + (</span><span class="identifier">c</span><span class="plain"> &gt;&gt; </span><span class="constant">12</span><span class="plain">), </span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">write_to_file</span><span class="plain">);</span>
<span class="identifier">fputc</span><span class="plain">(</span><span class="constant">0x80</span><span class="plain"> + ((</span><span class="identifier">c</span><span class="plain"> &gt;&gt; </span><span class="constant">6</span><span class="plain">) &amp; </span><span class="constant">0x3f</span><span class="plain">), </span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">write_to_file</span><span class="plain">);</span>
<span class="identifier">fputc</span><span class="plain">(</span><span class="constant">0x80</span><span class="plain"> + (</span><span class="identifier">c</span><span class="plain"> &amp; </span><span class="constant">0x3f</span><span class="plain">), </span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="identifier">write_to_file</span><span class="plain">);</span>
<span class="plain">} </span><span class="reserved">else</span><span class="plain"> </span><span class="reserved">if</span><span class="plain"> (</span><span class="identifier">c</span><span class="plain"> &gt;= </span><span class="constant">0x80</span><span class="plain">) {</span>
<span class="identifier">fputc</span><span class="plain">(</span><span class="constant">0xC0</span><span class="plain"> + (</span><span class="identifier">c</span><span class="plain"> &gt;&gt; </span><span class="constant">6</span><span class="plain">), </span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">write_to_file</span><span class="plain">);</span>
<span class="identifier">fputc</span><span class="plain">(</span><span class="constant">0x80</span><span class="plain"> + (</span><span class="identifier">c</span><span class="plain"> &amp; </span><span class="constant">0x3f</span><span class="plain">), </span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="identifier">write_to_file</span><span class="plain">);</span>
<span class="plain">} </span><span class="reserved">else</span><span class="plain"> </span><span class="identifier">fputc</span><span class="plain">((</span><span class="reserved">int</span><span class="plain">) </span><span class="identifier">c</span><span class="plain">, </span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">write_to_file</span><span class="plain">);</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">This code is used in <a href="#SP35">&#167;35</a>.</p>
<p class="inwebparagraph"><a id="SP35_2"></a><b>&#167;35.2. </b><code class="display">
&lt;<span class="cwebmacrodefn">Insert indentation if this is pending</span> <span class="cwebmacronumber">35.2</span>&gt; =
</code></p>
<pre class="displaydefn">
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">first_stream</span><span class="plain">-&gt;</span><span class="element">stream_flags</span><span class="plain"> &amp; </span><span class="constant">INDENT_PENDING_STRF</span><span class="plain">) {</span>
<span class="identifier">first_stream</span><span class="plain">-&gt;</span><span class="element">stream_flags</span><span class="plain"> -= </span><span class="constant">INDENT_PENDING_STRF</span><span class="plain">;</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">L</span><span class="plain"> = (</span><span class="identifier">first_stream</span><span class="plain">-&gt;</span><span class="element">stream_flags</span><span class="plain"> &amp; </span><span class="constant">INDENTATION_MASK_STRF</span><span class="plain">)/</span><span class="constant">INDENTATION_BASE_STRF</span><span class="plain">;</span>
<span class="reserved">for</span><span class="plain"> (</span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">i</span><span class="plain">=0; </span><span class="identifier">i</span><span class="plain">&lt;</span><span class="identifier">L</span><span class="plain">; </span><span class="identifier">i</span><span class="plain">++) {</span>
<span class="functiontext">Streams::putc</span><span class="plain">(</span><span class="character">' '</span><span class="plain">, </span><span class="identifier">first_stream</span><span class="plain">); </span><span class="functiontext">Streams::putc</span><span class="plain">(</span><span class="character">' '</span><span class="plain">, </span><span class="identifier">first_stream</span><span class="plain">);</span>
<span class="functiontext">Streams::putc</span><span class="plain">(</span><span class="character">' '</span><span class="plain">, </span><span class="identifier">first_stream</span><span class="plain">); </span><span class="functiontext">Streams::putc</span><span class="plain">(</span><span class="character">' '</span><span class="plain">, </span><span class="identifier">first_stream</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">This code is used in <a href="#SP35">&#167;35</a>.</p>
<p class="inwebparagraph"><a id="SP35_3"></a><b>&#167;35.3. </b>The following is checked before any numerical <code class="display"><span class="extract">printf</span></code>-style escape is expanded
into the stream, or before any single character is written. Thus we cannot
overrun our buffers unless the expansion of a numerical escape exceeds
<code class="display"><span class="extract">SPACE_AT_END_OF_STREAM</span></code> plus 1 in size. Since no outside influence gets to
choose what formatting escapes we use (so that <code class="display"><span class="extract">%3000d</span></code>, say, can't occur),
we can be pretty confident.
</p>
<p class="inwebparagraph">The interesting case occurs when we run out of memory in a memory stream.
We make a continuation to a fresh <code class="display"><span class="extract">text_stream</span></code> structure, which points to twice
as much memory as the original, allocated via <code class="display"><span class="extract">malloc</span></code>. (We will actually need
a little more memory than that because we also have to make room for the
<code class="display"><span class="extract">text_stream</span></code> structure itself.) We then set <code class="display"><span class="extract">stream</span></code> to the <code class="display"><span class="extract">continuation</span></code>. Given
that <code class="display"><span class="extract">malloc</span></code> was successful &mdash; and it must have been or we would have stopped
with a fatal error &mdash; the continuation is guaranteed to be large enough,
since it's twice the size of the original, which itself was large enough to
hold any escape sequence when opened.
</p>
<p class="macrodefinition"><code class="display">
&lt;<span class="cwebmacrodefn">Ensure there is room to expand the escape sequence into</span> <span class="cwebmacronumber">35.3</span>&gt; =
</code></p>
<pre class="displaydefn">
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">chars_written</span><span class="plain"> + </span><span class="constant">SPACE_AT_END_OF_STREAM</span><span class="plain"> &gt;= </span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">chars_capacity</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">write_to_file</span><span class="plain">) </span><span class="reserved">return</span><span class="plain">; </span><span class="comment"> write nothing further</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">write_to_memory</span><span class="plain">) {</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">offset</span><span class="plain"> = (32 + </span><span class="constant">2</span><span class="plain">*(</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">chars_capacity</span><span class="plain">))*((</span><span class="reserved">int</span><span class="plain">) </span><span class="reserved">sizeof</span><span class="plain">(</span><span class="identifier">wchar_t</span><span class="plain">));</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">needed</span><span class="plain"> = </span><span class="identifier">offset</span><span class="plain"> + ((</span><span class="reserved">int</span><span class="plain">) </span><span class="reserved">sizeof</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain">)) + </span><span class="constant">32</span><span class="plain">;</span>
<span class="reserved">void</span><span class="plain"> *</span><span class="identifier">further_allocation</span><span class="plain"> = </span><span class="functiontext">Memory::I7_malloc</span><span class="plain">(</span><span class="identifier">needed</span><span class="plain">, </span><span class="constant">STREAM_MREASON</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">further_allocation</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) </span><span class="functiontext">Errors::fatal</span><span class="plain">(</span><span class="string">"Out of memory"</span><span class="plain">);</span>
<span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">continuation</span><span class="plain"> = (</span><span class="reserved">text_stream</span><span class="plain"> *) (</span><span class="identifier">further_allocation</span><span class="plain"> + </span><span class="identifier">offset</span><span class="plain">);</span>
<span class="functiontext">Streams::initialise</span><span class="plain">(</span><span class="identifier">continuation</span><span class="plain">, </span><span class="constant">FOR_CO_STRF</span><span class="plain">);</span>
<span class="identifier">continuation</span><span class="plain">-&gt;</span><span class="element">write_to_memory</span><span class="plain"> = </span><span class="identifier">further_allocation</span><span class="plain">;</span>
<span class="identifier">continuation</span><span class="plain">-&gt;</span><span class="element">chars_capacity</span><span class="plain"> = </span><span class="constant">2</span><span class="plain">*</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">chars_capacity</span><span class="plain">;</span>
<span class="plain">(</span><span class="identifier">continuation</span><span class="plain">-&gt;</span><span class="element">write_to_memory</span><span class="plain">)[0] = </span><span class="constant">0</span><span class="plain">;</span>
<span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_continues</span><span class="plain"> = </span><span class="identifier">continuation</span><span class="plain">;</span>
<span class="identifier">stream</span><span class="plain"> = </span><span class="identifier">continuation</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">This code is used in <a href="#SP35">&#167;35</a>.</p>
<p class="inwebparagraph"><a id="SP36"></a><b>&#167;36. </b>Literal printing is just printing with XML escapes switched off:
</p>
<pre class="display">
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Streams::literal</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">stream</span><span class="plain">, </span><span class="reserved">char</span><span class="plain"> *</span><span class="identifier">p</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) </span><span class="reserved">return</span><span class="plain">;</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">i</span><span class="plain">, </span><span class="identifier">x</span><span class="plain"> = ((</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_flags</span><span class="plain">) &amp; </span><span class="constant">USES_XML_ESCAPES_STRF</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">x</span><span class="plain">) </span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_flags</span><span class="plain"> -= </span><span class="constant">USES_XML_ESCAPES_STRF</span><span class="plain">;</span>
<span class="reserved">for</span><span class="plain"> (</span><span class="identifier">i</span><span class="plain">=0; </span><span class="identifier">p</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">]; </span><span class="identifier">i</span><span class="plain">++) </span><span class="functiontext">Streams::putc</span><span class="plain">((</span><span class="reserved">int</span><span class="plain">) </span><span class="identifier">p</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">], </span><span class="identifier">stream</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">x</span><span class="plain">) </span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_flags</span><span class="plain"> += </span><span class="constant">USES_XML_ESCAPES_STRF</span><span class="plain">;</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Streams::literal is used in <a href="#SP35">&#167;35</a>.</p>
<p class="inwebparagraph"><a id="SP37"></a><b>&#167;37. </b>Shifting indentation. For every indent there must be an equal and opposite
outdent, but error conditions can cause some compilation routines to issue
problem messages and then leave what they're doing incomplete, so we will
be a little cautious about assuming that a mismatch means an error.
</p>
<pre class="display">
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Streams::indent</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">stream</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) </span><span class="reserved">return</span><span class="plain">;</span>
<span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_flags</span><span class="plain"> += </span><span class="constant">INDENTATION_BASE_STRF</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Streams::outdent</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">stream</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) </span><span class="reserved">return</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> ((</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_flags</span><span class="plain"> &amp; </span><span class="constant">INDENTATION_MASK_STRF</span><span class="plain">) == </span><span class="constant">0</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="functiontext">Errors::have_occurred</span><span class="plain">() == </span><span class="constant">FALSE</span><span class="plain">) </span><span class="identifier">internal_error</span><span class="plain">(</span><span class="string">"stream indentation negative"</span><span class="plain">);</span>
<span class="reserved">return</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_flags</span><span class="plain"> -= </span><span class="constant">INDENTATION_BASE_STRF</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Streams::set_indentation</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">stream</span><span class="plain">, </span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">N</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) </span><span class="reserved">return</span><span class="plain">;</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">B</span><span class="plain"> = </span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_flags</span><span class="plain"> &amp; </span><span class="constant">INDENTATION_MASK_STRF</span><span class="plain">;</span>
<span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_flags</span><span class="plain"> -= </span><span class="identifier">B</span><span class="plain">;</span>
<span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_flags</span><span class="plain"> += </span><span class="identifier">N</span><span class="plain">*</span><span class="constant">INDENTATION_BASE_STRF</span><span class="plain">;</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Streams::indent is used in <a href="#SP7">&#167;7</a>.</p>
<p class="endnote">The function Streams::outdent is used in <a href="#SP7">&#167;7</a>.</p>
<p class="endnote">The function Streams::set_indentation is used in <a href="#SP7">&#167;7</a>.</p>
<p class="inwebparagraph"><a id="SP38"></a><b>&#167;38. </b>We can read the position for any stream, including <code class="display"><span class="extract">NULL</span></code>, but no matter
how much is written to <code class="display"><span class="extract">NULL</span></code> this position never budges.
</p>
<p class="inwebparagraph">Because of continuations, this is not as simple as returning the <code class="display"><span class="extract">chars_written</span></code>
field.
</p>
<pre class="display">
<span class="reserved">int</span><span class="plain"> </span><span class="functiontext">Streams::get_position</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">stream</span><span class="plain">) {</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">t</span><span class="plain"> = </span><span class="constant">0</span><span class="plain">;</span>
<span class="reserved">while</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain">) {</span>
<span class="identifier">t</span><span class="plain"> += </span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">chars_written</span><span class="plain">;</span>
<span class="identifier">stream</span><span class="plain"> = </span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_continues</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">return</span><span class="plain"> </span><span class="identifier">t</span><span class="plain">;</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Streams::get_position is used in <a href="#SP11">&#167;11</a>, <a href="#SP13">&#167;13</a>, 4/sm (<a href="4-sm.html#SP8">&#167;8</a>).</p>
<p class="inwebparagraph"><a id="SP39"></a><b>&#167;39. Memory-stream-only functions. </b>While it would be easy enough to implement this for file streams too, there's
no point, since it is used only in concert with backspacing.
</p>
<pre class="display">
<span class="reserved">int</span><span class="plain"> </span><span class="functiontext">Streams::latest</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">stream</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) </span><span class="reserved">return</span><span class="plain"> </span><span class="constant">0</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">write_to_file</span><span class="plain">) </span><span class="identifier">internal_error</span><span class="plain">(</span><span class="string">"stream_latest on file stream"</span><span class="plain">);</span>
<span class="reserved">while</span><span class="plain"> ((</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_continues</span><span class="plain">) &amp;&amp; (</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_continues</span><span class="plain">-&gt;</span><span class="element">chars_written</span><span class="plain"> &gt; </span><span class="constant">0</span><span class="plain">))</span>
<span class="identifier">stream</span><span class="plain"> = </span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_continues</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">write_to_memory</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">chars_written</span><span class="plain"> &gt; </span><span class="constant">0</span><span class="plain">)</span>
<span class="reserved">return</span><span class="plain"> (</span><span class="reserved">int</span><span class="plain">) ((</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">write_to_memory</span><span class="plain">)[</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">chars_written</span><span class="plain"> - </span><span class="constant">1</span><span class="plain">]);</span>
<span class="plain">}</span>
<span class="reserved">return</span><span class="plain"> </span><span class="constant">0</span><span class="plain">;</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Streams::latest is used in <a href="#SP14">&#167;14</a>.</p>
<p class="inwebparagraph"><a id="SP40"></a><b>&#167;40. </b>Accessing characters by index. Note that the stream terminates at the first
zero byte found, so that putting a zero truncates it.
</p>
<pre class="display">
<span class="identifier">wchar_t</span><span class="plain"> </span><span class="functiontext">Streams::get_char_at_index</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">stream</span><span class="plain">, </span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">position</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) </span><span class="identifier">internal_error</span><span class="plain">(</span><span class="string">"examining null stream"</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">write_to_file</span><span class="plain">) </span><span class="identifier">internal_error</span><span class="plain">(</span><span class="string">"examining file stream"</span><span class="plain">);</span>
<span class="reserved">while</span><span class="plain"> (</span><span class="identifier">position</span><span class="plain"> &gt;= </span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">chars_written</span><span class="plain">) {</span>
<span class="identifier">position</span><span class="plain"> = </span><span class="identifier">position</span><span class="plain"> - </span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">chars_written</span><span class="plain">;</span>
<span class="identifier">stream</span><span class="plain"> = </span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_continues</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) </span><span class="reserved">return</span><span class="plain"> </span><span class="constant">0</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">write_to_memory</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) </span><span class="reserved">return</span><span class="plain"> </span><span class="constant">0</span><span class="plain">;</span>
<span class="reserved">return</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">write_to_memory</span><span class="plain">)[</span><span class="identifier">position</span><span class="plain">];</span>
<span class="plain">}</span>
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Streams::put_char_at_index</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">stream</span><span class="plain">, </span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">position</span><span class="plain">, </span><span class="identifier">wchar_t</span><span class="plain"> </span><span class="identifier">C</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) </span><span class="identifier">internal_error</span><span class="plain">(</span><span class="string">"modifying null stream"</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">write_to_file</span><span class="plain">) </span><span class="identifier">internal_error</span><span class="plain">(</span><span class="string">"modifying file stream"</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_flags</span><span class="plain"> &amp; </span><span class="constant">READ_ONLY_STRF</span><span class="plain">) </span><span class="identifier">internal_error</span><span class="plain">(</span><span class="string">"modifying read-only stream"</span><span class="plain">);</span>
<span class="reserved">while</span><span class="plain"> (</span><span class="identifier">position</span><span class="plain"> &gt;= </span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">chars_written</span><span class="plain">) {</span>
<span class="identifier">position</span><span class="plain"> = </span><span class="identifier">position</span><span class="plain"> - </span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">chars_written</span><span class="plain">;</span>
<span class="identifier">stream</span><span class="plain"> = </span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_continues</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) </span><span class="identifier">internal_error</span><span class="plain">(</span><span class="string">"overrun memory stream"</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="plain">(</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">write_to_memory</span><span class="plain">)[</span><span class="identifier">position</span><span class="plain">] = </span><span class="identifier">C</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">C</span><span class="plain"> == </span><span class="constant">0</span><span class="plain">) {</span>
<span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">chars_written</span><span class="plain"> = </span><span class="identifier">position</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_continues</span><span class="plain">) {</span>
<span class="functiontext">Streams::close</span><span class="plain">(</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_continues</span><span class="plain">);</span>
<span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_continues</span><span class="plain"> = </span><span class="identifier">NULL</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Streams::get_char_at_index is used in 4/sm (<a href="4-sm.html#SP13">&#167;13</a>).</p>
<p class="endnote">The function Streams::put_char_at_index is used in 4/sm (<a href="4-sm.html#SP14">&#167;14</a>).</p>
<p class="inwebparagraph"><a id="SP41"></a><b>&#167;41. </b>Now for what is the trickiest function, because the position may be moved back
so that later continuations fall away. This will very rarely happen, so we
won't worry about the inefficiency of freeing up the memory saved by closing
such continuation blocks (which would be inefficient if we immediately had
to open similar ones again).
</p>
<pre class="display">
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Streams::set_position</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">stream</span><span class="plain">, </span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">position</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) </span><span class="reserved">return</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">position</span><span class="plain"> &lt; </span><span class="constant">0</span><span class="plain">) </span><span class="identifier">position</span><span class="plain"> = </span><span class="constant">0</span><span class="plain">; </span><span class="comment"> to simplify the implementation of backspacing</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">write_to_file</span><span class="plain">) </span><span class="identifier">internal_error</span><span class="plain">(</span><span class="string">"stream_set_position on file stream"</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_flags</span><span class="plain"> &amp; </span><span class="constant">READ_ONLY_STRF</span><span class="plain">) </span><span class="identifier">internal_error</span><span class="plain">(</span><span class="string">"modifying read-only stream"</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">write_to_memory</span><span class="plain">) {</span>
<span class="reserved">while</span><span class="plain"> (</span><span class="identifier">position</span><span class="plain"> &gt; </span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">chars_written</span><span class="plain">) {</span>
<span class="identifier">position</span><span class="plain"> = </span><span class="identifier">position</span><span class="plain"> - </span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">chars_written</span><span class="plain">;</span>
<span class="identifier">stream</span><span class="plain"> = </span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_continues</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) </span><span class="identifier">internal_error</span><span class="plain">(</span><span class="string">"can't set position forwards"</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">chars_written</span><span class="plain"> = </span><span class="identifier">position</span><span class="plain">;</span>
<span class="plain">(</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">write_to_memory</span><span class="plain">)[</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">chars_written</span><span class="plain">] = </span><span class="constant">0</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_continues</span><span class="plain">) {</span>
<span class="functiontext">Streams::close</span><span class="plain">(</span><span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_continues</span><span class="plain">);</span>
<span class="identifier">stream</span><span class="plain">-&gt;</span><span class="element">stream_continues</span><span class="plain"> = </span><span class="identifier">NULL</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Streams::set_position is used in <a href="#SP13">&#167;13</a>.</p>
<p class="inwebparagraph"><a id="SP42"></a><b>&#167;42. </b>Lastly, our copying function, where <code class="display"><span class="extract">from</span></code> has to be a memory stream (or
<code class="display"><span class="extract">NULL</span></code>) but <code class="display"><span class="extract">to</span></code> can be anything (including <code class="display"><span class="extract">NULL</span></code>).
</p>
<pre class="display">
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Streams::copy</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">to</span><span class="plain">, </span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">from</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> ((</span><span class="identifier">from</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) || (</span><span class="identifier">to</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">)) </span><span class="reserved">return</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">from</span><span class="plain">-&gt;</span><span class="identifier">write_to_file</span><span class="plain">) </span><span class="identifier">internal_error</span><span class="plain">(</span><span class="string">"stream_copy from file stream"</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">from</span><span class="plain">-&gt;</span><span class="identifier">write_to_memory</span><span class="plain">) {</span>
<span class="reserved">for</span><span class="plain"> (</span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">i</span><span class="plain">=0; </span><span class="identifier">i</span><span class="plain">&lt;</span><span class="identifier">from</span><span class="plain">-&gt;</span><span class="element">chars_written</span><span class="plain">; </span><span class="identifier">i</span><span class="plain">++) {</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">c</span><span class="plain"> = (</span><span class="reserved">int</span><span class="plain">) ((</span><span class="identifier">from</span><span class="plain">-&gt;</span><span class="element">write_to_memory</span><span class="plain">)[</span><span class="identifier">i</span><span class="plain">]);</span>
<span class="functiontext">Streams::putc</span><span class="plain">(</span><span class="identifier">c</span><span class="plain">, </span><span class="identifier">to</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">from</span><span class="plain">-&gt;</span><span class="identifier">stream_continues</span><span class="plain">) </span><span class="functiontext">Streams::copy</span><span class="plain">(</span><span class="identifier">to</span><span class="plain">, </span><span class="identifier">from</span><span class="plain">-&gt;</span><span class="element">stream_continues</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Streams::copy is used in <a href="#SP14">&#167;14</a>, <a href="#SP43">&#167;43</a>, 4/sm (<a href="4-sm.html#SP3">&#167;3</a>, <a href="4-sm.html#SP16">&#167;16</a>).</p>
<p class="inwebparagraph"><a id="SP43"></a><b>&#167;43. Writer. </b>This writes one stream into another one, which implements <code class="display"><span class="extract">%S</span></code>.
</p>
<pre class="display">
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Streams::writer</span><span class="plain">(</span><span class="constant">OUTPUT_STREAM</span><span class="plain">, </span><span class="reserved">char</span><span class="plain"> *</span><span class="identifier">format_string</span><span class="plain">, </span><span class="reserved">void</span><span class="plain"> *</span><span class="identifier">vS</span><span class="plain">) {</span>
<span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S</span><span class="plain"> = (</span><span class="reserved">text_stream</span><span class="plain"> *) </span><span class="identifier">vS</span><span class="plain">;</span>
<span class="functiontext">Streams::copy</span><span class="plain">(</span><span class="identifier">OUT</span><span class="plain">, </span><span class="identifier">S</span><span class="plain">);</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Streams::writer is used in 1/fm (<a href="1-fm.html#SP8_1">&#167;8.1</a>).</p>
<hr class="tocbar">
<ul class="toc"><li><a href="2-mmr.html">Back to 'Memory'</a></li><li><a href="2-wal.html">Continue with 'Writers and Loggers'</a></li></ul><hr class="tocbar">
<!--End of weave-->
</main>
</body>
</html>