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

373 lines
55 KiB
HTML

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Writers and Loggers</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 'Writers and Loggers' 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>Writers and Loggers</b></li></ul><p class="purpose">Formatted text output to streams.</p>
<ul class="toc"><li><a href="#SP1">&#167;1. Registration</a></li><li><a href="#SP6">&#167;6. Writing</a></li></ul><hr class="tocbar">
<p class="inwebparagraph"><a id="SP1"></a><b>&#167;1. Registration. </b>The main function here is modelled on the "minimum <code class="display"><span class="extract">printf</span></code>" function
used as an example in Kernighan and Ritchie, Chapter 7, but because it
prints to streams, it combines the traditional functions <code class="display"><span class="extract">printf</span></code>, <code class="display"><span class="extract">sprintf</span></code>
and <code class="display"><span class="extract">fprintf</span></code> in one. It also contains a number of doohickeys to provide
for a wider and extensible range of string interpolations.
</p>
<p class="inwebparagraph">Traditionally, in the C library, everything in the formatting string is
literal except for <code class="display"><span class="extract">%</span></code> escapes: thus <code class="display"><span class="extract">%d</span></code> means "integer goes here", and
so on. We follow this but allow extra <code class="display"><span class="extract">%</span></code> escapes unknown to K&amp;R, and we
also allow a further family of <code class="display"><span class="extract">$</span></code> escapes intended for the debugging log
only; these are restricted to streams flagged as for debugging and generally
produce guru meditation numbers rather than user-friendly information.
</p>
<p class="inwebparagraph">Each escape, say <code class="display"><span class="extract">%z</span></code>, must be "registered" before use, and will be
given one of the following categories:
</p>
<pre class="definitions">
<span class="definitionkeyword">define</span> <span class="constant">VACANT_ECAT</span><span class="plain"> </span><span class="constant">0</span><span class="plain"> </span><span class="comment"> unregistered</span>
<span class="definitionkeyword">define</span> <span class="constant">POINTER_ECAT</span><span class="plain"> </span><span class="constant">1</span><span class="plain"> </span><span class="comment"> data to be printed is a pointer to a structure</span>
<span class="definitionkeyword">define</span> <span class="constant">INTSIZED_ECAT</span><span class="plain"> </span><span class="constant">2</span><span class="plain"> </span><span class="comment"> data to be printed is or fits into an integer</span>
<span class="definitionkeyword">define</span> <span class="constant">WORDING_ECAT</span><span class="plain"> </span><span class="constant">3</span><span class="plain"> </span><span class="comment"> data to be printed is a <code class="display"><span class="extract">wording</span></code> structure from inform7</span>
<span class="definitionkeyword">define</span> <span class="constant">DIRECT_ECAT</span><span class="plain"> </span><span class="constant">4</span><span class="plain"> </span><span class="comment"> data must be printed directly by the code below</span>
</pre>
<p class="inwebparagraph"><a id="SP2"></a><b>&#167;2. </b>We'll start with <code class="display"><span class="extract">%</span></code> escapes, which generalise the familiar <code class="display"><span class="extract">printf</span></code>
escapes such as <code class="display"><span class="extract">%d</span></code>. Cumbersomely, we need three sorts of escape: those where
the variable argument token is a pointer, those where it's essentially an
integer, and those where it's a structure used only in the Inform 7 compiler
called a <code class="display"><span class="extract">wording</span></code>. The standard C typechecker can't generalise across these,
so we have to do everything three times. (And then we have to do all that twice,
because the loggers don't use format strings.)
</p>
<pre class="display">
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">escapes_registered</span><span class="plain"> = </span><span class="constant">FALSE</span><span class="plain">;</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">escapes_category</span><span class="plain">[2][128]; </span><span class="comment"> one of the <code class="display"><span class="extract">*_ECAT</span></code> values above</span>
<span class="reserved">void</span><span class="plain"> *</span><span class="identifier">the_escapes</span><span class="plain">[2][128]; </span><span class="comment"> the function to call to implement this</span>
<span class="reserved">typedef</span><span class="plain"> </span><span class="reserved">void</span><span class="plain"> (*</span><span class="identifier">writer_function</span><span class="plain">)(</span><span class="reserved">text_stream</span><span class="plain"> *, </span><span class="reserved">char</span><span class="plain"> *, </span><span class="reserved">void</span><span class="plain"> *);</span>
<span class="reserved">typedef</span><span class="plain"> </span><span class="reserved">void</span><span class="plain"> (*</span><span class="identifier">writer_function_I</span><span class="plain">)(</span><span class="reserved">text_stream</span><span class="plain"> *, </span><span class="reserved">char</span><span class="plain"> *, </span><span class="reserved">int</span><span class="plain">);</span>
<span class="reserved">typedef</span><span class="plain"> </span><span class="reserved">void</span><span class="plain"> (*</span><span class="identifier">log_function</span><span class="plain">)(</span><span class="reserved">text_stream</span><span class="plain"> *, </span><span class="reserved">void</span><span class="plain"> *);</span>
<span class="reserved">typedef</span><span class="plain"> </span><span class="reserved">void</span><span class="plain"> (*</span><span class="identifier">log_function_I</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="plain">#</span><span class="identifier">ifdef</span><span class="plain"> </span><span class="identifier">WORDS_MODULE</span>
<span class="reserved">typedef</span><span class="plain"> </span><span class="reserved">void</span><span class="plain"> (*</span><span class="identifier">writer_function_W</span><span class="plain">)(</span><span class="reserved">text_stream</span><span class="plain"> *, </span><span class="reserved">char</span><span class="plain"> *, </span><span class="identifier">wording</span><span class="plain">);</span>
<span class="reserved">typedef</span><span class="plain"> </span><span class="reserved">void</span><span class="plain"> (*</span><span class="identifier">log_function_W</span><span class="plain">)(</span><span class="reserved">text_stream</span><span class="plain"> *, </span><span class="identifier">wording</span><span class="plain">);</span>
<span class="plain">#</span><span class="identifier">endif</span>
</pre>
<p class="inwebparagraph"></p>
<p class="inwebparagraph"><a id="SP3"></a><b>&#167;3. </b></p>
<pre class="display">
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Writers::log_escape_usage</span><span class="plain">(</span><span class="reserved">void</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">cat</span><span class="plain"> = </span><span class="constant">0</span><span class="plain">; </span><span class="identifier">cat</span><span class="plain"> &lt; </span><span class="constant">2</span><span class="plain">; </span><span class="identifier">cat</span><span class="plain">++) {</span>
<span class="reserved">char</span><span class="plain"> *</span><span class="identifier">alphanum</span><span class="plain"> = </span><span class="string">"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"</span><span class="plain">;</span>
<span class="identifier">LOG</span><span class="plain">(</span><span class="string">"Vacant escapes: %s: "</span><span class="plain">, (</span><span class="identifier">cat</span><span class="plain"> == </span><span class="constant">0</span><span class="plain">)?</span><span class="string">"%"</span><span class="plain">:</span><span class="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">alphanum</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="reserved">if</span><span class="plain"> (</span><span class="identifier">escapes_category</span><span class="plain">[</span><span class="identifier">cat</span><span class="plain">][(</span><span class="reserved">int</span><span class="plain">) </span><span class="identifier">alphanum</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">]] == </span><span class="constant">VACANT_ECAT</span><span class="plain">)</span>
<span class="identifier">LOG</span><span class="plain">(</span><span class="string">"%c"</span><span class="plain">, </span><span class="identifier">alphanum</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">]);</span>
<span class="reserved">else</span>
<span class="identifier">LOG</span><span class="plain">(</span><span class="string">"."</span><span class="plain">);</span>
<span class="identifier">LOG</span><span class="plain">(</span><span class="string">"\n"</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Writers::log_escape_usage appears nowhere else.</p>
<p class="inwebparagraph"><a id="SP4"></a><b>&#167;4. </b>That gives us a number of front doors:
</p>
<pre class="display">
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Writers::register_writer</span><span class="plain">(</span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">esc</span><span class="plain">, </span><span class="reserved">void</span><span class="plain"> (*</span><span class="identifier">f</span><span class="plain">)(</span><span class="reserved">text_stream</span><span class="plain"> *, </span><span class="reserved">char</span><span class="plain"> *, </span><span class="reserved">void</span><span class="plain"> *)) {</span>
<span class="functiontext">Writers::register_writer_p</span><span class="plain">(0, </span><span class="identifier">esc</span><span class="plain">, (</span><span class="reserved">void</span><span class="plain"> *) </span><span class="identifier">f</span><span class="plain">, </span><span class="constant">POINTER_ECAT</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Writers::register_logger</span><span class="plain">(</span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">esc</span><span class="plain">, </span><span class="reserved">void</span><span class="plain"> (*</span><span class="identifier">f</span><span class="plain">)(</span><span class="reserved">text_stream</span><span class="plain"> *, </span><span class="reserved">void</span><span class="plain"> *)) {</span>
<span class="functiontext">Writers::register_writer_p</span><span class="plain">(1, </span><span class="identifier">esc</span><span class="plain">, (</span><span class="reserved">void</span><span class="plain"> *) </span><span class="identifier">f</span><span class="plain">, </span><span class="constant">POINTER_ECAT</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Writers::register_writer_I</span><span class="plain">(</span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">esc</span><span class="plain">, </span><span class="reserved">void</span><span class="plain"> (*</span><span class="identifier">f</span><span class="plain">)(</span><span class="reserved">text_stream</span><span class="plain"> *, </span><span class="reserved">char</span><span class="plain"> *, </span><span class="reserved">int</span><span class="plain">)) {</span>
<span class="functiontext">Writers::register_writer_p</span><span class="plain">(0, </span><span class="identifier">esc</span><span class="plain">, (</span><span class="reserved">void</span><span class="plain"> *) </span><span class="identifier">f</span><span class="plain">, </span><span class="constant">INTSIZED_ECAT</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Writers::register_logger_I</span><span class="plain">(</span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">esc</span><span class="plain">, </span><span class="reserved">void</span><span class="plain"> (*</span><span class="identifier">f</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="functiontext">Writers::register_writer_p</span><span class="plain">(1, </span><span class="identifier">esc</span><span class="plain">, (</span><span class="reserved">void</span><span class="plain"> *) </span><span class="identifier">f</span><span class="plain">, </span><span class="constant">INTSIZED_ECAT</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="plain">#</span><span class="identifier">ifdef</span><span class="plain"> </span><span class="identifier">WORDS_MODULE</span>
<span class="plain">#</span><span class="identifier">define</span><span class="plain"> </span><span class="identifier">Writers::register_writer_W</span><span class="plain">(</span><span class="identifier">esc</span><span class="plain">, </span><span class="identifier">f</span><span class="plain">) </span><span class="functiontext">Writers::register_writer_p</span><span class="plain">(0, </span><span class="identifier">esc</span><span class="plain">, (</span><span class="reserved">void</span><span class="plain"> *) </span><span class="identifier">f</span><span class="plain">, </span><span class="constant">WORDING_ECAT</span><span class="plain">);</span>
<span class="plain">#</span><span class="identifier">define</span><span class="plain"> </span><span class="identifier">Writers::register_logger_W</span><span class="plain">(</span><span class="identifier">esc</span><span class="plain">, </span><span class="identifier">f</span><span class="plain">) </span><span class="functiontext">Writers::register_writer_p</span><span class="plain">(1, </span><span class="identifier">esc</span><span class="plain">, (</span><span class="reserved">void</span><span class="plain"> *) </span><span class="identifier">f</span><span class="plain">, </span><span class="constant">WORDING_ECAT</span><span class="plain">);</span>
<span class="plain">#</span><span class="identifier">endif</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Writers::register_writer is used in 1/fm (<a href="1-fm.html#SP8_1">&#167;8.1</a>).</p>
<p class="endnote">The function Writers::register_logger is used in 1/fm (<a href="1-fm.html#SP8_3">&#167;8.3</a>).</p>
<p class="endnote">The function Writers::register_writer_I appears nowhere else.</p>
<p class="endnote">The function Writers::register_logger_I appears nowhere else.</p>
<p class="inwebparagraph"><a id="SP5"></a><b>&#167;5. </b>All leading to:
</p>
<pre class="display">
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Writers::register_writer_p</span><span class="plain">(</span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">set</span><span class="plain">, </span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">esc</span><span class="plain">, </span><span class="reserved">void</span><span class="plain"> *</span><span class="identifier">f</span><span class="plain">, </span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">cat</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">escapes_registered</span><span class="plain"> == </span><span class="constant">FALSE</span><span class="plain">) </span>&lt;<span class="cwebmacro">Initialise the table of escapes</span> <span class="cwebmacronumber">5.1</span>&gt;<span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> ((</span><span class="identifier">esc</span><span class="plain"> &lt; </span><span class="constant">0</span><span class="plain">) || (</span><span class="identifier">esc</span><span class="plain"> &gt;= </span><span class="constant">128</span><span class="plain">) ||</span>
<span class="plain">((</span><span class="functiontext">Characters::isalpha</span><span class="plain">(</span><span class="identifier">esc</span><span class="plain">) == </span><span class="constant">FALSE</span><span class="plain">) &amp;&amp; (</span><span class="functiontext">Characters::isdigit</span><span class="plain">(</span><span class="identifier">esc</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">"nonalphabetic escape"</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">escapes_category</span><span class="plain">[</span><span class="identifier">set</span><span class="plain">][</span><span class="identifier">esc</span><span class="plain">] != </span><span class="constant">VACANT_ECAT</span><span class="plain">) {</span>
<span class="identifier">WRITE_TO</span><span class="plain">(</span><span class="constant">STDERR</span><span class="plain">, </span><span class="string">"Clashing escape is %s%c\n"</span><span class="plain">, (</span><span class="identifier">set</span><span class="plain"> == </span><span class="constant">0</span><span class="plain">)?</span><span class="string">"%"</span><span class="plain">:</span><span class="string">"$"</span><span class="plain">, </span><span class="identifier">esc</span><span class="plain">);</span>
<span class="identifier">internal_error</span><span class="plain">(</span><span class="string">"clash of escapes"</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="identifier">escapes_category</span><span class="plain">[</span><span class="identifier">set</span><span class="plain">][</span><span class="identifier">esc</span><span class="plain">] = </span><span class="identifier">cat</span><span class="plain">;</span>
<span class="identifier">the_escapes</span><span class="plain">[</span><span class="identifier">set</span><span class="plain">][</span><span class="identifier">esc</span><span class="plain">] = </span><span class="identifier">f</span><span class="plain">;</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Writers::register_writer_p is used in <a href="#SP4">&#167;4</a>.</p>
<p class="inwebparagraph"><a id="SP5_1"></a><b>&#167;5.1. </b>We're going to implement <code class="display"><span class="extract">%d</span></code> and a few others directly, so those are marked
in the table as being unavailable for registration.
</p>
<p class="inwebparagraph">Note that we don't support <code class="display"><span class="extract">%f</span></code> for floats; but we do add our very own <code class="display"><span class="extract">%w</span></code>
for wide strings.
</p>
<p class="macrodefinition"><code class="display">
&lt;<span class="cwebmacrodefn">Initialise the table of escapes</span> <span class="cwebmacronumber">5.1</span>&gt; =
</code></p>
<pre class="displaydefn">
<span class="identifier">escapes_registered</span><span class="plain"> = </span><span class="constant">TRUE</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">e</span><span class="plain">=0; </span><span class="identifier">e</span><span class="plain">&lt;2; </span><span class="identifier">e</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;128; </span><span class="identifier">i</span><span class="plain">++) {</span>
<span class="identifier">the_escapes</span><span class="plain">[</span><span class="identifier">e</span><span class="plain">][</span><span class="identifier">i</span><span class="plain">] = </span><span class="identifier">NULL</span><span class="plain">; </span><span class="identifier">escapes_category</span><span class="plain">[</span><span class="identifier">e</span><span class="plain">][</span><span class="identifier">i</span><span class="plain">] = </span><span class="constant">VACANT_ECAT</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="identifier">escapes_category</span><span class="plain">[0][</span><span class="character">'c'</span><span class="plain">] = </span><span class="constant">DIRECT_ECAT</span><span class="plain">;</span>
<span class="identifier">escapes_category</span><span class="plain">[0][</span><span class="character">'d'</span><span class="plain">] = </span><span class="constant">DIRECT_ECAT</span><span class="plain">;</span>
<span class="identifier">escapes_category</span><span class="plain">[0][</span><span class="character">'g'</span><span class="plain">] = </span><span class="constant">DIRECT_ECAT</span><span class="plain">;</span>
<span class="identifier">escapes_category</span><span class="plain">[0][</span><span class="character">'i'</span><span class="plain">] = </span><span class="constant">DIRECT_ECAT</span><span class="plain">;</span>
<span class="identifier">escapes_category</span><span class="plain">[0][</span><span class="character">'s'</span><span class="plain">] = </span><span class="constant">DIRECT_ECAT</span><span class="plain">;</span>
<span class="identifier">escapes_category</span><span class="plain">[0][</span><span class="character">'w'</span><span class="plain">] = </span><span class="constant">DIRECT_ECAT</span><span class="plain">;</span>
<span class="identifier">escapes_category</span><span class="plain">[0][</span><span class="character">'x'</span><span class="plain">] = </span><span class="constant">DIRECT_ECAT</span><span class="plain">;</span>
<span class="identifier">escapes_category</span><span class="plain">[0][</span><span class="character">'%'</span><span class="plain">] = </span><span class="constant">DIRECT_ECAT</span><span class="plain">;</span>
<span class="identifier">escapes_category</span><span class="plain">[0][</span><span class="character">'$'</span><span class="plain">] = </span><span class="constant">DIRECT_ECAT</span><span class="plain">;</span>
<span class="identifier">escapes_category</span><span class="plain">[1][</span><span class="character">'%'</span><span class="plain">] = </span><span class="constant">DIRECT_ECAT</span><span class="plain">;</span>
<span class="identifier">escapes_category</span><span class="plain">[1][</span><span class="character">'$'</span><span class="plain">] = </span><span class="constant">DIRECT_ECAT</span><span class="plain">;</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">This code is used in <a href="#SP5">&#167;5</a>.</p>
<p class="inwebparagraph"><a id="SP6"></a><b>&#167;6. Writing. </b>We can finally get on with that formatted-print function we've all been
waiting for:
</p>
<pre class="display">
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Writers::printf</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">fmt</span><span class="plain">, ...) {</span>
<span class="identifier">va_list</span><span class="plain"> </span><span class="identifier">ap</span><span class="plain">; </span><span class="comment"> the variable argument list signified by the dots</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="identifier">va_start</span><span class="plain">(</span><span class="identifier">ap</span><span class="plain">, </span><span class="identifier">fmt</span><span class="plain">); </span><span class="comment"> macro to begin variable argument processing</span>
<span class="reserved">for</span><span class="plain"> (</span><span class="identifier">p</span><span class="plain"> = </span><span class="identifier">fmt</span><span class="plain">; *</span><span class="identifier">p</span><span class="plain">; </span><span class="identifier">p</span><span class="plain">++) {</span>
<span class="reserved">switch</span><span class="plain"> (*</span><span class="identifier">p</span><span class="plain">) {</span>
<span class="reserved">case</span><span class="plain"> </span><span class="character">'%'</span><span class="plain">: {</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">set</span><span class="plain"> = </span><span class="constant">0</span><span class="plain">; </span>&lt;<span class="cwebmacro">Deal with escape sequences</span> <span class="cwebmacronumber">6.1</span>&gt;<span class="plain">;</span>
<span class="reserved">break</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">case</span><span class="plain"> </span><span class="character">'$'</span><span class="plain">: {</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">set</span><span class="plain"> = </span><span class="constant">1</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_LOG_ESCAPES_STRF</span><span class="plain">)</span>
&lt;<span class="cwebmacro">Deal with escape sequences</span> <span class="cwebmacronumber">6.1</span>&gt;
<span class="reserved">else</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">stream</span><span class="plain">);</span>
<span class="reserved">break</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">case</span><span class="plain"> </span><span class="character">'"'</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_I6_ESCAPES_STRF</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">stream</span><span class="plain">);</span>
<span class="reserved">else</span><span class="plain"> </span><span class="functiontext">Streams::putc</span><span class="plain">(*</span><span class="identifier">p</span><span class="plain">, </span><span class="identifier">stream</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="character">'\n'</span><span class="plain">:</span>
<span class="functiontext">Streams::putc</span><span class="plain">(*</span><span class="identifier">p</span><span class="plain">, </span><span class="identifier">stream</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="functiontext">Streams::putc</span><span class="plain">(*</span><span class="identifier">p</span><span class="plain">, </span><span class="identifier">stream</span><span class="plain">); </span><span class="reserved">break</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="identifier">va_end</span><span class="plain">(</span><span class="identifier">ap</span><span class="plain">); </span><span class="comment"> macro to end variable argument processing</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Writers::printf is used in 2/str (<a href="2-str.html#SP3">&#167;3</a>).</p>
<p class="inwebparagraph"><a id="SP6_1"></a><b>&#167;6.1. </b><code class="display">
&lt;<span class="cwebmacrodefn">Deal with escape sequences</span> <span class="cwebmacronumber">6.1</span>&gt; =
</code></p>
<pre class="displaydefn">
<span class="reserved">char</span><span class="plain"> </span><span class="identifier">format_string</span><span class="plain">[8];</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">esc_number</span><span class="plain"> = </span><span class="character">' '</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="identifier">format_string</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">++] = *(</span><span class="identifier">p</span><span class="plain">++);</span>
<span class="reserved">while</span><span class="plain"> (*</span><span class="identifier">p</span><span class="plain">) {</span>
<span class="identifier">format_string</span><span class="plain">[</span><span class="identifier">i</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">islower</span><span class="plain">(*</span><span class="identifier">p</span><span class="plain">)) || (</span><span class="identifier">isupper</span><span class="plain">(*</span><span class="identifier">p</span><span class="plain">)) || ((</span><span class="identifier">set</span><span class="plain"> == </span><span class="constant">1</span><span class="plain">) &amp;&amp; (</span><span class="identifier">isdigit</span><span class="plain">(*</span><span class="identifier">p</span><span class="plain">))) ||</span>
<span class="plain">(*</span><span class="identifier">p</span><span class="plain"> == </span><span class="character">'%'</span><span class="plain">)) </span><span class="identifier">esc_number</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">p</span><span class="plain">++;</span>
<span class="reserved">if</span><span class="plain"> ((</span><span class="identifier">esc_number</span><span class="plain"> != </span><span class="character">' '</span><span class="plain">) || (</span><span class="identifier">i</span><span class="plain">==6)) </span><span class="reserved">break</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="identifier">format_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="identifier">p</span><span class="plain">--;</span>
<span class="reserved">if</span><span class="plain"> ((</span><span class="identifier">esc_number</span><span class="plain">&lt;0) || (</span><span class="identifier">esc_number</span><span class="plain"> &gt; </span><span class="constant">255</span><span class="plain">)) </span><span class="identifier">esc_number</span><span class="plain"> = </span><span class="constant">0</span><span class="plain">;</span>
<span class="reserved">switch</span><span class="plain"> (</span><span class="identifier">escapes_category</span><span class="plain">[</span><span class="identifier">set</span><span class="plain">][</span><span class="identifier">esc_number</span><span class="plain">]) {</span>
<span class="reserved">case</span><span class="plain"> </span><span class="identifier">POINTER_ECAT:</span><span class="plain"> {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">set</span><span class="plain"> == </span><span class="constant">0</span><span class="plain">) {</span>
<span class="identifier">writer_function</span><span class="plain"> </span><span class="identifier">f</span><span class="plain"> = (</span><span class="identifier">writer_function</span><span class="plain">) </span><span class="identifier">the_escapes</span><span class="plain">[0][</span><span class="identifier">esc_number</span><span class="plain">];</span>
<span class="reserved">void</span><span class="plain"> *</span><span class="identifier">q</span><span class="plain"> = </span><span class="identifier">va_arg</span><span class="plain">(</span><span class="identifier">ap</span><span class="plain">, </span><span class="reserved">void</span><span class="plain"> *);</span>
<span class="plain">(*</span><span class="identifier">f</span><span class="plain">)(</span><span class="identifier">stream</span><span class="plain">, </span><span class="identifier">format_string</span><span class="plain">+1, </span><span class="identifier">q</span><span class="plain">);</span>
<span class="plain">} </span><span class="reserved">else</span><span class="plain"> {</span>
<span class="identifier">log_function</span><span class="plain"> </span><span class="identifier">f</span><span class="plain"> = (</span><span class="identifier">log_function</span><span class="plain">) </span><span class="identifier">the_escapes</span><span class="plain">[1][</span><span class="identifier">esc_number</span><span class="plain">];</span>
<span class="reserved">void</span><span class="plain"> *</span><span class="identifier">q</span><span class="plain"> = </span><span class="identifier">va_arg</span><span class="plain">(</span><span class="identifier">ap</span><span class="plain">, </span><span class="reserved">void</span><span class="plain"> *);</span>
<span class="plain">(*</span><span class="identifier">f</span><span class="plain">)(</span><span class="identifier">stream</span><span class="plain">, </span><span class="identifier">q</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="reserved">break</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">case</span><span class="plain"> </span><span class="identifier">INTSIZED_ECAT:</span><span class="plain"> {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">set</span><span class="plain"> == </span><span class="constant">0</span><span class="plain">) {</span>
<span class="identifier">writer_function_I</span><span class="plain"> </span><span class="identifier">f</span><span class="plain"> = (</span><span class="identifier">writer_function_I</span><span class="plain">) </span><span class="identifier">the_escapes</span><span class="plain">[0][</span><span class="identifier">esc_number</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="identifier">va_arg</span><span class="plain">(</span><span class="identifier">ap</span><span class="plain">, </span><span class="reserved">int</span><span class="plain">);</span>
<span class="plain">(*</span><span class="identifier">f</span><span class="plain">)(</span><span class="identifier">stream</span><span class="plain">, </span><span class="identifier">format_string</span><span class="plain">+1, </span><span class="identifier">N</span><span class="plain">);</span>
<span class="plain">} </span><span class="reserved">else</span><span class="plain"> {</span>
<span class="identifier">log_function_I</span><span class="plain"> </span><span class="identifier">f</span><span class="plain"> = (</span><span class="identifier">log_function_I</span><span class="plain">) </span><span class="identifier">the_escapes</span><span class="plain">[1][</span><span class="identifier">esc_number</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="identifier">va_arg</span><span class="plain">(</span><span class="identifier">ap</span><span class="plain">, </span><span class="reserved">int</span><span class="plain">);</span>
<span class="plain">(*</span><span class="identifier">f</span><span class="plain">)(</span><span class="identifier">stream</span><span class="plain">, </span><span class="identifier">N</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="reserved">break</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">case</span><span class="plain"> </span><span class="identifier">WORDING_ECAT:</span><span class="plain"> {</span>
<span class="plain">#</span><span class="identifier">ifdef</span><span class="plain"> </span><span class="identifier">WORDS_MODULE</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">set</span><span class="plain"> == </span><span class="constant">0</span><span class="plain">) {</span>
<span class="identifier">writer_function_W</span><span class="plain"> </span><span class="identifier">f</span><span class="plain"> = (</span><span class="identifier">writer_function_W</span><span class="plain">) </span><span class="identifier">the_escapes</span><span class="plain">[0][</span><span class="identifier">esc_number</span><span class="plain">];</span>
<span class="identifier">wording</span><span class="plain"> </span><span class="identifier">W</span><span class="plain"> = </span><span class="identifier">va_arg</span><span class="plain">(</span><span class="identifier">ap</span><span class="plain">, </span><span class="identifier">wording</span><span class="plain">);</span>
<span class="plain">(*</span><span class="identifier">f</span><span class="plain">)(</span><span class="identifier">stream</span><span class="plain">, </span><span class="identifier">format_string</span><span class="plain">+1, </span><span class="identifier">W</span><span class="plain">);</span>
<span class="plain">} </span><span class="reserved">else</span><span class="plain"> {</span>
<span class="identifier">log_function_W</span><span class="plain"> </span><span class="identifier">f</span><span class="plain"> = (</span><span class="identifier">log_function_W</span><span class="plain">) </span><span class="identifier">the_escapes</span><span class="plain">[1][</span><span class="identifier">esc_number</span><span class="plain">];</span>
<span class="identifier">wording</span><span class="plain"> </span><span class="identifier">W</span><span class="plain"> = </span><span class="identifier">va_arg</span><span class="plain">(</span><span class="identifier">ap</span><span class="plain">, </span><span class="identifier">wording</span><span class="plain">);</span>
<span class="plain">(*</span><span class="identifier">f</span><span class="plain">)(</span><span class="identifier">stream</span><span class="plain">, </span><span class="identifier">W</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="plain">#</span><span class="identifier">endif</span>
<span class="reserved">break</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">case</span><span class="plain"> </span><span class="identifier">DIRECT_ECAT:</span><span class="plain"> </span>&lt;<span class="cwebmacro">Implement this using the original printf</span> <span class="cwebmacronumber">6.1.1</span>&gt;<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">VACANT_ECAT:</span>
<span class="identifier">WRITE_TO</span><span class="plain">(</span><span class="constant">STDERR</span><span class="plain">, </span><span class="string">"*** Bad WRITE escape: &lt;%s&gt; ***\n"</span><span class="plain">, </span><span class="identifier">format_string</span><span class="plain">);</span>
<span class="identifier">internal_error</span><span class="plain">(</span><span class="string">"Unknown string escape"</span><span class="plain">);</span>
<span class="reserved">break</span><span class="plain">;</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">This code is used in <a href="#SP6">&#167;6</a> (twice).</p>
<p class="inwebparagraph"><a id="SP6_1_1"></a><b>&#167;6.1.1. </b>Here the traditional C library helps us out with the difficult ones to get
right. We don't trouble to check that correct <code class="display"><span class="extract">printf</span></code> escapes have been used:
instead, we pass anything in the form of a percentage sign, followed by
up to four nonalphabetic modifying characters, followed by an alphabetic
category character for numerical printing, straight through to <code class="display"><span class="extract">sprintf</span></code>
or <code class="display"><span class="extract">fprintf</span></code>.
</p>
<p class="inwebparagraph">Thus an escape like <code class="display"><span class="extract">%04d</span></code> is handled by the standard C library, but not
<code class="display"><span class="extract">%s</span></code>, which we handle directly. That's for two reasons: first, we want to
be careful to prevent overruns of memory streams; second, we need to ensure
that the correct encoding is used when writing to disc. The numerical
escapes involve only characters whose representation is the same in all our
file encodings, but expanding <code class="display"><span class="extract">%s</span></code> does not.
</p>
<p class="macrodefinition"><code class="display">
&lt;<span class="cwebmacrodefn">Implement this using the original printf</span> <span class="cwebmacronumber">6.1.1</span>&gt; =
</code></p>
<pre class="displaydefn">
<span class="plain">#</span><span class="identifier">pragma</span><span class="plain"> </span><span class="identifier">clang</span><span class="plain"> </span><span class="identifier">diagnostic</span><span class="plain"> </span><span class="identifier">push</span>
<span class="plain">#</span><span class="identifier">pragma</span><span class="plain"> </span><span class="identifier">clang</span><span class="plain"> </span><span class="identifier">diagnostic</span><span class="plain"> </span><span class="identifier">ignored</span><span class="plain"> </span><span class="string">"-Wformat-nonliteral"</span>
<span class="reserved">switch</span><span class="plain"> (</span><span class="identifier">esc_number</span><span class="plain">) {</span>
<span class="reserved">case</span><span class="plain"> </span><span class="character">'c'</span><span class="plain">: </span><span class="reserved">case</span><span class="plain"> </span><span class="character">'d'</span><span class="plain">: </span><span class="reserved">case</span><span class="plain"> </span><span class="character">'i'</span><span class="plain">: </span><span class="reserved">case</span><span class="plain"> </span><span class="character">'x'</span><span class="plain">: { /* |</span><span class="reserved">char</span><span class="plain">| </span><span class="identifier">is</span><span class="plain"> </span><span class="identifier">promoted</span><span class="plain"> </span><span class="identifier">to</span><span class="plain"> |</span><span class="reserved">int</span><span class="plain">| </span><span class="identifier">in</span><span class="plain"> </span><span class="identifier">variable</span><span class="plain"> </span><span class="identifier">arguments</span><span class="plain"> */</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">ival</span><span class="plain"> = </span><span class="identifier">va_arg</span><span class="plain">(</span><span class="identifier">ap</span><span class="plain">, </span><span class="reserved">int</span><span class="plain">);</span>
<span class="reserved">char</span><span class="plain"> </span><span class="identifier">temp</span><span class="plain">[256];</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">snprintf</span><span class="plain">(</span><span class="identifier">temp</span><span class="plain">, </span><span class="constant">255</span><span class="plain">, </span><span class="identifier">format_string</span><span class="plain">, </span><span class="identifier">ival</span><span class="plain">) &gt;= </span><span class="constant">255</span><span class="plain">) </span><span class="identifier">strcpy</span><span class="plain">(</span><span class="identifier">temp</span><span class="plain">, </span><span class="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">j</span><span class="plain"> = </span><span class="constant">0</span><span class="plain">; </span><span class="identifier">temp</span><span class="plain">[</span><span class="identifier">j</span><span class="plain">]; </span><span class="identifier">j</span><span class="plain">++) </span><span class="functiontext">Streams::putc</span><span class="plain">(</span><span class="identifier">temp</span><span class="plain">[</span><span class="identifier">j</span><span class="plain">], </span><span class="identifier">stream</span><span class="plain">);</span>
<span class="reserved">break</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">case</span><span class="plain"> </span><span class="character">'g'</span><span class="plain">: {</span>
<span class="reserved">double</span><span class="plain"> </span><span class="identifier">dval</span><span class="plain"> = </span><span class="identifier">va_arg</span><span class="plain">(</span><span class="identifier">ap</span><span class="plain">, </span><span class="reserved">double</span><span class="plain">);</span>
<span class="reserved">char</span><span class="plain"> </span><span class="identifier">temp</span><span class="plain">[256];</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">snprintf</span><span class="plain">(</span><span class="identifier">temp</span><span class="plain">, </span><span class="constant">255</span><span class="plain">, </span><span class="identifier">format_string</span><span class="plain">, </span><span class="identifier">dval</span><span class="plain">) &gt;= </span><span class="constant">255</span><span class="plain">) </span><span class="identifier">strcpy</span><span class="plain">(</span><span class="identifier">temp</span><span class="plain">, </span><span class="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">j</span><span class="plain"> = </span><span class="constant">0</span><span class="plain">; </span><span class="identifier">temp</span><span class="plain">[</span><span class="identifier">j</span><span class="plain">]; </span><span class="identifier">j</span><span class="plain">++) </span><span class="functiontext">Streams::putc</span><span class="plain">(</span><span class="identifier">temp</span><span class="plain">[</span><span class="identifier">j</span><span class="plain">], </span><span class="identifier">stream</span><span class="plain">);</span>
<span class="reserved">break</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">case</span><span class="plain"> </span><span class="character">'s'</span><span class="plain">:</span>
<span class="reserved">for</span><span class="plain"> (</span><span class="reserved">char</span><span class="plain"> *</span><span class="identifier">sval</span><span class="plain"> = </span><span class="identifier">va_arg</span><span class="plain">(</span><span class="identifier">ap</span><span class="plain">, </span><span class="reserved">char</span><span class="plain"> *); *</span><span class="identifier">sval</span><span class="plain">; </span><span class="identifier">sval</span><span class="plain">++) </span><span class="functiontext">Streams::putc</span><span class="plain">(*</span><span class="identifier">sval</span><span class="plain">, </span><span class="identifier">stream</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="character">'w'</span><span class="plain">: {</span>
<span class="identifier">wchar_t</span><span class="plain"> *</span><span class="identifier">W</span><span class="plain"> = (</span><span class="identifier">wchar_t</span><span class="plain"> *) </span><span class="identifier">va_arg</span><span class="plain">(</span><span class="identifier">ap</span><span class="plain">, </span><span class="identifier">wchar_t</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"> = </span><span class="constant">0</span><span class="plain">; </span><span class="identifier">W</span><span class="plain">[</span><span class="identifier">j</span><span class="plain">]; </span><span class="identifier">j</span><span class="plain">++) </span><span class="functiontext">Streams::putc</span><span class="plain">(</span><span class="identifier">W</span><span class="plain">[</span><span class="identifier">j</span><span class="plain">], </span><span class="identifier">stream</span><span class="plain">);</span>
<span class="reserved">break</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">case</span><span class="plain"> </span><span class="character">'%'</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">stream</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="character">'$'</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">stream</span><span class="plain">); </span><span class="reserved">break</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="plain">#</span><span class="identifier">pragma</span><span class="plain"> </span><span class="identifier">clang</span><span class="plain"> </span><span class="identifier">diagnostic</span><span class="plain"> </span><span class="identifier">pop</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">This code is used in <a href="#SP6_1">&#167;6.1</a>.</p>
<hr class="tocbar">
<ul class="toc"><li><a href="2-str.html">Back to 'Streams'</a></li><li><a href="2-mth.html">Continue with 'Methods'</a></li></ul><hr class="tocbar">
<!--End of weave-->
</main>
</body>
</html>