inweb-bootstrap/docs/foundation-module/2-wal.html
2021-02-04 17:56:57 +00:00

357 lines
78 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>
<link href="../docs-assets/Breadcrumbs.css" rel="stylesheet" rev="stylesheet" type="text/css">
<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="../docs-assets/Contents.css" rel="stylesheet" rev="stylesheet" type="text/css">
<link href="../docs-assets/Progress.css" rel="stylesheet" rev="stylesheet" type="text/css">
<link href="../docs-assets/Navigation.css" rel="stylesheet" rev="stylesheet" type="text/css">
<link href="../docs-assets/Fonts.css" rel="stylesheet" rev="stylesheet" type="text/css">
<link href="../docs-assets/Base.css" rel="stylesheet" rev="stylesheet" type="text/css">
<script>
function togglePopup(material_id) {
var popup = document.getElementById(material_id);
popup.classList.toggle("show");
}
</script>
<link href="../docs-assets/Popups.css" rel="stylesheet" rev="stylesheet" type="text/css">
<link href="../docs-assets/Colours.css" rel="stylesheet" rev="stylesheet" type="text/css">
</head>
<body class="commentary-font">
<nav role="navigation">
<h1><a href="../index.html">
<img src="../docs-assets/Octagram.png" width=72 height=72">
</a></h1>
<ul><li><a href="../inweb/index.html">inweb</a></li>
</ul><h2>Foundation Module</h2><ul>
<li><a href="index.html"><span class="selectedlink">foundation</span></a></li>
<li><a href="../foundation-test/index.html">foundation-test</a></li>
</ul><h2>Example Webs</h2><ul>
<li><a href="../goldbach/index.html">goldbach</a></li>
<li><a href="../twinprimes/twinprimes.html">twinprimes</a></li>
<li><a href="../eastertide/index.html">eastertide</a></li>
</ul><h2>Repository</h2><ul>
<li><a href="https://github.com/ganelson/inweb"><img src="../docs-assets/github.png" height=18> github</a></li>
</ul><h2>Related Projects</h2><ul>
<li><a href="../../../inform/docs/index.html">inform</a></li>
<li><a href="../../../intest/docs/index.html">intest</a></li>
</ul>
</nav>
<main role="main">
<!--Weave of 'Writers and Loggers' generated by Inweb-->
<div class="breadcrumbs">
<ul class="crumbs"><li><a href="../index.html">Home</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></div>
<p class="purpose">Formatted text output to streams.</p>
<ul class="toc"><li><a href="2-wal.html#SP1">&#167;1. Registration</a></li><li><a href="2-wal.html#SP6">&#167;6. Writing</a></li><li><a href="2-wal.html#SP7">&#167;7. Abbreviation macros</a></li></ul><hr class="tocbar">
<p class="commentary firstcommentary"><a id="SP1" class="paragraph-anchor"></a><b>&#167;1. Registration. </b>The main function here is modelled on the "minimum <span class="extract"><span class="extract-syntax">printf</span></span>" function
used as an example in Kernighan and Ritchie, Chapter 7, but because it
prints to streams, it combines the traditional functions <span class="extract"><span class="extract-syntax">printf</span></span>, <span class="extract"><span class="extract-syntax">sprintf</span></span>
and <span class="extract"><span class="extract-syntax">fprintf</span></span> in one. It also contains a number of doohickeys to provide
for a wider and extensible range of string interpolations.
</p>
<p class="commentary">Traditionally, in the C library, everything in the formatting string is
literal except for <span class="extract"><span class="extract-syntax">%</span></span> escapes: thus <span class="extract"><span class="extract-syntax">%d</span></span> means "integer goes here", and
so on. We follow this but allow extra <span class="extract"><span class="extract-syntax">%</span></span> escapes unknown to K&amp;R, and we
also allow a further family of <span class="extract"><span class="extract-syntax">$</span></span> 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="commentary">Each escape, say <span class="extract"><span class="extract-syntax">%z</span></span>, must be "registered" before use, and will be
given one of the following categories:
</p>
<pre class="definitions code-font"><span class="definition-keyword">define</span> <span class="constant-syntax">VACANT_ECAT</span><span class="plain-syntax"> </span><span class="constant-syntax">0</span><span class="plain-syntax"> </span><span class="comment-syntax"> unregistered</span>
<span class="definition-keyword">define</span> <span class="constant-syntax">POINTER_ECAT</span><span class="plain-syntax"> </span><span class="constant-syntax">1</span><span class="plain-syntax"> </span><span class="comment-syntax"> data to be printed is a pointer to a structure</span>
<span class="definition-keyword">define</span> <span class="constant-syntax">INTSIZED_ECAT</span><span class="plain-syntax"> </span><span class="constant-syntax">2</span><span class="plain-syntax"> </span><span class="comment-syntax"> data to be printed is or fits into an integer</span>
<span class="definition-keyword">define</span> <span class="constant-syntax">WORDING_ECAT</span><span class="plain-syntax"> </span><span class="constant-syntax">3</span><span class="plain-syntax"> </span><span class="comment-syntax"> data to be printed is a </span><span class="extract"><span class="extract-syntax">wording</span></span><span class="comment-syntax"> structure from inform7</span>
<span class="definition-keyword">define</span> <span class="constant-syntax">DIRECT_ECAT</span><span class="plain-syntax"> </span><span class="constant-syntax">4</span><span class="plain-syntax"> </span><span class="comment-syntax"> data must be printed directly by the code below</span>
</pre>
<p class="commentary firstcommentary"><a id="SP2" class="paragraph-anchor"></a><b>&#167;2. </b>We'll start with <span class="extract"><span class="extract-syntax">%</span></span> escapes, which generalise the familiar <span class="extract"><span class="extract-syntax">printf</span></span>
escapes such as <span class="extract"><span class="extract-syntax">%d</span></span>. 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 <span class="extract"><span class="extract-syntax">wording</span></span>. 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="displayed-code all-displayed-code code-font">
<span class="reserved-syntax">int</span><span class="plain-syntax"> </span><span class="identifier-syntax">escapes_registered</span><span class="plain-syntax"> = </span><span class="constant-syntax">FALSE</span><span class="plain-syntax">;</span>
<span class="reserved-syntax">int</span><span class="plain-syntax"> </span><span class="identifier-syntax">escapes_category</span><span class="plain-syntax">[2][128]; </span><span class="comment-syntax"> one of the </span><span class="extract"><span class="extract-syntax">*_ECAT</span></span><span class="comment-syntax"> values above</span>
<span class="reserved-syntax">void</span><span class="plain-syntax"> *</span><span class="identifier-syntax">the_escapes</span><span class="plain-syntax">[2][128]; </span><span class="comment-syntax"> the function to call to implement this</span>
<span class="reserved-syntax">typedef</span><span class="plain-syntax"> </span><span class="reserved-syntax">void</span><span class="plain-syntax"> (*</span><span class="identifier-syntax">writer_function</span><span class="plain-syntax">)(</span><span class="reserved-syntax">text_stream</span><span class="plain-syntax"> *, </span><span class="reserved-syntax">char</span><span class="plain-syntax"> *, </span><span class="reserved-syntax">void</span><span class="plain-syntax"> *);</span>
<span class="reserved-syntax">typedef</span><span class="plain-syntax"> </span><span class="reserved-syntax">void</span><span class="plain-syntax"> (*</span><span class="identifier-syntax">writer_function_I</span><span class="plain-syntax">)(</span><span class="reserved-syntax">text_stream</span><span class="plain-syntax"> *, </span><span class="reserved-syntax">char</span><span class="plain-syntax"> *, </span><span class="reserved-syntax">int</span><span class="plain-syntax">);</span>
<span class="reserved-syntax">typedef</span><span class="plain-syntax"> </span><span class="reserved-syntax">void</span><span class="plain-syntax"> (*</span><span class="identifier-syntax">log_function</span><span class="plain-syntax">)(</span><span class="reserved-syntax">text_stream</span><span class="plain-syntax"> *, </span><span class="reserved-syntax">void</span><span class="plain-syntax"> *);</span>
<span class="reserved-syntax">typedef</span><span class="plain-syntax"> </span><span class="reserved-syntax">void</span><span class="plain-syntax"> (*</span><span class="identifier-syntax">log_function_I</span><span class="plain-syntax">)(</span><span class="reserved-syntax">text_stream</span><span class="plain-syntax"> *, </span><span class="reserved-syntax">int</span><span class="plain-syntax">);</span>
<span class="plain-syntax">#</span><span class="identifier-syntax">ifdef</span><span class="plain-syntax"> </span><span class="identifier-syntax">WORDS_MODULE</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">typedef</span><span class="plain-syntax"> </span><span class="reserved-syntax">void</span><span class="plain-syntax"> (*</span><span class="identifier-syntax">writer_function_W</span><span class="plain-syntax">)(</span><span class="reserved-syntax">text_stream</span><span class="plain-syntax"> *, </span><span class="reserved-syntax">char</span><span class="plain-syntax"> *, </span><span class="identifier-syntax">wording</span><span class="plain-syntax">);</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">typedef</span><span class="plain-syntax"> </span><span class="reserved-syntax">void</span><span class="plain-syntax"> (*</span><span class="identifier-syntax">log_function_W</span><span class="plain-syntax">)(</span><span class="reserved-syntax">text_stream</span><span class="plain-syntax"> *, </span><span class="identifier-syntax">wording</span><span class="plain-syntax">);</span>
<span class="plain-syntax">#</span><span class="identifier-syntax">endif</span>
</pre>
<p class="commentary firstcommentary"><a id="SP3" class="paragraph-anchor"></a><b>&#167;3. </b></p>
<pre class="displayed-code all-displayed-code code-font">
<span class="reserved-syntax">void</span><span class="plain-syntax"> </span><span class="function-syntax">Writers::log_escape_usage</span><span class="plain-syntax">(</span><span class="reserved-syntax">void</span><span class="plain-syntax">) {</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">for</span><span class="plain-syntax"> (</span><span class="reserved-syntax">int</span><span class="plain-syntax"> </span><span class="identifier-syntax">cat</span><span class="plain-syntax"> = </span><span class="constant-syntax">0</span><span class="plain-syntax">; </span><span class="identifier-syntax">cat</span><span class="plain-syntax"> &lt; </span><span class="constant-syntax">2</span><span class="plain-syntax">; </span><span class="identifier-syntax">cat</span><span class="plain-syntax">++) {</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">char</span><span class="plain-syntax"> *</span><span class="identifier-syntax">alphanum</span><span class="plain-syntax"> = </span><span class="string-syntax">"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">LOG</span><span class="plain-syntax">(</span><span class="string-syntax">"Vacant escapes: %s: "</span><span class="plain-syntax">, (</span><span class="identifier-syntax">cat</span><span class="plain-syntax"> == </span><span class="constant-syntax">0</span><span class="plain-syntax">)?</span><span class="string-syntax">"%"</span><span class="plain-syntax">:</span><span class="string-syntax">"$"</span><span class="plain-syntax">);</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">for</span><span class="plain-syntax"> (</span><span class="reserved-syntax">int</span><span class="plain-syntax"> </span><span class="identifier-syntax">i</span><span class="plain-syntax">=0; </span><span class="identifier-syntax">alphanum</span><span class="plain-syntax">[</span><span class="identifier-syntax">i</span><span class="plain-syntax">]; </span><span class="identifier-syntax">i</span><span class="plain-syntax">++)</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="identifier-syntax">escapes_category</span><span class="plain-syntax">[</span><span class="identifier-syntax">cat</span><span class="plain-syntax">][(</span><span class="reserved-syntax">int</span><span class="plain-syntax">) </span><span class="identifier-syntax">alphanum</span><span class="plain-syntax">[</span><span class="identifier-syntax">i</span><span class="plain-syntax">]] == </span><span class="constant-syntax">VACANT_ECAT</span><span class="plain-syntax">)</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">LOG</span><span class="plain-syntax">(</span><span class="string-syntax">"%c"</span><span class="plain-syntax">, </span><span class="identifier-syntax">alphanum</span><span class="plain-syntax">[</span><span class="identifier-syntax">i</span><span class="plain-syntax">]);</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">else</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">LOG</span><span class="plain-syntax">(</span><span class="string-syntax">"."</span><span class="plain-syntax">);</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">LOG</span><span class="plain-syntax">(</span><span class="string-syntax">"\n"</span><span class="plain-syntax">);</span>
<span class="plain-syntax"> }</span>
<span class="plain-syntax">}</span>
</pre>
<p class="commentary firstcommentary"><a id="SP4" class="paragraph-anchor"></a><b>&#167;4. </b>That gives us a number of front doors:
</p>
<pre class="displayed-code all-displayed-code code-font">
<span class="reserved-syntax">void</span><span class="plain-syntax"> </span><span class="function-syntax">Writers::register_writer</span><button class="popup" onclick="togglePopup('usagePopup1')"><span class="comment-syntax">?</span><span class="popuptext" id="usagePopup1">Usage of <span class="code-font"><span class="function-syntax">Writers::register_writer</span></span>:<br/>Foundation Module - <a href="1-fm.html#SP8_1">&#167;8.1</a></span></button><span class="plain-syntax">(</span><span class="reserved-syntax">int</span><span class="plain-syntax"> </span><span class="identifier-syntax">esc</span><span class="plain-syntax">, </span><span class="reserved-syntax">void</span><span class="plain-syntax"> (*</span><span class="identifier-syntax">f</span><span class="plain-syntax">)(</span><span class="reserved-syntax">text_stream</span><span class="plain-syntax"> *, </span><span class="reserved-syntax">char</span><span class="plain-syntax"> *, </span><span class="reserved-syntax">void</span><span class="plain-syntax"> *)) {</span>
<span class="plain-syntax"> </span><a href="2-wal.html#SP5" class="function-link"><span class="function-syntax">Writers::register_writer_p</span></a><span class="plain-syntax">(0, </span><span class="identifier-syntax">esc</span><span class="plain-syntax">, (</span><span class="reserved-syntax">void</span><span class="plain-syntax"> *) </span><span class="identifier-syntax">f</span><span class="plain-syntax">, </span><span class="constant-syntax">POINTER_ECAT</span><span class="plain-syntax">);</span>
<span class="plain-syntax">}</span>
<span class="reserved-syntax">void</span><span class="plain-syntax"> </span><span class="function-syntax">Writers::register_logger</span><button class="popup" onclick="togglePopup('usagePopup2')"><span class="comment-syntax">?</span><span class="popuptext" id="usagePopup2">Usage of <span class="code-font"><span class="function-syntax">Writers::register_logger</span></span>:<br/><a href="2-wal.html#SP7">&#167;7</a><br/>Foundation Module - <a href="1-fm.html#SP8_3">&#167;8.3</a></span></button><span class="plain-syntax">(</span><span class="reserved-syntax">int</span><span class="plain-syntax"> </span><span class="identifier-syntax">esc</span><span class="plain-syntax">, </span><span class="reserved-syntax">void</span><span class="plain-syntax"> (*</span><span class="identifier-syntax">f</span><span class="plain-syntax">)(</span><span class="reserved-syntax">text_stream</span><span class="plain-syntax"> *, </span><span class="reserved-syntax">void</span><span class="plain-syntax"> *)) {</span>
<span class="plain-syntax"> </span><a href="2-wal.html#SP5" class="function-link"><span class="function-syntax">Writers::register_writer_p</span></a><span class="plain-syntax">(1, </span><span class="identifier-syntax">esc</span><span class="plain-syntax">, (</span><span class="reserved-syntax">void</span><span class="plain-syntax"> *) </span><span class="identifier-syntax">f</span><span class="plain-syntax">, </span><span class="constant-syntax">POINTER_ECAT</span><span class="plain-syntax">);</span>
<span class="plain-syntax">}</span>
<span class="reserved-syntax">void</span><span class="plain-syntax"> </span><span class="function-syntax">Writers::register_writer_I</span><span class="plain-syntax">(</span><span class="reserved-syntax">int</span><span class="plain-syntax"> </span><span class="identifier-syntax">esc</span><span class="plain-syntax">, </span><span class="reserved-syntax">void</span><span class="plain-syntax"> (*</span><span class="identifier-syntax">f</span><span class="plain-syntax">)(</span><span class="reserved-syntax">text_stream</span><span class="plain-syntax"> *, </span><span class="reserved-syntax">char</span><span class="plain-syntax"> *, </span><span class="reserved-syntax">int</span><span class="plain-syntax">)) {</span>
<span class="plain-syntax"> </span><a href="2-wal.html#SP5" class="function-link"><span class="function-syntax">Writers::register_writer_p</span></a><span class="plain-syntax">(0, </span><span class="identifier-syntax">esc</span><span class="plain-syntax">, (</span><span class="reserved-syntax">void</span><span class="plain-syntax"> *) </span><span class="identifier-syntax">f</span><span class="plain-syntax">, </span><span class="constant-syntax">INTSIZED_ECAT</span><span class="plain-syntax">);</span>
<span class="plain-syntax">}</span>
<span class="reserved-syntax">void</span><span class="plain-syntax"> </span><span class="function-syntax">Writers::register_logger_I</span><button class="popup" onclick="togglePopup('usagePopup3')"><span class="comment-syntax">?</span><span class="popuptext" id="usagePopup3">Usage of <span class="code-font"><span class="function-syntax">Writers::register_logger_I</span></span>:<br/><a href="2-wal.html#SP7">&#167;7</a></span></button><span class="plain-syntax">(</span><span class="reserved-syntax">int</span><span class="plain-syntax"> </span><span class="identifier-syntax">esc</span><span class="plain-syntax">, </span><span class="reserved-syntax">void</span><span class="plain-syntax"> (*</span><span class="identifier-syntax">f</span><span class="plain-syntax">)(</span><span class="reserved-syntax">text_stream</span><span class="plain-syntax"> *, </span><span class="reserved-syntax">int</span><span class="plain-syntax">)) {</span>
<span class="plain-syntax"> </span><a href="2-wal.html#SP5" class="function-link"><span class="function-syntax">Writers::register_writer_p</span></a><span class="plain-syntax">(1, </span><span class="identifier-syntax">esc</span><span class="plain-syntax">, (</span><span class="reserved-syntax">void</span><span class="plain-syntax"> *) </span><span class="identifier-syntax">f</span><span class="plain-syntax">, </span><span class="constant-syntax">INTSIZED_ECAT</span><span class="plain-syntax">);</span>
<span class="plain-syntax">}</span>
<span class="plain-syntax">#</span><span class="identifier-syntax">ifdef</span><span class="plain-syntax"> </span><span class="identifier-syntax">WORDS_MODULE</span>
<span class="plain-syntax">#</span><span class="identifier-syntax">define</span><span class="plain-syntax"> </span><span class="identifier-syntax">Writers::register_writer_W</span><span class="plain-syntax">(</span><span class="identifier-syntax">esc</span><span class="plain-syntax">, </span><span class="identifier-syntax">f</span><span class="plain-syntax">) </span><a href="2-wal.html#SP5" class="function-link"><span class="function-syntax">Writers::register_writer_p</span></a><span class="plain-syntax">(0, </span><span class="identifier-syntax">esc</span><span class="plain-syntax">, (</span><span class="reserved-syntax">void</span><span class="plain-syntax"> *) </span><span class="identifier-syntax">f</span><span class="plain-syntax">, </span><span class="constant-syntax">WORDING_ECAT</span><span class="plain-syntax">);</span>
<span class="plain-syntax">#</span><span class="identifier-syntax">define</span><span class="plain-syntax"> </span><span class="identifier-syntax">Writers::register_logger_W</span><span class="plain-syntax">(</span><span class="identifier-syntax">esc</span><span class="plain-syntax">, </span><span class="identifier-syntax">f</span><span class="plain-syntax">) </span><a href="2-wal.html#SP5" class="function-link"><span class="function-syntax">Writers::register_writer_p</span></a><span class="plain-syntax">(1, </span><span class="identifier-syntax">esc</span><span class="plain-syntax">, (</span><span class="reserved-syntax">void</span><span class="plain-syntax"> *) </span><span class="identifier-syntax">f</span><span class="plain-syntax">, </span><span class="constant-syntax">WORDING_ECAT</span><span class="plain-syntax">);</span>
<span class="plain-syntax">#</span><span class="identifier-syntax">endif</span>
</pre>
<p class="commentary firstcommentary"><a id="SP5" class="paragraph-anchor"></a><b>&#167;5. </b>All leading to:
</p>
<pre class="displayed-code all-displayed-code code-font">
<span class="reserved-syntax">void</span><span class="plain-syntax"> </span><span class="function-syntax">Writers::register_writer_p</span><button class="popup" onclick="togglePopup('usagePopup4')"><span class="comment-syntax">?</span><span class="popuptext" id="usagePopup4">Usage of <span class="code-font"><span class="function-syntax">Writers::register_writer_p</span></span>:<br/><a href="2-wal.html#SP4">&#167;4</a></span></button><span class="plain-syntax">(</span><span class="reserved-syntax">int</span><span class="plain-syntax"> </span><span class="identifier-syntax">set</span><span class="plain-syntax">, </span><span class="reserved-syntax">int</span><span class="plain-syntax"> </span><span class="identifier-syntax">esc</span><span class="plain-syntax">, </span><span class="reserved-syntax">void</span><span class="plain-syntax"> *</span><span class="identifier-syntax">f</span><span class="plain-syntax">, </span><span class="reserved-syntax">int</span><span class="plain-syntax"> </span><span class="identifier-syntax">cat</span><span class="plain-syntax">) {</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="identifier-syntax">escapes_registered</span><span class="plain-syntax"> == </span><span class="constant-syntax">FALSE</span><span class="plain-syntax">) </span><span class="named-paragraph-container code-font"><a href="2-wal.html#SP5_1" class="named-paragraph-link"><span class="named-paragraph">Initialise the table of escapes</span><span class="named-paragraph-number">5.1</span></a></span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> ((</span><span class="identifier-syntax">esc</span><span class="plain-syntax"> &lt; </span><span class="constant-syntax">0</span><span class="plain-syntax">) || (</span><span class="identifier-syntax">esc</span><span class="plain-syntax"> &gt;= </span><span class="constant-syntax">128</span><span class="plain-syntax">) ||</span>
<span class="plain-syntax"> ((</span><a href="4-chr.html#SP1" class="function-link"><span class="function-syntax">Characters::isalpha</span></a><span class="plain-syntax">((</span><span class="identifier-syntax">wchar_t</span><span class="plain-syntax">) </span><span class="identifier-syntax">esc</span><span class="plain-syntax">) == </span><span class="constant-syntax">FALSE</span><span class="plain-syntax">) &amp;&amp;</span>
<span class="plain-syntax"> (</span><a href="4-chr.html#SP1" class="function-link"><span class="function-syntax">Characters::isdigit</span></a><span class="plain-syntax">((</span><span class="identifier-syntax">wchar_t</span><span class="plain-syntax">) </span><span class="identifier-syntax">esc</span><span class="plain-syntax">) == </span><span class="constant-syntax">FALSE</span><span class="plain-syntax">)))</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">internal_error</span><span class="plain-syntax">(</span><span class="string-syntax">"nonalphabetic escape"</span><span class="plain-syntax">);</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="identifier-syntax">escapes_category</span><span class="plain-syntax">[</span><span class="identifier-syntax">set</span><span class="plain-syntax">][</span><span class="identifier-syntax">esc</span><span class="plain-syntax">] != </span><span class="constant-syntax">VACANT_ECAT</span><span class="plain-syntax">) {</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">WRITE_TO</span><span class="plain-syntax">(</span><span class="constant-syntax">STDERR</span><span class="plain-syntax">, </span><span class="string-syntax">"Clashing escape is %s%c\n"</span><span class="plain-syntax">, (</span><span class="identifier-syntax">set</span><span class="plain-syntax"> == </span><span class="constant-syntax">0</span><span class="plain-syntax">)?</span><span class="string-syntax">"%"</span><span class="plain-syntax">:</span><span class="string-syntax">"$"</span><span class="plain-syntax">, </span><span class="identifier-syntax">esc</span><span class="plain-syntax">);</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">internal_error</span><span class="plain-syntax">(</span><span class="string-syntax">"clash of escapes"</span><span class="plain-syntax">);</span>
<span class="plain-syntax"> }</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">escapes_category</span><span class="plain-syntax">[</span><span class="identifier-syntax">set</span><span class="plain-syntax">][</span><span class="identifier-syntax">esc</span><span class="plain-syntax">] = </span><span class="identifier-syntax">cat</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">the_escapes</span><span class="plain-syntax">[</span><span class="identifier-syntax">set</span><span class="plain-syntax">][</span><span class="identifier-syntax">esc</span><span class="plain-syntax">] = </span><span class="identifier-syntax">f</span><span class="plain-syntax">;</span>
<span class="plain-syntax">}</span>
</pre>
<p class="commentary firstcommentary"><a id="SP5_1" class="paragraph-anchor"></a><b>&#167;5.1. </b>We're going to implement <span class="extract"><span class="extract-syntax">%d</span></span> and a few others directly, so those are marked
in the table as being unavailable for registration.
</p>
<p class="commentary">Note that we don't support <span class="extract"><span class="extract-syntax">%f</span></span> for floats; but we do add our very own <span class="extract"><span class="extract-syntax">%w</span></span>
for wide strings.
</p>
<p class="commentary"><span class="named-paragraph-container code-font"><span class="named-paragraph-defn">Initialise the table of escapes</span><span class="named-paragraph-number">5.1</span></span><span class="comment-syntax"> =</span>
</p>
<pre class="displayed-code all-displayed-code code-font">
<span class="plain-syntax"> </span><span class="identifier-syntax">escapes_registered</span><span class="plain-syntax"> = </span><span class="constant-syntax">TRUE</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">for</span><span class="plain-syntax"> (</span><span class="reserved-syntax">int</span><span class="plain-syntax"> </span><span class="identifier-syntax">e</span><span class="plain-syntax">=0; </span><span class="identifier-syntax">e</span><span class="plain-syntax">&lt;2; </span><span class="identifier-syntax">e</span><span class="plain-syntax">++)</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">for</span><span class="plain-syntax"> (</span><span class="reserved-syntax">int</span><span class="plain-syntax"> </span><span class="identifier-syntax">i</span><span class="plain-syntax">=0; </span><span class="identifier-syntax">i</span><span class="plain-syntax">&lt;128; </span><span class="identifier-syntax">i</span><span class="plain-syntax">++) {</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">the_escapes</span><span class="plain-syntax">[</span><span class="identifier-syntax">e</span><span class="plain-syntax">][</span><span class="identifier-syntax">i</span><span class="plain-syntax">] = </span><span class="identifier-syntax">NULL</span><span class="plain-syntax">; </span><span class="identifier-syntax">escapes_category</span><span class="plain-syntax">[</span><span class="identifier-syntax">e</span><span class="plain-syntax">][</span><span class="identifier-syntax">i</span><span class="plain-syntax">] = </span><span class="constant-syntax">VACANT_ECAT</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> }</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">escapes_category</span><span class="plain-syntax">[0][</span><span class="character-syntax">'c'</span><span class="plain-syntax">] = </span><span class="constant-syntax">DIRECT_ECAT</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">escapes_category</span><span class="plain-syntax">[0][</span><span class="character-syntax">'d'</span><span class="plain-syntax">] = </span><span class="constant-syntax">DIRECT_ECAT</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">escapes_category</span><span class="plain-syntax">[0][</span><span class="character-syntax">'g'</span><span class="plain-syntax">] = </span><span class="constant-syntax">DIRECT_ECAT</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">escapes_category</span><span class="plain-syntax">[0][</span><span class="character-syntax">'i'</span><span class="plain-syntax">] = </span><span class="constant-syntax">DIRECT_ECAT</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">escapes_category</span><span class="plain-syntax">[0][</span><span class="character-syntax">'s'</span><span class="plain-syntax">] = </span><span class="constant-syntax">DIRECT_ECAT</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">escapes_category</span><span class="plain-syntax">[0][</span><span class="character-syntax">'w'</span><span class="plain-syntax">] = </span><span class="constant-syntax">DIRECT_ECAT</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">escapes_category</span><span class="plain-syntax">[0][</span><span class="character-syntax">'x'</span><span class="plain-syntax">] = </span><span class="constant-syntax">DIRECT_ECAT</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">escapes_category</span><span class="plain-syntax">[0][</span><span class="character-syntax">'%'</span><span class="plain-syntax">] = </span><span class="constant-syntax">DIRECT_ECAT</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">escapes_category</span><span class="plain-syntax">[0][</span><span class="character-syntax">'$'</span><span class="plain-syntax">] = </span><span class="constant-syntax">DIRECT_ECAT</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">escapes_category</span><span class="plain-syntax">[1][</span><span class="character-syntax">'%'</span><span class="plain-syntax">] = </span><span class="constant-syntax">DIRECT_ECAT</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">escapes_category</span><span class="plain-syntax">[1][</span><span class="character-syntax">'$'</span><span class="plain-syntax">] = </span><span class="constant-syntax">DIRECT_ECAT</span><span class="plain-syntax">;</span>
</pre>
<ul class="endnotetexts"><li>This code is used in <a href="2-wal.html#SP5">&#167;5</a>.</li></ul>
<p class="commentary firstcommentary"><a id="SP6" class="paragraph-anchor"></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="displayed-code all-displayed-code code-font">
<span class="reserved-syntax">void</span><span class="plain-syntax"> </span><span class="function-syntax">Writers::printf</span><button class="popup" onclick="togglePopup('usagePopup5')"><span class="comment-syntax">?</span><span class="popuptext" id="usagePopup5">Usage of <span class="code-font"><span class="function-syntax">Writers::printf</span></span>:<br/>Streams - <a href="2-str.html#SP3">&#167;3</a></span></button><span class="plain-syntax">(</span><span class="reserved-syntax">text_stream</span><span class="plain-syntax"> *</span><span class="identifier-syntax">stream</span><span class="plain-syntax">, </span><span class="reserved-syntax">char</span><span class="plain-syntax"> *</span><span class="identifier-syntax">fmt</span><span class="plain-syntax">, ...) {</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">va_list</span><span class="plain-syntax"> </span><span class="identifier-syntax">ap</span><span class="plain-syntax">; </span><span class="comment-syntax"> the variable argument list signified by the dots</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">char</span><span class="plain-syntax"> *</span><span class="identifier-syntax">p</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="identifier-syntax">stream</span><span class="plain-syntax"> == </span><span class="identifier-syntax">NULL</span><span class="plain-syntax">) </span><span class="reserved-syntax">return</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">va_start</span><span class="plain-syntax">(</span><span class="identifier-syntax">ap</span><span class="plain-syntax">, </span><span class="identifier-syntax">fmt</span><span class="plain-syntax">); </span><span class="comment-syntax"> macro to begin variable argument processing</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">for</span><span class="plain-syntax"> (</span><span class="identifier-syntax">p</span><span class="plain-syntax"> = </span><span class="identifier-syntax">fmt</span><span class="plain-syntax">; *</span><span class="identifier-syntax">p</span><span class="plain-syntax">; </span><span class="identifier-syntax">p</span><span class="plain-syntax">++) {</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">switch</span><span class="plain-syntax"> (*</span><span class="identifier-syntax">p</span><span class="plain-syntax">) {</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">case</span><span class="plain-syntax"> </span><span class="character-syntax">'%'</span><span class="plain-syntax">: {</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">int</span><span class="plain-syntax"> </span><span class="identifier-syntax">set</span><span class="plain-syntax"> = </span><span class="constant-syntax">0</span><span class="plain-syntax">; </span><span class="named-paragraph-container code-font"><a href="2-wal.html#SP6_1" class="named-paragraph-link"><span class="named-paragraph">Deal with escape sequences</span><span class="named-paragraph-number">6.1</span></a></span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">break</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> }</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">case</span><span class="plain-syntax"> </span><span class="character-syntax">'$'</span><span class="plain-syntax">: {</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">int</span><span class="plain-syntax"> </span><span class="identifier-syntax">set</span><span class="plain-syntax"> = </span><span class="constant-syntax">1</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> ((</span><span class="identifier-syntax">stream</span><span class="plain-syntax">-&gt;</span><span class="element-syntax">stream_flags</span><span class="plain-syntax">) &amp; </span><span class="constant-syntax">USES_LOG_ESCAPES_STRF</span><span class="plain-syntax">)</span>
<span class="plain-syntax"> </span><span class="named-paragraph-container code-font"><a href="2-wal.html#SP6_1" class="named-paragraph-link"><span class="named-paragraph">Deal with escape sequences</span><span class="named-paragraph-number">6.1</span></a></span>
<span class="plain-syntax"> </span><span class="reserved-syntax">else</span><span class="plain-syntax"> </span><a href="2-str.html#SP35" class="function-link"><span class="function-syntax">Streams::putc</span></a><span class="plain-syntax">(</span><span class="character-syntax">'$'</span><span class="plain-syntax">, </span><span class="identifier-syntax">stream</span><span class="plain-syntax">);</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">break</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> }</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">case</span><span class="plain-syntax"> </span><span class="character-syntax">'"'</span><span class="plain-syntax">:</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="identifier-syntax">stream</span><span class="plain-syntax">-&gt;</span><span class="element-syntax">stream_flags</span><span class="plain-syntax"> &amp; </span><span class="constant-syntax">USES_I6_ESCAPES_STRF</span><span class="plain-syntax">)</span>
<span class="plain-syntax"> </span><a href="2-str.html#SP35" class="function-link"><span class="function-syntax">Streams::putc</span></a><span class="plain-syntax">(</span><span class="character-syntax">'~'</span><span class="plain-syntax">, </span><span class="identifier-syntax">stream</span><span class="plain-syntax">);</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">else</span><span class="plain-syntax"> </span><a href="2-str.html#SP35" class="function-link"><span class="function-syntax">Streams::putc</span></a><span class="plain-syntax">(*</span><span class="identifier-syntax">p</span><span class="plain-syntax">, </span><span class="identifier-syntax">stream</span><span class="plain-syntax">);</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">break</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">case</span><span class="plain-syntax"> </span><span class="character-syntax">'\n'</span><span class="plain-syntax">:</span>
<span class="plain-syntax"> </span><a href="2-str.html#SP35" class="function-link"><span class="function-syntax">Streams::putc</span></a><span class="plain-syntax">(*</span><span class="identifier-syntax">p</span><span class="plain-syntax">, </span><span class="identifier-syntax">stream</span><span class="plain-syntax">);</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">break</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">default:</span><span class="plain-syntax"> </span><a href="2-str.html#SP35" class="function-link"><span class="function-syntax">Streams::putc</span></a><span class="plain-syntax">(*</span><span class="identifier-syntax">p</span><span class="plain-syntax">, </span><span class="identifier-syntax">stream</span><span class="plain-syntax">); </span><span class="reserved-syntax">break</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> }</span>
<span class="plain-syntax"> }</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">va_end</span><span class="plain-syntax">(</span><span class="identifier-syntax">ap</span><span class="plain-syntax">); </span><span class="comment-syntax"> macro to end variable argument processing</span>
<span class="plain-syntax">}</span>
</pre>
<p class="commentary firstcommentary"><a id="SP6_1" class="paragraph-anchor"></a><b>&#167;6.1. </b><span class="named-paragraph-container code-font"><span class="named-paragraph-defn">Deal with escape sequences</span><span class="named-paragraph-number">6.1</span></span><span class="comment-syntax"> =</span>
</p>
<pre class="displayed-code all-displayed-code code-font">
<span class="plain-syntax"> </span><span class="reserved-syntax">char</span><span class="plain-syntax"> </span><span class="identifier-syntax">format_string</span><span class="plain-syntax">[8];</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">int</span><span class="plain-syntax"> </span><span class="identifier-syntax">esc_number</span><span class="plain-syntax"> = </span><span class="character-syntax">' '</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">int</span><span class="plain-syntax"> </span><span class="identifier-syntax">i</span><span class="plain-syntax"> = </span><span class="constant-syntax">0</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">format_string</span><span class="plain-syntax">[</span><span class="identifier-syntax">i</span><span class="plain-syntax">++] = *(</span><span class="identifier-syntax">p</span><span class="plain-syntax">++);</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">while</span><span class="plain-syntax"> (*</span><span class="identifier-syntax">p</span><span class="plain-syntax">) {</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">format_string</span><span class="plain-syntax">[</span><span class="identifier-syntax">i</span><span class="plain-syntax">++] = *</span><span class="identifier-syntax">p</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> ((</span><span class="identifier-syntax">islower</span><span class="plain-syntax">(*</span><span class="identifier-syntax">p</span><span class="plain-syntax">)) || (</span><span class="identifier-syntax">isupper</span><span class="plain-syntax">(*</span><span class="identifier-syntax">p</span><span class="plain-syntax">)) || ((</span><span class="identifier-syntax">set</span><span class="plain-syntax"> == </span><span class="constant-syntax">1</span><span class="plain-syntax">) &amp;&amp; (</span><span class="identifier-syntax">isdigit</span><span class="plain-syntax">(*</span><span class="identifier-syntax">p</span><span class="plain-syntax">))) ||</span>
<span class="plain-syntax"> (*</span><span class="identifier-syntax">p</span><span class="plain-syntax"> == </span><span class="character-syntax">'%'</span><span class="plain-syntax">)) </span><span class="identifier-syntax">esc_number</span><span class="plain-syntax"> = (</span><span class="reserved-syntax">int</span><span class="plain-syntax">) *</span><span class="identifier-syntax">p</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">p</span><span class="plain-syntax">++;</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> ((</span><span class="identifier-syntax">esc_number</span><span class="plain-syntax"> != </span><span class="character-syntax">' '</span><span class="plain-syntax">) || (</span><span class="identifier-syntax">i</span><span class="plain-syntax">==6)) </span><span class="reserved-syntax">break</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> }</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">format_string</span><span class="plain-syntax">[</span><span class="identifier-syntax">i</span><span class="plain-syntax">] = </span><span class="constant-syntax">0</span><span class="plain-syntax">; </span><span class="identifier-syntax">p</span><span class="plain-syntax">--;</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> ((</span><span class="identifier-syntax">esc_number</span><span class="plain-syntax">&lt;0) || (</span><span class="identifier-syntax">esc_number</span><span class="plain-syntax"> &gt; </span><span class="constant-syntax">255</span><span class="plain-syntax">)) </span><span class="identifier-syntax">esc_number</span><span class="plain-syntax"> = </span><span class="constant-syntax">0</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">switch</span><span class="plain-syntax"> (</span><span class="identifier-syntax">escapes_category</span><span class="plain-syntax">[</span><span class="identifier-syntax">set</span><span class="plain-syntax">][</span><span class="identifier-syntax">esc_number</span><span class="plain-syntax">]) {</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">case</span><span class="plain-syntax"> </span><span class="identifier-syntax">POINTER_ECAT:</span><span class="plain-syntax"> {</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="identifier-syntax">set</span><span class="plain-syntax"> == </span><span class="constant-syntax">0</span><span class="plain-syntax">) {</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">writer_function</span><span class="plain-syntax"> </span><span class="identifier-syntax">f</span><span class="plain-syntax"> = (</span><span class="identifier-syntax">writer_function</span><span class="plain-syntax">) </span><span class="identifier-syntax">the_escapes</span><span class="plain-syntax">[0][</span><span class="identifier-syntax">esc_number</span><span class="plain-syntax">];</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">void</span><span class="plain-syntax"> *</span><span class="identifier-syntax">q</span><span class="plain-syntax"> = </span><span class="identifier-syntax">va_arg</span><span class="plain-syntax">(</span><span class="identifier-syntax">ap</span><span class="plain-syntax">, </span><span class="reserved-syntax">void</span><span class="plain-syntax"> *);</span>
<span class="plain-syntax"> (*</span><span class="identifier-syntax">f</span><span class="plain-syntax">)(</span><span class="identifier-syntax">stream</span><span class="plain-syntax">, </span><span class="identifier-syntax">format_string</span><span class="plain-syntax">+1, </span><span class="identifier-syntax">q</span><span class="plain-syntax">);</span>
<span class="plain-syntax"> } </span><span class="reserved-syntax">else</span><span class="plain-syntax"> {</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">log_function</span><span class="plain-syntax"> </span><span class="identifier-syntax">f</span><span class="plain-syntax"> = (</span><span class="identifier-syntax">log_function</span><span class="plain-syntax">) </span><span class="identifier-syntax">the_escapes</span><span class="plain-syntax">[1][</span><span class="identifier-syntax">esc_number</span><span class="plain-syntax">];</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">void</span><span class="plain-syntax"> *</span><span class="identifier-syntax">q</span><span class="plain-syntax"> = </span><span class="identifier-syntax">va_arg</span><span class="plain-syntax">(</span><span class="identifier-syntax">ap</span><span class="plain-syntax">, </span><span class="reserved-syntax">void</span><span class="plain-syntax"> *);</span>
<span class="plain-syntax"> (*</span><span class="identifier-syntax">f</span><span class="plain-syntax">)(</span><span class="identifier-syntax">stream</span><span class="plain-syntax">, </span><span class="identifier-syntax">q</span><span class="plain-syntax">);</span>
<span class="plain-syntax"> }</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">break</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> }</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">case</span><span class="plain-syntax"> </span><span class="identifier-syntax">INTSIZED_ECAT:</span><span class="plain-syntax"> {</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="identifier-syntax">set</span><span class="plain-syntax"> == </span><span class="constant-syntax">0</span><span class="plain-syntax">) {</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">writer_function_I</span><span class="plain-syntax"> </span><span class="identifier-syntax">f</span><span class="plain-syntax"> = (</span><span class="identifier-syntax">writer_function_I</span><span class="plain-syntax">) </span><span class="identifier-syntax">the_escapes</span><span class="plain-syntax">[0][</span><span class="identifier-syntax">esc_number</span><span class="plain-syntax">];</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">int</span><span class="plain-syntax"> </span><span class="identifier-syntax">N</span><span class="plain-syntax"> = </span><span class="identifier-syntax">va_arg</span><span class="plain-syntax">(</span><span class="identifier-syntax">ap</span><span class="plain-syntax">, </span><span class="reserved-syntax">int</span><span class="plain-syntax">);</span>
<span class="plain-syntax"> (*</span><span class="identifier-syntax">f</span><span class="plain-syntax">)(</span><span class="identifier-syntax">stream</span><span class="plain-syntax">, </span><span class="identifier-syntax">format_string</span><span class="plain-syntax">+1, </span><span class="identifier-syntax">N</span><span class="plain-syntax">);</span>
<span class="plain-syntax"> } </span><span class="reserved-syntax">else</span><span class="plain-syntax"> {</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">log_function_I</span><span class="plain-syntax"> </span><span class="identifier-syntax">f</span><span class="plain-syntax"> = (</span><span class="identifier-syntax">log_function_I</span><span class="plain-syntax">) </span><span class="identifier-syntax">the_escapes</span><span class="plain-syntax">[1][</span><span class="identifier-syntax">esc_number</span><span class="plain-syntax">];</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">int</span><span class="plain-syntax"> </span><span class="identifier-syntax">N</span><span class="plain-syntax"> = </span><span class="identifier-syntax">va_arg</span><span class="plain-syntax">(</span><span class="identifier-syntax">ap</span><span class="plain-syntax">, </span><span class="reserved-syntax">int</span><span class="plain-syntax">);</span>
<span class="plain-syntax"> (*</span><span class="identifier-syntax">f</span><span class="plain-syntax">)(</span><span class="identifier-syntax">stream</span><span class="plain-syntax">, </span><span class="identifier-syntax">N</span><span class="plain-syntax">);</span>
<span class="plain-syntax"> }</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">break</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> }</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">case</span><span class="plain-syntax"> </span><span class="identifier-syntax">WORDING_ECAT:</span><span class="plain-syntax"> {</span>
<span class="plain-syntax"> #</span><span class="identifier-syntax">ifdef</span><span class="plain-syntax"> </span><span class="identifier-syntax">WORDS_MODULE</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="identifier-syntax">set</span><span class="plain-syntax"> == </span><span class="constant-syntax">0</span><span class="plain-syntax">) {</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">writer_function_W</span><span class="plain-syntax"> </span><span class="identifier-syntax">f</span><span class="plain-syntax"> = (</span><span class="identifier-syntax">writer_function_W</span><span class="plain-syntax">) </span><span class="identifier-syntax">the_escapes</span><span class="plain-syntax">[0][</span><span class="identifier-syntax">esc_number</span><span class="plain-syntax">];</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">wording</span><span class="plain-syntax"> </span><span class="identifier-syntax">W</span><span class="plain-syntax"> = </span><span class="identifier-syntax">va_arg</span><span class="plain-syntax">(</span><span class="identifier-syntax">ap</span><span class="plain-syntax">, </span><span class="identifier-syntax">wording</span><span class="plain-syntax">);</span>
<span class="plain-syntax"> (*</span><span class="identifier-syntax">f</span><span class="plain-syntax">)(</span><span class="identifier-syntax">stream</span><span class="plain-syntax">, </span><span class="identifier-syntax">format_string</span><span class="plain-syntax">+1, </span><span class="identifier-syntax">W</span><span class="plain-syntax">);</span>
<span class="plain-syntax"> } </span><span class="reserved-syntax">else</span><span class="plain-syntax"> {</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">log_function_W</span><span class="plain-syntax"> </span><span class="identifier-syntax">f</span><span class="plain-syntax"> = (</span><span class="identifier-syntax">log_function_W</span><span class="plain-syntax">) </span><span class="identifier-syntax">the_escapes</span><span class="plain-syntax">[1][</span><span class="identifier-syntax">esc_number</span><span class="plain-syntax">];</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">wording</span><span class="plain-syntax"> </span><span class="identifier-syntax">W</span><span class="plain-syntax"> = </span><span class="identifier-syntax">va_arg</span><span class="plain-syntax">(</span><span class="identifier-syntax">ap</span><span class="plain-syntax">, </span><span class="identifier-syntax">wording</span><span class="plain-syntax">);</span>
<span class="plain-syntax"> (*</span><span class="identifier-syntax">f</span><span class="plain-syntax">)(</span><span class="identifier-syntax">stream</span><span class="plain-syntax">, </span><span class="identifier-syntax">W</span><span class="plain-syntax">);</span>
<span class="plain-syntax"> }</span>
<span class="plain-syntax"> #</span><span class="identifier-syntax">endif</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">break</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> }</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">case</span><span class="plain-syntax"> </span><span class="identifier-syntax">DIRECT_ECAT:</span><span class="plain-syntax"> </span><span class="named-paragraph-container code-font"><a href="2-wal.html#SP6_1_1" class="named-paragraph-link"><span class="named-paragraph">Implement this using the original printf</span><span class="named-paragraph-number">6.1.1</span></a></span><span class="plain-syntax">; </span><span class="reserved-syntax">break</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">case</span><span class="plain-syntax"> </span><span class="identifier-syntax">VACANT_ECAT:</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">WRITE_TO</span><span class="plain-syntax">(</span><span class="constant-syntax">STDERR</span><span class="plain-syntax">, </span><span class="string-syntax">"*** Bad WRITE escape: &lt;%s&gt; ***\n"</span><span class="plain-syntax">, </span><span class="identifier-syntax">format_string</span><span class="plain-syntax">);</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">internal_error</span><span class="plain-syntax">(</span><span class="string-syntax">"Unknown string escape"</span><span class="plain-syntax">);</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">break</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> }</span>
</pre>
<ul class="endnotetexts"><li>This code is used in <a href="2-wal.html#SP6">&#167;6</a> (twice).</li></ul>
<p class="commentary firstcommentary"><a id="SP6_1_1" class="paragraph-anchor"></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 <span class="extract"><span class="extract-syntax">printf</span></span> 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 <span class="extract"><span class="extract-syntax">sprintf</span></span>
or <span class="extract"><span class="extract-syntax">fprintf</span></span>.
</p>
<p class="commentary">Thus an escape like <span class="extract"><span class="extract-syntax">%04d</span></span> is handled by the standard C library, but not
<span class="extract"><span class="extract-syntax">%s</span></span>, 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 <span class="extract"><span class="extract-syntax">%s</span></span> does not.
</p>
<p class="commentary"><span class="named-paragraph-container code-font"><span class="named-paragraph-defn">Implement this using the original printf</span><span class="named-paragraph-number">6.1.1</span></span><span class="comment-syntax"> =</span>
</p>
<pre class="displayed-code all-displayed-code code-font">
<span class="plain-syntax"> #</span><span class="identifier-syntax">pragma</span><span class="plain-syntax"> </span><span class="identifier-syntax">clang</span><span class="plain-syntax"> </span><span class="identifier-syntax">diagnostic</span><span class="plain-syntax"> </span><span class="identifier-syntax">push</span>
<span class="plain-syntax"> #</span><span class="identifier-syntax">pragma</span><span class="plain-syntax"> </span><span class="identifier-syntax">clang</span><span class="plain-syntax"> </span><span class="identifier-syntax">diagnostic</span><span class="plain-syntax"> </span><span class="identifier-syntax">ignored</span><span class="plain-syntax"> </span><span class="string-syntax">"-Wformat-nonliteral"</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">switch</span><span class="plain-syntax"> (</span><span class="identifier-syntax">esc_number</span><span class="plain-syntax">) {</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">case</span><span class="plain-syntax"> </span><span class="character-syntax">'c'</span><span class="plain-syntax">: </span><span class="reserved-syntax">case</span><span class="plain-syntax"> </span><span class="character-syntax">'d'</span><span class="plain-syntax">: </span><span class="reserved-syntax">case</span><span class="plain-syntax"> </span><span class="character-syntax">'i'</span><span class="plain-syntax">: </span><span class="reserved-syntax">case</span><span class="plain-syntax"> </span><span class="character-syntax">'x'</span><span class="plain-syntax">: { </span><span class="comment-syntax"> </span><span class="extract"><span class="extract-syntax">char</span></span><span class="comment-syntax"> is promoted to </span><span class="extract"><span class="extract-syntax">int</span></span><span class="comment-syntax"> in variable arguments</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">int</span><span class="plain-syntax"> </span><span class="identifier-syntax">ival</span><span class="plain-syntax"> = </span><span class="identifier-syntax">va_arg</span><span class="plain-syntax">(</span><span class="identifier-syntax">ap</span><span class="plain-syntax">, </span><span class="reserved-syntax">int</span><span class="plain-syntax">);</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">char</span><span class="plain-syntax"> </span><span class="identifier-syntax">temp</span><span class="plain-syntax">[256];</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="constant-syntax">PLATFORM_SNPRINTF</span><span class="plain-syntax">(</span><span class="identifier-syntax">temp</span><span class="plain-syntax">, </span><span class="constant-syntax">255</span><span class="plain-syntax">, </span><span class="identifier-syntax">format_string</span><span class="plain-syntax">, </span><span class="identifier-syntax">ival</span><span class="plain-syntax">) &gt;= </span><span class="constant-syntax">255</span><span class="plain-syntax">) </span><span class="identifier-syntax">strcpy</span><span class="plain-syntax">(</span><span class="identifier-syntax">temp</span><span class="plain-syntax">, </span><span class="string-syntax">"?"</span><span class="plain-syntax">);</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">for</span><span class="plain-syntax"> (</span><span class="reserved-syntax">int</span><span class="plain-syntax"> </span><span class="identifier-syntax">j</span><span class="plain-syntax"> = </span><span class="constant-syntax">0</span><span class="plain-syntax">; </span><span class="identifier-syntax">temp</span><span class="plain-syntax">[</span><span class="identifier-syntax">j</span><span class="plain-syntax">]; </span><span class="identifier-syntax">j</span><span class="plain-syntax">++) </span><a href="2-str.html#SP35" class="function-link"><span class="function-syntax">Streams::putc</span></a><span class="plain-syntax">(</span><span class="identifier-syntax">temp</span><span class="plain-syntax">[</span><span class="identifier-syntax">j</span><span class="plain-syntax">], </span><span class="identifier-syntax">stream</span><span class="plain-syntax">);</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">break</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> }</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">case</span><span class="plain-syntax"> </span><span class="character-syntax">'g'</span><span class="plain-syntax">: {</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">double</span><span class="plain-syntax"> </span><span class="identifier-syntax">dval</span><span class="plain-syntax"> = </span><span class="identifier-syntax">va_arg</span><span class="plain-syntax">(</span><span class="identifier-syntax">ap</span><span class="plain-syntax">, </span><span class="reserved-syntax">double</span><span class="plain-syntax">);</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">char</span><span class="plain-syntax"> </span><span class="identifier-syntax">temp</span><span class="plain-syntax">[256];</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="constant-syntax">PLATFORM_SNPRINTF</span><span class="plain-syntax">(</span><span class="identifier-syntax">temp</span><span class="plain-syntax">, </span><span class="constant-syntax">255</span><span class="plain-syntax">, </span><span class="identifier-syntax">format_string</span><span class="plain-syntax">, </span><span class="identifier-syntax">dval</span><span class="plain-syntax">) &gt;= </span><span class="constant-syntax">255</span><span class="plain-syntax">) </span><span class="identifier-syntax">strcpy</span><span class="plain-syntax">(</span><span class="identifier-syntax">temp</span><span class="plain-syntax">, </span><span class="string-syntax">"?"</span><span class="plain-syntax">);</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">for</span><span class="plain-syntax"> (</span><span class="reserved-syntax">int</span><span class="plain-syntax"> </span><span class="identifier-syntax">j</span><span class="plain-syntax"> = </span><span class="constant-syntax">0</span><span class="plain-syntax">; </span><span class="identifier-syntax">temp</span><span class="plain-syntax">[</span><span class="identifier-syntax">j</span><span class="plain-syntax">]; </span><span class="identifier-syntax">j</span><span class="plain-syntax">++) </span><a href="2-str.html#SP35" class="function-link"><span class="function-syntax">Streams::putc</span></a><span class="plain-syntax">(</span><span class="identifier-syntax">temp</span><span class="plain-syntax">[</span><span class="identifier-syntax">j</span><span class="plain-syntax">], </span><span class="identifier-syntax">stream</span><span class="plain-syntax">);</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">break</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> }</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">case</span><span class="plain-syntax"> </span><span class="character-syntax">'s'</span><span class="plain-syntax">:</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">for</span><span class="plain-syntax"> (</span><span class="reserved-syntax">char</span><span class="plain-syntax"> *</span><span class="identifier-syntax">sval</span><span class="plain-syntax"> = </span><span class="identifier-syntax">va_arg</span><span class="plain-syntax">(</span><span class="identifier-syntax">ap</span><span class="plain-syntax">, </span><span class="reserved-syntax">char</span><span class="plain-syntax"> *); *</span><span class="identifier-syntax">sval</span><span class="plain-syntax">; </span><span class="identifier-syntax">sval</span><span class="plain-syntax">++) </span><a href="2-str.html#SP35" class="function-link"><span class="function-syntax">Streams::putc</span></a><span class="plain-syntax">(*</span><span class="identifier-syntax">sval</span><span class="plain-syntax">, </span><span class="identifier-syntax">stream</span><span class="plain-syntax">);</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">break</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">case</span><span class="plain-syntax"> </span><span class="character-syntax">'w'</span><span class="plain-syntax">: {</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">wchar_t</span><span class="plain-syntax"> *</span><span class="identifier-syntax">W</span><span class="plain-syntax"> = (</span><span class="identifier-syntax">wchar_t</span><span class="plain-syntax"> *) </span><span class="identifier-syntax">va_arg</span><span class="plain-syntax">(</span><span class="identifier-syntax">ap</span><span class="plain-syntax">, </span><span class="identifier-syntax">wchar_t</span><span class="plain-syntax"> *);</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">for</span><span class="plain-syntax"> (</span><span class="reserved-syntax">int</span><span class="plain-syntax"> </span><span class="identifier-syntax">j</span><span class="plain-syntax"> = </span><span class="constant-syntax">0</span><span class="plain-syntax">; </span><span class="identifier-syntax">W</span><span class="plain-syntax">[</span><span class="identifier-syntax">j</span><span class="plain-syntax">]; </span><span class="identifier-syntax">j</span><span class="plain-syntax">++) </span><a href="2-str.html#SP35" class="function-link"><span class="function-syntax">Streams::putc</span></a><span class="plain-syntax">(</span><span class="identifier-syntax">W</span><span class="plain-syntax">[</span><span class="identifier-syntax">j</span><span class="plain-syntax">], </span><span class="identifier-syntax">stream</span><span class="plain-syntax">);</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">break</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> }</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">case</span><span class="plain-syntax"> </span><span class="character-syntax">'%'</span><span class="plain-syntax">: </span><a href="2-str.html#SP35" class="function-link"><span class="function-syntax">Streams::putc</span></a><span class="plain-syntax">(</span><span class="character-syntax">'%'</span><span class="plain-syntax">, </span><span class="identifier-syntax">stream</span><span class="plain-syntax">); </span><span class="reserved-syntax">break</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">case</span><span class="plain-syntax"> </span><span class="character-syntax">'$'</span><span class="plain-syntax">: </span><a href="2-str.html#SP35" class="function-link"><span class="function-syntax">Streams::putc</span></a><span class="plain-syntax">(</span><span class="character-syntax">'$'</span><span class="plain-syntax">, </span><span class="identifier-syntax">stream</span><span class="plain-syntax">); </span><span class="reserved-syntax">break</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> }</span>
<span class="plain-syntax"> #</span><span class="identifier-syntax">pragma</span><span class="plain-syntax"> </span><span class="identifier-syntax">clang</span><span class="plain-syntax"> </span><span class="identifier-syntax">diagnostic</span><span class="plain-syntax"> </span><span class="identifier-syntax">pop</span>
</pre>
<ul class="endnotetexts"><li>This code is used in <a href="2-wal.html#SP6_1">&#167;6.1</a>.</li></ul>
<p class="commentary firstcommentary"><a id="SP7" class="paragraph-anchor"></a><b>&#167;7. Abbreviation macros. </b>The following proved convenient for Inform, at any rate.
</p>
<pre class="definitions code-font"><span class="definition-keyword">define</span> <span class="identifier-syntax">REGISTER_WRITER</span><span class="plain-syntax">(</span><span class="identifier-syntax">c</span><span class="plain-syntax">, </span><span class="identifier-syntax">f</span><span class="plain-syntax">) </span><a href="2-wal.html#SP4" class="function-link"><span class="function-syntax">Writers::register_logger</span></a><span class="plain-syntax">(</span><span class="identifier-syntax">c</span><span class="plain-syntax">, &amp;</span><span class="identifier-syntax">f</span><span class="plain-syntax">##</span><span class="identifier-syntax">_writer</span><span class="plain-syntax">);</span>
<span class="definition-keyword">define</span> <span class="identifier-syntax">COMPILE_WRITER</span><span class="plain-syntax">(</span><span class="identifier-syntax">t</span><span class="plain-syntax">, </span><span class="identifier-syntax">f</span><span class="plain-syntax">)</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">void</span><span class="plain-syntax"> </span><span class="identifier-syntax">f</span><span class="plain-syntax">##</span><span class="identifier-syntax">_writer</span><span class="plain-syntax">(</span><span class="reserved-syntax">text_stream</span><span class="plain-syntax"> *</span><span class="identifier-syntax">format</span><span class="plain-syntax">, </span><span class="reserved-syntax">void</span><span class="plain-syntax"> *</span><span class="identifier-syntax">obj</span><span class="plain-syntax">) { </span><span class="reserved-syntax">text_stream</span><span class="plain-syntax"> *</span><span class="identifier-syntax">SDL</span><span class="plain-syntax"> = </span><span class="identifier-syntax">DL</span><span class="plain-syntax">; </span><span class="identifier-syntax">DL</span><span class="plain-syntax"> = </span><span class="identifier-syntax">format</span><span class="plain-syntax">; </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="identifier-syntax">DL</span><span class="plain-syntax">) </span><span class="identifier-syntax">f</span><span class="plain-syntax">((</span><span class="identifier-syntax">t</span><span class="plain-syntax">) </span><span class="identifier-syntax">obj</span><span class="plain-syntax">); </span><span class="identifier-syntax">DL</span><span class="plain-syntax"> = </span><span class="identifier-syntax">SDL</span><span class="plain-syntax">; }</span>
<span class="definition-keyword">define</span> <span class="identifier-syntax">REGISTER_WRITER_I</span><span class="plain-syntax">(</span><span class="identifier-syntax">c</span><span class="plain-syntax">, </span><span class="identifier-syntax">f</span><span class="plain-syntax">) </span><a href="2-wal.html#SP4" class="function-link"><span class="function-syntax">Writers::register_logger_I</span></a><span class="plain-syntax">(</span><span class="identifier-syntax">c</span><span class="plain-syntax">, &amp;</span><span class="identifier-syntax">f</span><span class="plain-syntax">##</span><span class="identifier-syntax">_writer</span><span class="plain-syntax">);</span>
<span class="definition-keyword">define</span> <span class="identifier-syntax">COMPILE_WRITER_I</span><span class="plain-syntax">(</span><span class="identifier-syntax">t</span><span class="plain-syntax">, </span><span class="identifier-syntax">f</span><span class="plain-syntax">)</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">void</span><span class="plain-syntax"> </span><span class="identifier-syntax">f</span><span class="plain-syntax">##</span><span class="identifier-syntax">_writer</span><span class="plain-syntax">(</span><span class="reserved-syntax">text_stream</span><span class="plain-syntax"> *</span><span class="identifier-syntax">format</span><span class="plain-syntax">, </span><span class="reserved-syntax">int</span><span class="plain-syntax"> </span><span class="identifier-syntax">I</span><span class="plain-syntax">) { </span><span class="reserved-syntax">text_stream</span><span class="plain-syntax"> *</span><span class="identifier-syntax">SDL</span><span class="plain-syntax"> = </span><span class="identifier-syntax">DL</span><span class="plain-syntax">; </span><span class="identifier-syntax">DL</span><span class="plain-syntax"> = </span><span class="identifier-syntax">format</span><span class="plain-syntax">; </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="identifier-syntax">DL</span><span class="plain-syntax">) </span><span class="identifier-syntax">f</span><span class="plain-syntax">((</span><span class="identifier-syntax">t</span><span class="plain-syntax">) </span><span class="identifier-syntax">I</span><span class="plain-syntax">); </span><span class="identifier-syntax">DL</span><span class="plain-syntax"> = </span><span class="identifier-syntax">SDL</span><span class="plain-syntax">; }</span>
</pre>
<nav role="progress"><div class="progresscontainer">
<ul class="progressbar"><li class="progressprev"><a href="2-str.html">&#10094;</a></li><li class="progresschapter"><a href="P-abgtf.html">P</a></li><li class="progresschapter"><a href="1-fm.html">1</a></li><li class="progresscurrentchapter">2</li><li class="progresssection"><a href="2-dl.html">dl</a></li><li class="progresssection"><a href="2-mmr.html">mmr</a></li><li class="progresssection"><a href="2-fc.html">fc</a></li><li class="progresssection"><a href="2-lcl.html">lcl</a></li><li class="progresssection"><a href="2-str.html">str</a></li><li class="progresscurrent">wal</li><li class="progresssection"><a href="2-mth.html">mth</a></li><li class="progresssection"><a href="2-llas.html">llas</a></li><li class="progresssection"><a href="2-dct.html">dct</a></li><li class="progresssection"><a href="2-trs.html">trs</a></li><li class="progresschapter"><a href="3-em.html">3</a></li><li class="progresschapter"><a href="4-chr.html">4</a></li><li class="progresschapter"><a href="5-htm.html">5</a></li><li class="progresschapter"><a href="6-bf.html">6</a></li><li class="progresschapter"><a href="7-vn.html">7</a></li><li class="progresschapter"><a href="8-ws.html">8</a></li><li class="progressnext"><a href="2-mth.html">&#10095;</a></li></ul></div>
</nav><!--End of weave-->
</main>
</body>
</html>