inweb-bootstrap/docs/foundation-module/4-sm.html
2020-03-30 00:29:23 +01:00

901 lines
121 KiB
HTML

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>4/ws</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 '4/sm' 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#4">Chapter 4: Text Handling</a></li><li><b>String Manipulation</b></li></ul><p class="purpose">Convenient routines for manipulating strings of text.</p>
<ul class="toc"><li><a href="#SP1">&#167;1. Strings are streams</a></li><li><a href="#SP2">&#167;2. New strings</a></li><li><a href="#SP4">&#167;4. Converting from C strings</a></li><li><a href="#SP6">&#167;6. Converting to C strings</a></li><li><a href="#SP7">&#167;7. Converting to integers</a></li><li><a href="#SP8">&#167;8. Length</a></li><li><a href="#SP9">&#167;9. Position markers</a></li><li><a href="#SP13">&#167;13. Character operations</a></li><li><a href="#SP15">&#167;15. Truncation</a></li><li><a href="#SP16">&#167;16. Copying</a></li><li><a href="#SP18">&#167;18. Comparisons</a></li><li><a href="#SP22">&#167;22. White space</a></li><li><a href="#SP24">&#167;24. Deleting characters</a></li><li><a href="#SP25">&#167;25. Substrings</a></li><li><a href="#SP26">&#167;26. Shim for literal storage</a></li></ul><hr class="tocbar">
<p class="inwebparagraph"><a id="SP1"></a><b>&#167;1. Strings are streams. </b>Although Foundation provides limited facilities for handling standard or
wide C-style strings &mdash; that is, null-terminated arrays of <code class="display"><span class="extract">char</span></code> or
<code class="display"><span class="extract">wchar_t</span></code> &mdash; these are not encouraged.
</p>
<p class="inwebparagraph">Instead, a standard string for a program using Foundation is nothing more than
a text stream (see Chapter 2). These are unbounded in size, with memory
allocation being automatic; they are encoded as an array of Unicode code
points (not as UTF-8, -16 or -32); and they do not use a null or indeed any
terminator. This has the advantage that finding the length of a string, and
appending characters to it, run in constant time regardless of the string's
length. It is is entirely feasible to write hundreds of megabytes of
output into a string, if that's useful, and no substantial slowing down
will occur in handling the result (except, of course, that printing it
out on screen would take a while). Strings are also very well protected
against buffer overruns.
</p>
<p class="inwebparagraph">The present section of code provides convenient routines for creating,
duplicating, modifying and examining such strings.
</p>
<p class="inwebparagraph"><a id="SP2"></a><b>&#167;2. New strings. </b>Sometimes we want to make a new string in the sense of allocating more
memory to hold it. These objects won't automatically be destroyed, so we
shouldn't call these routines too casually. If we need a string just for
some space to play with for a short while, it's better to create one
with <code class="display"><span class="extract">TEMPORARY_TEXT</span></code> and then get rid of it with <code class="display"><span class="extract">DISCARD_TEXT</span></code>, macros
defined in Chapter 2.
</p>
<p class="inwebparagraph">The capacity of these strings is unlimited in principle, and the number
here is just the size of the initial memory block, which is fastest to
access.
</p>
<pre class="display">
<span class="reserved">text_stream</span><span class="plain"> *</span><span class="functiontext">Str::new</span><span class="plain">(</span><span class="reserved">void</span><span class="plain">) {</span>
<span class="reserved">return</span><span class="plain"> </span><span class="functiontext">Str::new_with_capacity</span><span class="plain">(32);</span>
<span class="plain">}</span>
<span class="reserved">text_stream</span><span class="plain"> *</span><span class="functiontext">Str::new_with_capacity</span><span class="plain">(</span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">c</span><span class="plain">) {</span>
<span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S</span><span class="plain"> = </span><span class="identifier">CREATE</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="functiontext">Streams::open_to_memory</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">, </span><span class="identifier">c</span><span class="plain">)) </span><span class="reserved">return</span><span class="plain"> </span><span class="identifier">S</span><span class="plain">;</span>
<span class="reserved">return</span><span class="plain"> </span><span class="identifier">NULL</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Str::dispose_of</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">text</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">text</span><span class="plain">) </span><span class="identifier">STREAM_CLOSE</span><span class="plain">(</span><span class="identifier">text</span><span class="plain">);</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Str::new is used in <a href="#SP3">&#167;3</a>, 2/dl (<a href="2-dl.html#SP4_1">&#167;4.1</a>), 2/dct (<a href="2-dct.html#SP7_3_1">&#167;7.3.1</a>), 3/cla (<a href="3-cla.html#SP6">&#167;6</a>, <a href="3-cla.html#SP11">&#167;11</a>), 5/ee (<a href="5-ee.html#SP5">&#167;5</a>), 7/vn (<a href="7-vn.html#SP7">&#167;7</a>).</p>
<p class="endnote">The function Str::new_with_capacity is used in 3/pth (<a href="3-pth.html#SP4">&#167;4</a>), 3/fln (<a href="3-fln.html#SP2">&#167;2</a>).</p>
<p class="endnote">The function Str::dispose_of is used in 2/dct (<a href="2-dct.html#SP7_3_2">&#167;7.3.2</a>, <a href="2-dct.html#SP11">&#167;11</a>).</p>
<p class="inwebparagraph"><a id="SP3"></a><b>&#167;3. </b>Duplication of an existing string is complicated only by the issue that
we want the duplicate always to be writeable, so that <code class="display"><span class="extract">NULL</span></code> can't be
duplicated as <code class="display"><span class="extract">NULL</span></code>.
</p>
<pre class="display">
<span class="reserved">text_stream</span><span class="plain"> *</span><span class="functiontext">Str::duplicate</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">E</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">E</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="functiontext">Str::new</span><span class="plain">();</span>
<span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S</span><span class="plain"> = </span><span class="identifier">CREATE</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="functiontext">Streams::open_to_memory</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">, </span><span class="functiontext">Str::len</span><span class="plain">(</span><span class="identifier">E</span><span class="plain">)+4)) {</span>
<span class="functiontext">Streams::copy</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">, </span><span class="identifier">E</span><span class="plain">);</span>
<span class="reserved">return</span><span class="plain"> </span><span class="identifier">S</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">return</span><span class="plain"> </span><span class="identifier">NULL</span><span class="plain">;</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Str::duplicate is used in 2/dct (<a href="2-dct.html#SP7_3_1">&#167;7.3.1</a>), 3/cla (<a href="3-cla.html#SP5_1">&#167;5.1</a>), 5/ee (<a href="5-ee.html#SP5">&#167;5</a>), 7/vn (<a href="7-vn.html#SP7_1">&#167;7.1</a>).</p>
<p class="inwebparagraph"><a id="SP4"></a><b>&#167;4. Converting from C strings. </b>Here we open text streams initially equal to the given C strings, and
with the capacity of the initial block large enough to hold the whole
thing plus a little extra, for efficiency's sake.
</p>
<pre class="display">
<span class="reserved">text_stream</span><span class="plain"> *</span><span class="functiontext">Str::new_from_wide_string</span><span class="plain">(</span><span class="identifier">wchar_t</span><span class="plain"> *</span><span class="identifier">C_string</span><span class="plain">) {</span>
<span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S</span><span class="plain"> = </span><span class="identifier">CREATE</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="functiontext">Streams::open_from_wide_string</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">, </span><span class="identifier">C_string</span><span class="plain">)) </span><span class="reserved">return</span><span class="plain"> </span><span class="identifier">S</span><span class="plain">;</span>
<span class="reserved">return</span><span class="plain"> </span><span class="identifier">NULL</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">text_stream</span><span class="plain"> *</span><span class="functiontext">Str::new_from_ISO_string</span><span class="plain">(</span><span class="reserved">char</span><span class="plain"> *</span><span class="identifier">C_string</span><span class="plain">) {</span>
<span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S</span><span class="plain"> = </span><span class="identifier">CREATE</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="functiontext">Streams::open_from_ISO_string</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">, </span><span class="identifier">C_string</span><span class="plain">)) </span><span class="reserved">return</span><span class="plain"> </span><span class="identifier">S</span><span class="plain">;</span>
<span class="reserved">return</span><span class="plain"> </span><span class="identifier">NULL</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">text_stream</span><span class="plain"> *</span><span class="functiontext">Str::new_from_UTF8_string</span><span class="plain">(</span><span class="reserved">char</span><span class="plain"> *</span><span class="identifier">C_string</span><span class="plain">) {</span>
<span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S</span><span class="plain"> = </span><span class="identifier">CREATE</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="functiontext">Streams::open_from_UTF8_string</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">, </span><span class="identifier">C_string</span><span class="plain">)) </span><span class="reserved">return</span><span class="plain"> </span><span class="identifier">S</span><span class="plain">;</span>
<span class="reserved">return</span><span class="plain"> </span><span class="identifier">NULL</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">text_stream</span><span class="plain"> *</span><span class="functiontext">Str::new_from_locale_string</span><span class="plain">(</span><span class="reserved">char</span><span class="plain"> *</span><span class="identifier">C_string</span><span class="plain">) {</span>
<span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S</span><span class="plain"> = </span><span class="identifier">CREATE</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="functiontext">Streams::open_from_locale_string</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">, </span><span class="identifier">C_string</span><span class="plain">)) </span><span class="reserved">return</span><span class="plain"> </span><span class="identifier">S</span><span class="plain">;</span>
<span class="reserved">return</span><span class="plain"> </span><span class="identifier">NULL</span><span class="plain">;</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Str::new_from_wide_string is used in 3/cla (<a href="3-cla.html#SP5">&#167;5</a>, <a href="3-cla.html#SP14">&#167;14</a>), 3/pth (<a href="3-pth.html#SP3">&#167;3</a>), 5/ee (<a href="5-ee.html#SP5">&#167;5</a>).</p>
<p class="endnote">The function Str::new_from_ISO_string appears nowhere else.</p>
<p class="endnote">The function Str::new_from_UTF8_string appears nowhere else.</p>
<p class="endnote">The function Str::new_from_locale_string is used in 3/pth (<a href="3-pth.html#SP2">&#167;2</a>, <a href="3-pth.html#SP3">&#167;3</a>).</p>
<p class="inwebparagraph"><a id="SP5"></a><b>&#167;5. </b>And sometimes we want to use an existing stream object:
</p>
<pre class="display">
<span class="reserved">text_stream</span><span class="plain"> *</span><span class="functiontext">Str::from_wide_string</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S</span><span class="plain">, </span><span class="identifier">wchar_t</span><span class="plain"> *</span><span class="identifier">c_string</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="functiontext">Streams::open_from_wide_string</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">, </span><span class="identifier">c_string</span><span class="plain">) == </span><span class="constant">FALSE</span><span class="plain">) </span><span class="reserved">return</span><span class="plain"> </span><span class="identifier">NULL</span><span class="plain">;</span>
<span class="reserved">return</span><span class="plain"> </span><span class="identifier">S</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">text_stream</span><span class="plain"> *</span><span class="functiontext">Str::from_locale_string</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S</span><span class="plain">, </span><span class="reserved">char</span><span class="plain"> *</span><span class="identifier">c_string</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="functiontext">Streams::open_from_locale_string</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">, </span><span class="identifier">c_string</span><span class="plain">) == </span><span class="constant">FALSE</span><span class="plain">) </span><span class="reserved">return</span><span class="plain"> </span><span class="identifier">NULL</span><span class="plain">;</span>
<span class="reserved">return</span><span class="plain"> </span><span class="identifier">S</span><span class="plain">;</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Str::from_wide_string appears nowhere else.</p>
<p class="endnote">The function Str::from_locale_string appears nowhere else.</p>
<p class="inwebparagraph"><a id="SP6"></a><b>&#167;6. Converting to C strings. </b></p>
<pre class="display">
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Str::copy_to_ISO_string</span><span class="plain">(</span><span class="reserved">char</span><span class="plain"> *</span><span class="identifier">C_string</span><span class="plain">, </span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S</span><span class="plain">, </span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">buffer_size</span><span class="plain">) {</span>
<span class="functiontext">Streams::write_as_ISO_string</span><span class="plain">(</span><span class="identifier">C_string</span><span class="plain">, </span><span class="identifier">S</span><span class="plain">, </span><span class="identifier">buffer_size</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Str::copy_to_UTF8_string</span><span class="plain">(</span><span class="reserved">char</span><span class="plain"> *</span><span class="identifier">C_string</span><span class="plain">, </span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S</span><span class="plain">, </span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">buffer_size</span><span class="plain">) {</span>
<span class="functiontext">Streams::write_as_UTF8_string</span><span class="plain">(</span><span class="identifier">C_string</span><span class="plain">, </span><span class="identifier">S</span><span class="plain">, </span><span class="identifier">buffer_size</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Str::copy_to_wide_string</span><span class="plain">(</span><span class="identifier">wchar_t</span><span class="plain"> *</span><span class="identifier">C_string</span><span class="plain">, </span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S</span><span class="plain">, </span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">buffer_size</span><span class="plain">) {</span>
<span class="functiontext">Streams::write_as_wide_string</span><span class="plain">(</span><span class="identifier">C_string</span><span class="plain">, </span><span class="identifier">S</span><span class="plain">, </span><span class="identifier">buffer_size</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Str::copy_to_locale_string</span><span class="plain">(</span><span class="reserved">char</span><span class="plain"> *</span><span class="identifier">C_string</span><span class="plain">, </span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S</span><span class="plain">, </span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">buffer_size</span><span class="plain">) {</span>
<span class="functiontext">Streams::write_as_locale_string</span><span class="plain">(</span><span class="identifier">C_string</span><span class="plain">, </span><span class="identifier">S</span><span class="plain">, </span><span class="identifier">buffer_size</span><span class="plain">);</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Str::copy_to_ISO_string appears nowhere else.</p>
<p class="endnote">The function Str::copy_to_UTF8_string appears nowhere else.</p>
<p class="endnote">The function Str::copy_to_wide_string appears nowhere else.</p>
<p class="endnote">The function Str::copy_to_locale_string is used in 3/pth (<a href="3-pth.html#SP8">&#167;8</a>, <a href="3-pth.html#SP9">&#167;9</a>), 3/fln (<a href="3-fln.html#SP10">&#167;10</a>, <a href="3-fln.html#SP12">&#167;12</a>), 3/drc (<a href="3-drc.html#SP2">&#167;2</a>).</p>
<p class="inwebparagraph"><a id="SP7"></a><b>&#167;7. Converting to integers. </b></p>
<pre class="display">
<span class="reserved">int</span><span class="plain"> </span><span class="functiontext">Str::atoi</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S</span><span class="plain">, </span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">index</span><span class="plain">) {</span>
<span class="reserved">char</span><span class="plain"> </span><span class="identifier">buffer</span><span class="plain">[32];</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">i</span><span class="plain"> = 0;</span>
<span class="reserved">for</span><span class="plain"> (</span><span class="reserved">string_position</span><span class="plain"> </span><span class="identifier">P</span><span class="plain"> = </span><span class="functiontext">Str::at</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">, </span><span class="identifier">index</span><span class="plain">);</span>
<span class="plain">((</span><span class="identifier">i</span><span class="plain"> &lt; 31) &amp;&amp; (</span><span class="identifier">P</span><span class="element">.index</span><span class="plain"> &lt; </span><span class="functiontext">Str::len</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">))); </span><span class="identifier">P</span><span class="plain"> = </span><span class="functiontext">Str::forward</span><span class="plain">(</span><span class="identifier">P</span><span class="plain">))</span>
<span class="identifier">buffer</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">++] = (</span><span class="reserved">char</span><span class="plain">) </span><span class="functiontext">Str::get</span><span class="plain">(</span><span class="identifier">P</span><span class="plain">);</span>
<span class="identifier">buffer</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">] = 0;</span>
<span class="reserved">return</span><span class="plain"> </span><span class="identifier">atoi</span><span class="plain">(</span><span class="identifier">buffer</span><span class="plain">);</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Str::atoi is used in 3/cla (<a href="3-cla.html#SP12">&#167;12</a>), 7/vn (<a href="7-vn.html#SP10">&#167;10</a>).</p>
<p class="inwebparagraph"><a id="SP8"></a><b>&#167;8. Length. </b>A puritan would return a <code class="display"><span class="extract">size_t</span></code> here, but I am not a puritan.
</p>
<pre class="display">
<span class="reserved">int</span><span class="plain"> </span><span class="functiontext">Str::len</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S</span><span class="plain">) {</span>
<span class="reserved">return</span><span class="plain"> </span><span class="functiontext">Streams::get_position</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">);</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Str::len is used in <a href="#SP3">&#167;3</a>, <a href="#SP7">&#167;7</a>, <a href="#SP10">&#167;10</a>, <a href="#SP11">&#167;11</a>, <a href="#SP12">&#167;12</a>, <a href="#SP13">&#167;13</a>, <a href="#SP14">&#167;14</a>, <a href="#SP15">&#167;15</a>, <a href="#SP16">&#167;16</a>, <a href="#SP18">&#167;18</a>, <a href="#SP19">&#167;19</a>, <a href="#SP20">&#167;20</a>, <a href="#SP21">&#167;21</a>, <a href="#SP23">&#167;23</a>, <a href="#SP24">&#167;24</a>, <a href="#SP25">&#167;25</a>, 2/dl (<a href="2-dl.html#SP9">&#167;9</a>), 3/cla (<a href="3-cla.html#SP13">&#167;13</a>, <a href="3-cla.html#SP14">&#167;14</a>, <a href="3-cla.html#SP14_1">&#167;14.1</a>), 3/pth (<a href="3-pth.html#SP4">&#167;4</a>, <a href="3-pth.html#SP5">&#167;5</a>, <a href="3-pth.html#SP7">&#167;7</a>), 3/fln (<a href="3-fln.html#SP2">&#167;2</a>, <a href="3-fln.html#SP3">&#167;3</a>, <a href="3-fln.html#SP5">&#167;5</a>, <a href="3-fln.html#SP9">&#167;9</a>), 4/taa (<a href="4-taa.html#SP2_1">&#167;2.1</a>), 4/pm (<a href="4-pm.html#SP3">&#167;3</a>, <a href="4-pm.html#SP4">&#167;4</a>, <a href="4-pm.html#SP11_3">&#167;11.3</a>, <a href="4-pm.html#SP14">&#167;14</a>), 5/htm (<a href="5-htm.html#SP7">&#167;7</a>, <a href="5-htm.html#SP15">&#167;15</a>), 5/ee (<a href="5-ee.html#SP7_1">&#167;7.1</a>, <a href="5-ee.html#SP7_2_3">&#167;7.2.3</a>, <a href="5-ee.html#SP7_2_4">&#167;7.2.4</a>), 7/vn (<a href="7-vn.html#SP7">&#167;7</a>, <a href="7-vn.html#SP7_1">&#167;7.1</a>, <a href="7-vn.html#SP10">&#167;10</a>).</p>
<p class="inwebparagraph"><a id="SP9"></a><b>&#167;9. Position markers. </b>A position marker is a lightweight way to refer to a particular position
in a given string. Position 0 is before the first character; if, for
example, the string contains the word "gazpacho", then position 8 represents
the end of the string, after the "o". Negative positions are not allowed,
but positive ones well past the end of the string are legal. (Doing things
at those positions may well not be, of course.)
</p>
<pre class="display">
<span class="reserved">typedef</span><span class="plain"> </span><span class="reserved">struct</span><span class="plain"> </span><span class="reserved">string_position</span><span class="plain"> {</span>
<span class="reserved">struct</span><span class="plain"> </span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S</span><span class="plain">;</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">index</span><span class="plain">;</span>
<span class="plain">} </span><span class="reserved">string_position</span><span class="plain">;</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The structure string_position is private to this section.</p>
<p class="inwebparagraph"><a id="SP10"></a><b>&#167;10. </b>You can then find a position in a given string thus:
</p>
<pre class="display">
<span class="reserved">string_position</span><span class="plain"> </span><span class="functiontext">Str::start</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S</span><span class="plain">) {</span>
<span class="reserved">string_position</span><span class="plain"> </span><span class="identifier">P</span><span class="plain">; </span><span class="identifier">P</span><span class="element">.S</span><span class="plain"> = </span><span class="identifier">S</span><span class="plain">; </span><span class="identifier">P</span><span class="element">.index</span><span class="plain"> = 0; </span><span class="reserved">return</span><span class="plain"> </span><span class="identifier">P</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">string_position</span><span class="plain"> </span><span class="functiontext">Str::at</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S</span><span class="plain">, </span><span class="reserved">int</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">i</span><span class="plain"> &lt; 0) </span><span class="identifier">i</span><span class="plain"> = 0;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">i</span><span class="plain"> &gt; </span><span class="functiontext">Str::len</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">)) </span><span class="identifier">i</span><span class="plain"> = </span><span class="functiontext">Str::len</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">);</span>
<span class="reserved">string_position</span><span class="plain"> </span><span class="identifier">P</span><span class="plain">; </span><span class="identifier">P</span><span class="element">.S</span><span class="plain"> = </span><span class="identifier">S</span><span class="plain">; </span><span class="identifier">P</span><span class="element">.index</span><span class="plain"> = </span><span class="identifier">i</span><span class="plain">; </span><span class="reserved">return</span><span class="plain"> </span><span class="identifier">P</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">string_position</span><span class="plain"> </span><span class="functiontext">Str::end</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S</span><span class="plain">) {</span>
<span class="reserved">string_position</span><span class="plain"> </span><span class="identifier">P</span><span class="plain">; </span><span class="identifier">P</span><span class="element">.S</span><span class="plain"> = </span><span class="identifier">S</span><span class="plain">; </span><span class="identifier">P</span><span class="element">.index</span><span class="plain"> = </span><span class="functiontext">Str::len</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">); </span><span class="reserved">return</span><span class="plain"> </span><span class="identifier">P</span><span class="plain">;</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Str::start is used in <a href="#SP12">&#167;12</a>, <a href="#SP19">&#167;19</a>, <a href="#SP23">&#167;23</a>, 3/pth (<a href="3-pth.html#SP5">&#167;5</a>).</p>
<p class="endnote">The function Str::at is used in <a href="#SP7">&#167;7</a>, <a href="#SP13">&#167;13</a>, <a href="#SP14">&#167;14</a>, <a href="#SP15">&#167;15</a>, <a href="#SP16">&#167;16</a>, <a href="#SP24">&#167;24</a>, 3/pth (<a href="3-pth.html#SP4">&#167;4</a>, <a href="3-pth.html#SP5">&#167;5</a>), 3/fln (<a href="3-fln.html#SP2">&#167;2</a>, <a href="3-fln.html#SP3">&#167;3</a>, <a href="3-fln.html#SP9">&#167;9</a>).</p>
<p class="endnote">The function Str::end is used in <a href="#SP12">&#167;12</a>, 3/fln (<a href="3-fln.html#SP9">&#167;9</a>).</p>
<p class="inwebparagraph"><a id="SP11"></a><b>&#167;11. </b>And you can step forwards or backwards:
</p>
<pre class="display">
<span class="reserved">string_position</span><span class="plain"> </span><span class="functiontext">Str::back</span><span class="plain">(</span><span class="reserved">string_position</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">P</span><span class="element">.index</span><span class="plain"> &gt; 0) </span><span class="identifier">P</span><span class="element">.index</span><span class="plain">--; </span><span class="reserved">return</span><span class="plain"> </span><span class="identifier">P</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">string_position</span><span class="plain"> </span><span class="functiontext">Str::forward</span><span class="plain">(</span><span class="reserved">string_position</span><span class="plain"> </span><span class="identifier">P</span><span class="plain">) {</span>
<span class="identifier">P</span><span class="element">.index</span><span class="plain">++; </span><span class="reserved">return</span><span class="plain"> </span><span class="identifier">P</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">string_position</span><span class="plain"> </span><span class="functiontext">Str::plus</span><span class="plain">(</span><span class="reserved">string_position</span><span class="plain"> </span><span class="identifier">P</span><span class="plain">, </span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">increment</span><span class="plain">) {</span>
<span class="identifier">P</span><span class="element">.index</span><span class="plain"> += </span><span class="identifier">increment</span><span class="plain">; </span><span class="reserved">return</span><span class="plain"> </span><span class="identifier">P</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">int</span><span class="plain"> </span><span class="functiontext">Str::width_between</span><span class="plain">(</span><span class="reserved">string_position</span><span class="plain"> </span><span class="identifier">P1</span><span class="plain">, </span><span class="reserved">string_position</span><span class="plain"> </span><span class="identifier">P2</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">P1</span><span class="element">.S</span><span class="plain"> != </span><span class="identifier">P2</span><span class="element">.S</span><span class="plain">) </span><span class="identifier">internal_error</span><span class="plain">(</span><span class="string">"positions are in different strings"</span><span class="plain">);</span>
<span class="reserved">return</span><span class="plain"> </span><span class="identifier">P2</span><span class="element">.index</span><span class="plain"> - </span><span class="identifier">P1</span><span class="element">.index</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">int</span><span class="plain"> </span><span class="functiontext">Str::in_range</span><span class="plain">(</span><span class="reserved">string_position</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">P</span><span class="element">.index</span><span class="plain"> &lt; </span><span class="functiontext">Str::len</span><span class="plain">(</span><span class="identifier">P</span><span class="element">.S</span><span class="plain">)) </span><span class="reserved">return</span><span class="plain"> </span><span class="constant">TRUE</span><span class="plain">;</span>
<span class="reserved">return</span><span class="plain"> </span><span class="constant">FALSE</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">int</span><span class="plain"> </span><span class="functiontext">Str::index</span><span class="plain">(</span><span class="reserved">string_position</span><span class="plain"> </span><span class="identifier">P</span><span class="plain">) {</span>
<span class="reserved">return</span><span class="plain"> </span><span class="identifier">P</span><span class="element">.index</span><span class="plain">;</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Str::back is used in <a href="#SP12">&#167;12</a>, 3/fln (<a href="3-fln.html#SP9">&#167;9</a>).</p>
<p class="endnote">The function Str::forward is used in <a href="#SP7">&#167;7</a>, <a href="#SP16">&#167;16</a>, <a href="#SP19">&#167;19</a>, <a href="#SP23">&#167;23</a>, <a href="#SP24">&#167;24</a>, 3/fln (<a href="3-fln.html#SP2">&#167;2</a>).</p>
<p class="endnote">The function Str::plus appears nowhere else.</p>
<p class="endnote">The function Str::width_between appears nowhere else.</p>
<p class="endnote">The function Str::in_range appears nowhere else.</p>
<p class="endnote">The function Str::index appears nowhere else.</p>
<p class="inwebparagraph"><a id="SP12"></a><b>&#167;12. </b>This leads to the following convenient loop macros:
</p>
<pre class="definitions">
<span class="definitionkeyword">define</span> <span class="identifier">LOOP_THROUGH_TEXT</span><span class="plain">(</span><span class="identifier">P</span><span class="plain">, </span><span class="identifier">ST</span><span class="plain">)</span>
<span class="reserved">for</span><span class="plain"> (</span><span class="reserved">string_position</span><span class="plain"> </span><span class="identifier">P</span><span class="plain"> = </span><span class="functiontext">Str::start</span><span class="plain">(</span><span class="identifier">ST</span><span class="plain">); </span><span class="identifier">P</span><span class="element">.index</span><span class="plain"> &lt; </span><span class="functiontext">Str::len</span><span class="plain">(</span><span class="identifier">P</span><span class="element">.S</span><span class="plain">); </span><span class="identifier">P</span><span class="element">.index</span><span class="plain">++)</span>
<span class="definitionkeyword">define</span> <span class="identifier">LOOP_BACKWARDS_THROUGH_TEXT</span><span class="plain">(</span><span class="identifier">P</span><span class="plain">, </span><span class="identifier">ST</span><span class="plain">)</span>
<span class="reserved">for</span><span class="plain"> (</span><span class="reserved">string_position</span><span class="plain"> </span><span class="identifier">P</span><span class="plain"> = </span><span class="functiontext">Str::back</span><span class="plain">(</span><span class="functiontext">Str::end</span><span class="plain">(</span><span class="identifier">ST</span><span class="plain">)); </span><span class="identifier">P</span><span class="element">.index</span><span class="plain"> &gt;= 0; </span><span class="identifier">P</span><span class="element">.index</span><span class="plain">--)</span>
</pre>
<p class="inwebparagraph"><a id="SP13"></a><b>&#167;13. Character operations. </b>How to get at individual characters, then, now that we can refer to positions:
</p>
<pre class="display">
<span class="identifier">wchar_t</span><span class="plain"> </span><span class="functiontext">Str::get</span><span class="plain">(</span><span class="reserved">string_position</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">P</span><span class="element">.S</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) || (</span><span class="identifier">P</span><span class="element">.index</span><span class="plain"> &lt; 0)) </span><span class="reserved">return</span><span class="plain"> 0;</span>
<span class="reserved">return</span><span class="plain"> </span><span class="functiontext">Streams::get_char_at_index</span><span class="plain">(</span><span class="identifier">P</span><span class="element">.S</span><span class="plain">, </span><span class="identifier">P</span><span class="element">.index</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="identifier">wchar_t</span><span class="plain"> </span><span class="functiontext">Str::get_at</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S</span><span class="plain">, </span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">index</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> ((</span><span class="identifier">S</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) || (</span><span class="identifier">index</span><span class="plain"> &lt; 0)) </span><span class="reserved">return</span><span class="plain"> 0;</span>
<span class="reserved">return</span><span class="plain"> </span><span class="functiontext">Streams::get_char_at_index</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">, </span><span class="identifier">index</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="identifier">wchar_t</span><span class="plain"> </span><span class="functiontext">Str::get_first_char</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S</span><span class="plain">) {</span>
<span class="reserved">return</span><span class="plain"> </span><span class="functiontext">Str::get</span><span class="plain">(</span><span class="functiontext">Str::at</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">, 0));</span>
<span class="plain">}</span>
<span class="identifier">wchar_t</span><span class="plain"> </span><span class="functiontext">Str::get_last_char</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S</span><span class="plain">) {</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">L</span><span class="plain"> = </span><span class="functiontext">Str::len</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">L</span><span class="plain"> == 0) </span><span class="reserved">return</span><span class="plain"> 0;</span>
<span class="reserved">return</span><span class="plain"> </span><span class="functiontext">Str::get</span><span class="plain">(</span><span class="functiontext">Str::at</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">, </span><span class="identifier">L</span><span class="plain">-1));</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Str::get is used in <a href="#SP7">&#167;7</a>, <a href="#SP16">&#167;16</a>, <a href="#SP19">&#167;19</a>, <a href="#SP21">&#167;21</a>, <a href="#SP22">&#167;22</a>, <a href="#SP23">&#167;23</a>, <a href="#SP24">&#167;24</a>, <a href="#SP25">&#167;25</a>, 2/dct (<a href="2-dct.html#SP4">&#167;4</a>), 3/pth (<a href="3-pth.html#SP4">&#167;4</a>, <a href="3-pth.html#SP5">&#167;5</a>), 3/fln (<a href="3-fln.html#SP2">&#167;2</a>, <a href="3-fln.html#SP3">&#167;3</a>, <a href="3-fln.html#SP7">&#167;7</a>, <a href="3-fln.html#SP8">&#167;8</a>, <a href="3-fln.html#SP9">&#167;9</a>), 3/shl (<a href="3-shl.html#SP1">&#167;1</a>), 4/pm (<a href="4-pm.html#SP5">&#167;5</a>), 5/ee (<a href="5-ee.html#SP5">&#167;5</a>), 7/vn (<a href="7-vn.html#SP7">&#167;7</a>, <a href="7-vn.html#SP10">&#167;10</a>).</p>
<p class="endnote">The function Str::get_at is used in <a href="#SP20">&#167;20</a>, <a href="#SP23">&#167;23</a>, <a href="#SP25">&#167;25</a>, 3/pth (<a href="3-pth.html#SP7">&#167;7</a>), 3/fln (<a href="3-fln.html#SP5">&#167;5</a>), 4/taa (<a href="4-taa.html#SP2">&#167;2</a>), 4/pm (<a href="4-pm.html#SP3">&#167;3</a>, <a href="4-pm.html#SP4">&#167;4</a>, <a href="4-pm.html#SP11">&#167;11</a>, <a href="4-pm.html#SP11_4">&#167;11.4</a>, <a href="4-pm.html#SP11_6">&#167;11.6</a>, <a href="4-pm.html#SP14">&#167;14</a>, <a href="4-pm.html#SP14_1">&#167;14.1</a>).</p>
<p class="endnote">The function Str::get_first_char is used in 7/vn (<a href="7-vn.html#SP10">&#167;10</a>).</p>
<p class="endnote">The function Str::get_last_char appears nowhere else.</p>
<p class="inwebparagraph"><a id="SP14"></a><b>&#167;14. </b></p>
<pre class="display">
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Str::put</span><span class="plain">(</span><span class="reserved">string_position</span><span class="plain"> </span><span class="identifier">P</span><span class="plain">, </span><span class="identifier">wchar_t</span><span class="plain"> </span><span class="identifier">C</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">P</span><span class="element">.index</span><span class="plain"> &lt; 0) </span><span class="identifier">internal_error</span><span class="plain">(</span><span class="string">"wrote before start of string"</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">P</span><span class="element">.S</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) </span><span class="identifier">internal_error</span><span class="plain">(</span><span class="string">"wrote to null stream"</span><span class="plain">);</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">ext</span><span class="plain"> = </span><span class="functiontext">Str::len</span><span class="plain">(</span><span class="identifier">P</span><span class="element">.S</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">P</span><span class="element">.index</span><span class="plain"> &gt; </span><span class="identifier">ext</span><span class="plain">) </span><span class="identifier">internal_error</span><span class="plain">(</span><span class="string">"wrote beyond end of string"</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">P</span><span class="element">.index</span><span class="plain"> == </span><span class="identifier">ext</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">C</span><span class="plain">) </span><span class="identifier">PUT_TO</span><span class="plain">(</span><span class="identifier">P</span><span class="element">.S</span><span class="plain">, (</span><span class="reserved">int</span><span class="plain">) </span><span class="identifier">C</span><span class="plain">);</span>
<span class="reserved">return</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="functiontext">Streams::put_char_at_index</span><span class="plain">(</span><span class="identifier">P</span><span class="element">.S</span><span class="plain">, </span><span class="identifier">P</span><span class="element">.index</span><span class="plain">, </span><span class="identifier">C</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Str::put_at</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S</span><span class="plain">, </span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">index</span><span class="plain">, </span><span class="identifier">wchar_t</span><span class="plain"> </span><span class="identifier">C</span><span class="plain">) {</span>
<span class="functiontext">Str::put</span><span class="plain">(</span><span class="functiontext">Str::at</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">, </span><span class="identifier">index</span><span class="plain">), </span><span class="identifier">C</span><span class="plain">);</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Str::put is used in <a href="#SP15">&#167;15</a>, <a href="#SP23">&#167;23</a>, <a href="#SP24">&#167;24</a>, 5/ee (<a href="5-ee.html#SP5">&#167;5</a>).</p>
<p class="endnote">The function Str::put_at is used in 4/tf (<a href="4-tf.html#SP5_3">&#167;5.3</a>, <a href="4-tf.html#SP6">&#167;6</a>).</p>
<p class="inwebparagraph"><a id="SP15"></a><b>&#167;15. Truncation. </b></p>
<pre class="display">
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Str::clear</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S</span><span class="plain">) {</span>
<span class="functiontext">Str::truncate</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">, 0);</span>
<span class="plain">}</span>
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Str::truncate</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S</span><span class="plain">, </span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">len</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">len</span><span class="plain"> &lt; 0) </span><span class="identifier">len</span><span class="plain"> = 0;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">len</span><span class="plain"> &lt; </span><span class="functiontext">Str::len</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">)) </span><span class="functiontext">Str::put</span><span class="plain">(</span><span class="functiontext">Str::at</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">, </span><span class="identifier">len</span><span class="plain">), 0);</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Str::clear is used in <a href="#SP16">&#167;16</a>, <a href="#SP17">&#167;17</a>, <a href="#SP24">&#167;24</a>, 3/drc (<a href="3-drc.html#SP2">&#167;2</a>), 4/tf (<a href="4-tf.html#SP6">&#167;6</a>), 4/pm (<a href="4-pm.html#SP11_6">&#167;11.6</a>), 7/vn (<a href="7-vn.html#SP7_1">&#167;7.1</a>).</p>
<p class="endnote">The function Str::truncate is used in <a href="#SP23">&#167;23</a>, <a href="#SP24">&#167;24</a>.</p>
<p class="inwebparagraph"><a id="SP16"></a><b>&#167;16. Copying. </b></p>
<pre class="display">
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Str::concatenate</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S1</span><span class="plain">, </span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S2</span><span class="plain">) {</span>
<span class="functiontext">Streams::copy</span><span class="plain">(</span><span class="identifier">S1</span><span class="plain">, </span><span class="identifier">S2</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Str::copy</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S1</span><span class="plain">, </span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S2</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">S1</span><span class="plain"> == </span><span class="identifier">S2</span><span class="plain">) </span><span class="reserved">return</span><span class="plain">;</span>
<span class="functiontext">Str::clear</span><span class="plain">(</span><span class="identifier">S1</span><span class="plain">);</span>
<span class="functiontext">Streams::copy</span><span class="plain">(</span><span class="identifier">S1</span><span class="plain">, </span><span class="identifier">S2</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Str::copy_tail</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S1</span><span class="plain">, </span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S2</span><span class="plain">, </span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">from</span><span class="plain">) {</span>
<span class="functiontext">Str::clear</span><span class="plain">(</span><span class="identifier">S1</span><span class="plain">);</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">L</span><span class="plain"> = </span><span class="functiontext">Str::len</span><span class="plain">(</span><span class="identifier">S2</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">from</span><span class="plain"> &lt; </span><span class="identifier">L</span><span class="plain">)</span>
<span class="reserved">for</span><span class="plain"> (</span><span class="reserved">string_position</span><span class="plain"> </span><span class="identifier">P</span><span class="plain"> = </span><span class="functiontext">Str::at</span><span class="plain">(</span><span class="identifier">S2</span><span class="plain">, </span><span class="identifier">from</span><span class="plain">); </span><span class="identifier">P</span><span class="element">.index</span><span class="plain"> &lt; </span><span class="identifier">L</span><span class="plain">; </span><span class="identifier">P</span><span class="plain"> = </span><span class="functiontext">Str::forward</span><span class="plain">(</span><span class="identifier">P</span><span class="plain">))</span>
<span class="identifier">PUT_TO</span><span class="plain">(</span><span class="identifier">S1</span><span class="plain">, </span><span class="functiontext">Str::get</span><span class="plain">(</span><span class="identifier">P</span><span class="plain">));</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Str::concatenate appears nowhere else.</p>
<p class="endnote">The function Str::copy is used in 3/cla (<a href="3-cla.html#SP12">&#167;12</a>), 4/pm (<a href="4-pm.html#SP14">&#167;14</a>), 5/ee (<a href="5-ee.html#SP5">&#167;5</a>, <a href="5-ee.html#SP7_1">&#167;7.1</a>).</p>
<p class="endnote">The function Str::copy_tail appears nowhere else.</p>
<p class="inwebparagraph"><a id="SP17"></a><b>&#167;17. </b>A subtly different operation is to set a string equal to a given C string:
</p>
<pre class="display">
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Str::copy_ISO_string</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S</span><span class="plain">, </span><span class="reserved">char</span><span class="plain"> *</span><span class="identifier">C_string</span><span class="plain">) {</span>
<span class="functiontext">Str::clear</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">);</span>
<span class="functiontext">Streams::write_ISO_string</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">, </span><span class="identifier">C_string</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Str::copy_UTF8_string</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S</span><span class="plain">, </span><span class="reserved">char</span><span class="plain"> *</span><span class="identifier">C_string</span><span class="plain">) {</span>
<span class="functiontext">Str::clear</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">);</span>
<span class="functiontext">Streams::write_UTF8_string</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">, </span><span class="identifier">C_string</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Str::copy_wide_string</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S</span><span class="plain">, </span><span class="identifier">wchar_t</span><span class="plain"> *</span><span class="identifier">C_string</span><span class="plain">) {</span>
<span class="functiontext">Str::clear</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">);</span>
<span class="functiontext">Streams::write_wide_string</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">, </span><span class="identifier">C_string</span><span class="plain">);</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Str::copy_ISO_string appears nowhere else.</p>
<p class="endnote">The function Str::copy_UTF8_string appears nowhere else.</p>
<p class="endnote">The function Str::copy_wide_string appears nowhere else.</p>
<p class="inwebparagraph"><a id="SP18"></a><b>&#167;18. Comparisons. </b>We provide both case sensitive and insensitive versions.
</p>
<pre class="display">
<span class="reserved">int</span><span class="plain"> </span><span class="functiontext">Str::eq</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S1</span><span class="plain">, </span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S2</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> ((</span><span class="functiontext">Str::len</span><span class="plain">(</span><span class="identifier">S1</span><span class="plain">) == </span><span class="functiontext">Str::len</span><span class="plain">(</span><span class="identifier">S2</span><span class="plain">)) &amp;&amp; (</span><span class="functiontext">Str::cmp</span><span class="plain">(</span><span class="identifier">S1</span><span class="plain">, </span><span class="identifier">S2</span><span class="plain">) == 0)) </span><span class="reserved">return</span><span class="plain"> </span><span class="constant">TRUE</span><span class="plain">;</span>
<span class="reserved">return</span><span class="plain"> </span><span class="constant">FALSE</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">int</span><span class="plain"> </span><span class="functiontext">Str::eq_insensitive</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S1</span><span class="plain">, </span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S2</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> ((</span><span class="functiontext">Str::len</span><span class="plain">(</span><span class="identifier">S1</span><span class="plain">) == </span><span class="functiontext">Str::len</span><span class="plain">(</span><span class="identifier">S2</span><span class="plain">)) &amp;&amp; (</span><span class="functiontext">Str::cmp_insensitive</span><span class="plain">(</span><span class="identifier">S1</span><span class="plain">, </span><span class="identifier">S2</span><span class="plain">) == 0)) </span><span class="reserved">return</span><span class="plain"> </span><span class="constant">TRUE</span><span class="plain">;</span>
<span class="reserved">return</span><span class="plain"> </span><span class="constant">FALSE</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">int</span><span class="plain"> </span><span class="functiontext">Str::ne</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S1</span><span class="plain">, </span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S2</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> ((</span><span class="functiontext">Str::len</span><span class="plain">(</span><span class="identifier">S1</span><span class="plain">) != </span><span class="functiontext">Str::len</span><span class="plain">(</span><span class="identifier">S2</span><span class="plain">)) || (</span><span class="functiontext">Str::cmp</span><span class="plain">(</span><span class="identifier">S1</span><span class="plain">, </span><span class="identifier">S2</span><span class="plain">) != 0)) </span><span class="reserved">return</span><span class="plain"> </span><span class="constant">TRUE</span><span class="plain">;</span>
<span class="reserved">return</span><span class="plain"> </span><span class="constant">FALSE</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">int</span><span class="plain"> </span><span class="functiontext">Str::ne_insensitive</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S1</span><span class="plain">, </span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S2</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> ((</span><span class="functiontext">Str::len</span><span class="plain">(</span><span class="identifier">S1</span><span class="plain">) != </span><span class="functiontext">Str::len</span><span class="plain">(</span><span class="identifier">S2</span><span class="plain">)) || (</span><span class="functiontext">Str::cmp_insensitive</span><span class="plain">(</span><span class="identifier">S1</span><span class="plain">, </span><span class="identifier">S2</span><span class="plain">) != 0)) </span><span class="reserved">return</span><span class="plain"> </span><span class="constant">TRUE</span><span class="plain">;</span>
<span class="reserved">return</span><span class="plain"> </span><span class="constant">FALSE</span><span class="plain">;</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Str::eq is used in 2/dl (<a href="2-dl.html#SP9">&#167;9</a>), 2/dct (<a href="2-dct.html#SP7_3">&#167;7.3</a>), 3/pth (<a href="3-pth.html#SP3">&#167;3</a>), 3/fln (<a href="3-fln.html#SP11">&#167;11</a>).</p>
<p class="endnote">The function Str::eq_insensitive appears nowhere else.</p>
<p class="endnote">The function Str::ne is used in 7/vn (<a href="7-vn.html#SP8">&#167;8</a>).</p>
<p class="endnote">The function Str::ne_insensitive appears nowhere else.</p>
<p class="inwebparagraph"><a id="SP19"></a><b>&#167;19. </b>These two routines produce a numerical string difference suitable for
alphabetic sorting, like <code class="display"><span class="extract">strlen</span></code> in the C standard library.
</p>
<pre class="display">
<span class="reserved">int</span><span class="plain"> </span><span class="functiontext">Str::cmp</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S1</span><span class="plain">, </span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S2</span><span class="plain">) {</span>
<span class="reserved">for</span><span class="plain"> (</span><span class="reserved">string_position</span><span class="plain"> </span><span class="identifier">P</span><span class="plain"> = </span><span class="functiontext">Str::start</span><span class="plain">(</span><span class="identifier">S1</span><span class="plain">), </span><span class="identifier">Q</span><span class="plain"> = </span><span class="functiontext">Str::start</span><span class="plain">(</span><span class="identifier">S2</span><span class="plain">);</span>
<span class="plain">(</span><span class="identifier">P</span><span class="element">.index</span><span class="plain"> &lt; </span><span class="functiontext">Str::len</span><span class="plain">(</span><span class="identifier">S1</span><span class="plain">)) &amp;&amp; (</span><span class="identifier">Q</span><span class="element">.index</span><span class="plain"> &lt; </span><span class="functiontext">Str::len</span><span class="plain">(</span><span class="identifier">S2</span><span class="plain">));</span>
<span class="identifier">P</span><span class="plain"> = </span><span class="functiontext">Str::forward</span><span class="plain">(</span><span class="identifier">P</span><span class="plain">), </span><span class="identifier">Q</span><span class="plain"> = </span><span class="functiontext">Str::forward</span><span class="plain">(</span><span class="identifier">Q</span><span class="plain">)) {</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">d</span><span class="plain"> = (</span><span class="reserved">int</span><span class="plain">) </span><span class="functiontext">Str::get</span><span class="plain">(</span><span class="identifier">P</span><span class="plain">) - (</span><span class="reserved">int</span><span class="plain">) </span><span class="functiontext">Str::get</span><span class="plain">(</span><span class="identifier">Q</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">d</span><span class="plain"> != 0) </span><span class="reserved">return</span><span class="plain"> </span><span class="identifier">d</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">return</span><span class="plain"> </span><span class="functiontext">Str::len</span><span class="plain">(</span><span class="identifier">S1</span><span class="plain">) - </span><span class="functiontext">Str::len</span><span class="plain">(</span><span class="identifier">S2</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="reserved">int</span><span class="plain"> </span><span class="functiontext">Str::cmp_insensitive</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S1</span><span class="plain">, </span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S2</span><span class="plain">) {</span>
<span class="reserved">for</span><span class="plain"> (</span><span class="reserved">string_position</span><span class="plain"> </span><span class="identifier">P</span><span class="plain"> = </span><span class="functiontext">Str::start</span><span class="plain">(</span><span class="identifier">S1</span><span class="plain">), </span><span class="identifier">Q</span><span class="plain"> = </span><span class="functiontext">Str::start</span><span class="plain">(</span><span class="identifier">S2</span><span class="plain">);</span>
<span class="plain">(</span><span class="identifier">P</span><span class="element">.index</span><span class="plain"> &lt; </span><span class="functiontext">Str::len</span><span class="plain">(</span><span class="identifier">S1</span><span class="plain">)) &amp;&amp; (</span><span class="identifier">Q</span><span class="element">.index</span><span class="plain"> &lt; </span><span class="functiontext">Str::len</span><span class="plain">(</span><span class="identifier">S2</span><span class="plain">));</span>
<span class="identifier">P</span><span class="plain"> = </span><span class="functiontext">Str::forward</span><span class="plain">(</span><span class="identifier">P</span><span class="plain">), </span><span class="identifier">Q</span><span class="plain"> = </span><span class="functiontext">Str::forward</span><span class="plain">(</span><span class="identifier">Q</span><span class="plain">)) {</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">d</span><span class="plain"> = </span><span class="identifier">tolower</span><span class="plain">((</span><span class="reserved">int</span><span class="plain">) </span><span class="functiontext">Str::get</span><span class="plain">(</span><span class="identifier">P</span><span class="plain">)) - </span><span class="identifier">tolower</span><span class="plain">((</span><span class="reserved">int</span><span class="plain">) </span><span class="functiontext">Str::get</span><span class="plain">(</span><span class="identifier">Q</span><span class="plain">));</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">d</span><span class="plain"> != 0) </span><span class="reserved">return</span><span class="plain"> </span><span class="identifier">d</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">return</span><span class="plain"> </span><span class="functiontext">Str::len</span><span class="plain">(</span><span class="identifier">S1</span><span class="plain">) - </span><span class="functiontext">Str::len</span><span class="plain">(</span><span class="identifier">S2</span><span class="plain">);</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Str::cmp is used in <a href="#SP18">&#167;18</a>, 7/vn (<a href="7-vn.html#SP8">&#167;8</a>).</p>
<p class="endnote">The function Str::cmp_insensitive is used in <a href="#SP18">&#167;18</a>, 3/cla (<a href="3-cla.html#SP15">&#167;15</a>).</p>
<p class="inwebparagraph"><a id="SP20"></a><b>&#167;20. </b>It's sometimes useful to see whether two strings agree on their last
<code class="display"><span class="extract">N</span></code> characters, or their first <code class="display"><span class="extract">N</span></code>. For example,
</p>
<p class="inwebparagraph"></p>
<pre class="display">
<span class="plain">Str::suffix_eq(I"wayzgoose", I"snow goose", N)</span>
</pre>
<p class="inwebparagraph">will return <code class="display"><span class="extract">TRUE</span></code> for <code class="display"><span class="extract">N</span></code> equal to 0 to 5, and <code class="display"><span class="extract">FALSE</span></code> thereafter.
</p>
<p class="inwebparagraph">(The Oxford English Dictionary defines a "wayzgoose" as a holiday outing
for the staff of a publishing house.)
</p>
<pre class="display">
<span class="reserved">int</span><span class="plain"> </span><span class="functiontext">Str::prefix_eq</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S1</span><span class="plain">, </span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S2</span><span class="plain">, </span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">N</span><span class="plain">) {</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">L1</span><span class="plain"> = </span><span class="functiontext">Str::len</span><span class="plain">(</span><span class="identifier">S1</span><span class="plain">), </span><span class="identifier">L2</span><span class="plain"> = </span><span class="functiontext">Str::len</span><span class="plain">(</span><span class="identifier">S2</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> ((</span><span class="identifier">N</span><span class="plain"> &gt; </span><span class="identifier">L1</span><span class="plain">) || (</span><span class="identifier">N</span><span class="plain"> &gt; </span><span class="identifier">L2</span><span class="plain">)) </span><span class="reserved">return</span><span class="plain"> </span><span class="constant">FALSE</span><span class="plain">;</span>
<span class="reserved">for</span><span class="plain"> (</span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">i</span><span class="plain">=0; </span><span class="identifier">i</span><span class="plain">&lt;</span><span class="identifier">N</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="functiontext">Str::get_at</span><span class="plain">(</span><span class="identifier">S1</span><span class="plain">, </span><span class="identifier">i</span><span class="plain">) != </span><span class="functiontext">Str::get_at</span><span class="plain">(</span><span class="identifier">S2</span><span class="plain">, </span><span class="identifier">i</span><span class="plain">))</span>
<span class="reserved">return</span><span class="plain"> </span><span class="constant">FALSE</span><span class="plain">;</span>
<span class="reserved">return</span><span class="plain"> </span><span class="constant">TRUE</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">int</span><span class="plain"> </span><span class="functiontext">Str::suffix_eq</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S1</span><span class="plain">, </span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S2</span><span class="plain">, </span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">N</span><span class="plain">) {</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">L1</span><span class="plain"> = </span><span class="functiontext">Str::len</span><span class="plain">(</span><span class="identifier">S1</span><span class="plain">), </span><span class="identifier">L2</span><span class="plain"> = </span><span class="functiontext">Str::len</span><span class="plain">(</span><span class="identifier">S2</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> ((</span><span class="identifier">N</span><span class="plain"> &gt; </span><span class="identifier">L1</span><span class="plain">) || (</span><span class="identifier">N</span><span class="plain"> &gt; </span><span class="identifier">L2</span><span class="plain">)) </span><span class="reserved">return</span><span class="plain"> </span><span class="constant">FALSE</span><span class="plain">;</span>
<span class="reserved">for</span><span class="plain"> (</span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">i</span><span class="plain">=1; </span><span class="identifier">i</span><span class="plain">&lt;=</span><span class="identifier">N</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="functiontext">Str::get_at</span><span class="plain">(</span><span class="identifier">S1</span><span class="plain">, </span><span class="identifier">L1</span><span class="plain">-</span><span class="identifier">i</span><span class="plain">) != </span><span class="functiontext">Str::get_at</span><span class="plain">(</span><span class="identifier">S2</span><span class="plain">, </span><span class="identifier">L2</span><span class="plain">-</span><span class="identifier">i</span><span class="plain">))</span>
<span class="reserved">return</span><span class="plain"> </span><span class="constant">FALSE</span><span class="plain">;</span>
<span class="reserved">return</span><span class="plain"> </span><span class="constant">TRUE</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">int</span><span class="plain"> </span><span class="functiontext">Str::begins_with_wide_string</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S</span><span class="plain">, </span><span class="identifier">wchar_t</span><span class="plain"> *</span><span class="identifier">prefix</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> ((</span><span class="identifier">prefix</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) || (*</span><span class="identifier">prefix</span><span class="plain"> == 0)) </span><span class="reserved">return</span><span class="plain"> </span><span class="constant">TRUE</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">S</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) </span><span class="reserved">return</span><span class="plain"> </span><span class="constant">FALSE</span><span class="plain">;</span>
<span class="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">prefix</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="functiontext">Str::get_at</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">, </span><span class="identifier">i</span><span class="plain">) != </span><span class="identifier">prefix</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">])</span>
<span class="reserved">return</span><span class="plain"> </span><span class="constant">FALSE</span><span class="plain">;</span>
<span class="reserved">return</span><span class="plain"> </span><span class="constant">TRUE</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">int</span><span class="plain"> </span><span class="functiontext">Str::ends_with_wide_string</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S</span><span class="plain">, </span><span class="identifier">wchar_t</span><span class="plain"> *</span><span class="identifier">suffix</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> ((</span><span class="identifier">suffix</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) || (*</span><span class="identifier">suffix</span><span class="plain"> == 0)) </span><span class="reserved">return</span><span class="plain"> </span><span class="constant">TRUE</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">S</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) </span><span class="reserved">return</span><span class="plain"> </span><span class="constant">FALSE</span><span class="plain">;</span>
<span class="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">at</span><span class="plain"> = </span><span class="functiontext">Str::len</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">) - (</span><span class="reserved">int</span><span class="plain">) </span><span class="identifier">wcslen</span><span class="plain">(</span><span class="identifier">suffix</span><span class="plain">); </span><span class="identifier">suffix</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="functiontext">Str::get_at</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">, </span><span class="identifier">at</span><span class="plain">+</span><span class="identifier">i</span><span class="plain">) != </span><span class="identifier">suffix</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">])</span>
<span class="reserved">return</span><span class="plain"> </span><span class="constant">FALSE</span><span class="plain">;</span>
<span class="reserved">return</span><span class="plain"> </span><span class="constant">TRUE</span><span class="plain">;</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Str::prefix_eq is used in 3/pth (<a href="3-pth.html#SP7">&#167;7</a>), 3/fln (<a href="3-fln.html#SP5">&#167;5</a>).</p>
<p class="endnote">The function Str::suffix_eq appears nowhere else.</p>
<p class="endnote">The function Str::begins_with_wide_string is used in 3/cla (<a href="3-cla.html#SP5_1">&#167;5.1</a>).</p>
<p class="endnote">The function Str::ends_with_wide_string appears nowhere else.</p>
<p class="inwebparagraph"><a id="SP21"></a><b>&#167;21. </b></p>
<pre class="display">
<span class="reserved">int</span><span class="plain"> </span><span class="functiontext">Str::eq_wide_string</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S1</span><span class="plain">, </span><span class="identifier">wchar_t</span><span class="plain"> *</span><span class="identifier">S2</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="functiontext">Str::len</span><span class="plain">(</span><span class="identifier">S1</span><span class="plain">) == (</span><span class="reserved">int</span><span class="plain">) </span><span class="identifier">wcslen</span><span class="plain">(</span><span class="identifier">S2</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">LOOP_THROUGH_TEXT</span><span class="plain">(</span><span class="identifier">P</span><span class="plain">, </span><span class="identifier">S1</span><span class="plain">)</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="functiontext">Str::get</span><span class="plain">(</span><span class="identifier">P</span><span class="plain">) != </span><span class="identifier">S2</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">++])</span>
<span class="reserved">return</span><span class="plain"> </span><span class="constant">FALSE</span><span class="plain">;</span>
<span class="reserved">return</span><span class="plain"> </span><span class="constant">TRUE</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">return</span><span class="plain"> </span><span class="constant">FALSE</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">int</span><span class="plain"> </span><span class="functiontext">Str::eq_narrow_string</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S1</span><span class="plain">, </span><span class="reserved">char</span><span class="plain"> *</span><span class="identifier">S2</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="functiontext">Str::len</span><span class="plain">(</span><span class="identifier">S1</span><span class="plain">) == (</span><span class="reserved">int</span><span class="plain">) </span><span class="identifier">strlen</span><span class="plain">(</span><span class="identifier">S2</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">LOOP_THROUGH_TEXT</span><span class="plain">(</span><span class="identifier">P</span><span class="plain">, </span><span class="identifier">S1</span><span class="plain">)</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="functiontext">Str::get</span><span class="plain">(</span><span class="identifier">P</span><span class="plain">) != (</span><span class="identifier">wchar_t</span><span class="plain">) </span><span class="identifier">S2</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">++])</span>
<span class="reserved">return</span><span class="plain"> </span><span class="constant">FALSE</span><span class="plain">;</span>
<span class="reserved">return</span><span class="plain"> </span><span class="constant">TRUE</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">return</span><span class="plain"> </span><span class="constant">FALSE</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">int</span><span class="plain"> </span><span class="functiontext">Str::ne_wide_string</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S1</span><span class="plain">, </span><span class="identifier">wchar_t</span><span class="plain"> *</span><span class="identifier">S2</span><span class="plain">) {</span>
<span class="reserved">return</span><span class="plain"> (</span><span class="functiontext">Str::eq_wide_string</span><span class="plain">(</span><span class="identifier">S1</span><span class="plain">, </span><span class="identifier">S2</span><span class="plain">)?</span><span class="constant">FALSE</span><span class="plain">:</span><span class="constant">TRUE</span><span class="plain">);</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Str::eq_wide_string is used in 2/dl (<a href="2-dl.html#SP9">&#167;9</a>), 3/fln (<a href="3-fln.html#SP9">&#167;9</a>), 5/ee (<a href="5-ee.html#SP5">&#167;5</a>, <a href="5-ee.html#SP7_2_1">&#167;7.2.1</a>, <a href="5-ee.html#SP7_3_2_1">&#167;7.3.2.1</a>).</p>
<p class="endnote">The function Str::eq_narrow_string appears nowhere else.</p>
<p class="endnote">The function Str::ne_wide_string appears nowhere else.</p>
<p class="inwebparagraph"><a id="SP22"></a><b>&#167;22. White space. </b></p>
<pre class="display">
<span class="reserved">int</span><span class="plain"> </span><span class="functiontext">Str::is_whitespace</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S</span><span class="plain">) {</span>
<span class="identifier">LOOP_THROUGH_TEXT</span><span class="plain">(</span><span class="identifier">pos</span><span class="plain">, </span><span class="identifier">S</span><span class="plain">)</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="functiontext">Characters::is_space_or_tab</span><span class="plain">(</span><span class="functiontext">Str::get</span><span class="plain">(</span><span class="identifier">pos</span><span class="plain">)) == </span><span class="constant">FALSE</span><span class="plain">)</span>
<span class="reserved">return</span><span class="plain"> </span><span class="constant">FALSE</span><span class="plain">;</span>
<span class="reserved">return</span><span class="plain"> </span><span class="constant">TRUE</span><span class="plain">;</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Str::is_whitespace is used in 3/cla (<a href="3-cla.html#SP11">&#167;11</a>).</p>
<p class="inwebparagraph"><a id="SP23"></a><b>&#167;23. </b>This removes spaces and tabs from both ends:
</p>
<pre class="display">
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Str::trim_white_space</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S</span><span class="plain">) {</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">len</span><span class="plain"> = </span><span class="functiontext">Str::len</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">), </span><span class="identifier">i</span><span class="plain"> = 0, </span><span class="identifier">j</span><span class="plain"> = 0;</span>
<span class="reserved">string_position</span><span class="plain"> </span><span class="identifier">F</span><span class="plain"> = </span><span class="functiontext">Str::start</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">);</span>
<span class="identifier">LOOP_THROUGH_TEXT</span><span class="plain">(</span><span class="identifier">P</span><span class="plain">, </span><span class="identifier">S</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (!(</span><span class="functiontext">Characters::is_space_or_tab</span><span class="plain">(</span><span class="functiontext">Str::get</span><span class="plain">(</span><span class="identifier">P</span><span class="plain">)))) { </span><span class="identifier">F</span><span class="plain"> = </span><span class="identifier">P</span><span class="plain">; </span><span class="reserved">break</span><span class="plain">; }</span>
<span class="identifier">i</span><span class="plain">++;</span>
<span class="plain">}</span>
<span class="identifier">LOOP_BACKWARDS_THROUGH_TEXT</span><span class="plain">(</span><span class="identifier">Q</span><span class="plain">, </span><span class="identifier">S</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (!(</span><span class="functiontext">Characters::is_space_or_tab</span><span class="plain">(</span><span class="functiontext">Str::get</span><span class="plain">(</span><span class="identifier">Q</span><span class="plain">)))) </span><span class="reserved">break</span><span class="plain">;</span>
<span class="identifier">j</span><span class="plain">++;</span>
<span class="plain">}</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">i</span><span class="plain">+</span><span class="identifier">j</span><span class="plain"> &gt; </span><span class="functiontext">Str::len</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">)) </span><span class="functiontext">Str::truncate</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">, 0);</span>
<span class="reserved">else</span><span class="plain"> {</span>
<span class="identifier">len</span><span class="plain"> = </span><span class="identifier">len</span><span class="plain"> - </span><span class="identifier">j</span><span class="plain">;</span>
<span class="functiontext">Str::truncate</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">, </span><span class="identifier">len</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">i</span><span class="plain"> &gt; 0) {</span>
<span class="reserved">string_position</span><span class="plain"> </span><span class="identifier">P</span><span class="plain"> = </span><span class="functiontext">Str::start</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">);</span>
<span class="identifier">wchar_t</span><span class="plain"> </span><span class="identifier">c</span><span class="plain"> = 0;</span>
<span class="reserved">do</span><span class="plain"> {</span>
<span class="identifier">c</span><span class="plain"> = </span><span class="functiontext">Str::get</span><span class="plain">(</span><span class="identifier">F</span><span class="plain">);</span>
<span class="functiontext">Str::put</span><span class="plain">(</span><span class="identifier">P</span><span class="plain">, </span><span class="identifier">c</span><span class="plain">);</span>
<span class="identifier">P</span><span class="plain"> = </span><span class="functiontext">Str::forward</span><span class="plain">(</span><span class="identifier">P</span><span class="plain">); </span><span class="identifier">F</span><span class="plain"> = </span><span class="functiontext">Str::forward</span><span class="plain">(</span><span class="identifier">F</span><span class="plain">);</span>
<span class="plain">} </span><span class="reserved">while</span><span class="plain"> (</span><span class="identifier">c</span><span class="plain"> != 0);</span>
<span class="identifier">len</span><span class="plain"> = </span><span class="identifier">len</span><span class="plain"> - </span><span class="identifier">i</span><span class="plain">;</span>
<span class="functiontext">Str::truncate</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">, </span><span class="identifier">len</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="reserved">int</span><span class="plain"> </span><span class="functiontext">Str::trim_white_space_at_end</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S</span><span class="plain">) {</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">shortened</span><span class="plain"> = </span><span class="constant">FALSE</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="functiontext">Str::len</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">)-1; </span><span class="identifier">j</span><span class="plain"> &gt;= 0; </span><span class="identifier">j</span><span class="plain">--) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="functiontext">Characters::is_space_or_tab</span><span class="plain">(</span><span class="functiontext">Str::get_at</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">, </span><span class="identifier">j</span><span class="plain">))) {</span>
<span class="functiontext">Str::truncate</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">, </span><span class="identifier">j</span><span class="plain">);</span>
<span class="identifier">shortened</span><span class="plain"> = </span><span class="constant">TRUE</span><span class="plain">;</span>
<span class="plain">} </span><span class="reserved">else</span><span class="plain"> </span><span class="reserved">break</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">return</span><span class="plain"> </span><span class="identifier">shortened</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">int</span><span class="plain"> </span><span class="functiontext">Str::trim_all_white_space_at_end</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S</span><span class="plain">) {</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">shortened</span><span class="plain"> = </span><span class="constant">FALSE</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="functiontext">Str::len</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">)-1; </span><span class="identifier">j</span><span class="plain"> &gt;= 0; </span><span class="identifier">j</span><span class="plain">--) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="functiontext">Characters::is_babel_whitespace</span><span class="plain">(</span><span class="functiontext">Str::get_at</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">, </span><span class="identifier">j</span><span class="plain">))) {</span>
<span class="functiontext">Str::truncate</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">, </span><span class="identifier">j</span><span class="plain">);</span>
<span class="identifier">shortened</span><span class="plain"> = </span><span class="constant">TRUE</span><span class="plain">;</span>
<span class="plain">} </span><span class="reserved">else</span><span class="plain"> </span><span class="reserved">break</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">return</span><span class="plain"> </span><span class="identifier">shortened</span><span class="plain">;</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Str::trim_white_space appears nowhere else.</p>
<p class="endnote">The function Str::trim_white_space_at_end appears nowhere else.</p>
<p class="endnote">The function Str::trim_all_white_space_at_end appears nowhere else.</p>
<p class="inwebparagraph"><a id="SP24"></a><b>&#167;24. Deleting characters. </b></p>
<pre class="display">
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Str::delete_first_character</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S</span><span class="plain">) {</span>
<span class="functiontext">Str::delete_nth_character</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">, 0);</span>
<span class="plain">}</span>
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Str::delete_last_character</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="functiontext">Str::len</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">) &gt; 0)</span>
<span class="functiontext">Str::truncate</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">, </span><span class="functiontext">Str::len</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">) - 1);</span>
<span class="plain">}</span>
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Str::delete_nth_character</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S</span><span class="plain">, </span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">n</span><span class="plain">) {</span>
<span class="reserved">for</span><span class="plain"> (</span><span class="reserved">string_position</span><span class="plain"> </span><span class="identifier">P</span><span class="plain"> = </span><span class="functiontext">Str::at</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">, </span><span class="identifier">n</span><span class="plain">); </span><span class="identifier">P</span><span class="element">.index</span><span class="plain"> &lt; </span><span class="functiontext">Str::len</span><span class="plain">(</span><span class="identifier">P</span><span class="element">.S</span><span class="plain">); </span><span class="identifier">P</span><span class="plain"> = </span><span class="functiontext">Str::forward</span><span class="plain">(</span><span class="identifier">P</span><span class="plain">))</span>
<span class="functiontext">Str::put</span><span class="plain">(</span><span class="identifier">P</span><span class="plain">, </span><span class="functiontext">Str::get</span><span class="plain">(</span><span class="functiontext">Str::forward</span><span class="plain">(</span><span class="identifier">P</span><span class="plain">)));</span>
<span class="plain">}</span>
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Str::delete_n_characters</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S</span><span class="plain">, </span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">n</span><span class="plain">) {</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">L</span><span class="plain"> = </span><span class="functiontext">Str::len</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">) - </span><span class="identifier">n</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">L</span><span class="plain"> &lt;= 0) </span><span class="functiontext">Str::clear</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">);</span>
<span class="reserved">else</span><span class="plain"> {</span>
<span class="reserved">for</span><span class="plain"> (</span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">i</span><span class="plain">=0; </span><span class="identifier">i</span><span class="plain">&lt;</span><span class="identifier">L</span><span class="plain">; </span><span class="identifier">i</span><span class="plain">++)</span>
<span class="functiontext">Str::put</span><span class="plain">(</span><span class="functiontext">Str::at</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">, </span><span class="identifier">i</span><span class="plain">), </span><span class="functiontext">Str::get</span><span class="plain">(</span><span class="functiontext">Str::at</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">, </span><span class="identifier">i</span><span class="plain">+</span><span class="identifier">n</span><span class="plain">)));</span>
<span class="functiontext">Str::truncate</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">, </span><span class="identifier">L</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Str::delete_first_character appears nowhere else.</p>
<p class="endnote">The function Str::delete_last_character appears nowhere else.</p>
<p class="endnote">The function Str::delete_nth_character appears nowhere else.</p>
<p class="endnote">The function Str::delete_n_characters is used in 3/cla (<a href="3-cla.html#SP5_1">&#167;5.1</a>), 3/pth (<a href="3-pth.html#SP7">&#167;7</a>), 3/fln (<a href="3-fln.html#SP5">&#167;5</a>).</p>
<p class="inwebparagraph"><a id="SP25"></a><b>&#167;25. Substrings. </b></p>
<pre class="display">
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Str::substr</span><span class="plain">(</span><span class="constant">OUTPUT_STREAM</span><span class="plain">, </span><span class="reserved">string_position</span><span class="plain"> </span><span class="identifier">from</span><span class="plain">, </span><span class="reserved">string_position</span><span class="plain"> </span><span class="identifier">to</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">from</span><span class="element">.S</span><span class="plain"> != </span><span class="identifier">to</span><span class="element">.S</span><span class="plain">) </span><span class="identifier">internal_error</span><span class="plain">(</span><span class="string">"substr on two different strings"</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"> = </span><span class="identifier">from</span><span class="element">.index</span><span class="plain">; </span><span class="identifier">i</span><span class="plain"> &lt; </span><span class="identifier">to</span><span class="element">.index</span><span class="plain">; </span><span class="identifier">i</span><span class="plain">++)</span>
<span class="identifier">PUT</span><span class="plain">(</span><span class="functiontext">Str::get_at</span><span class="plain">(</span><span class="identifier">from</span><span class="element">.S</span><span class="plain">, </span><span class="identifier">i</span><span class="plain">));</span>
<span class="plain">}</span>
<span class="reserved">int</span><span class="plain"> </span><span class="functiontext">Str::includes_character</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S</span><span class="plain">, </span><span class="identifier">wchar_t</span><span class="plain"> </span><span class="identifier">c</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">S</span><span class="plain">)</span>
<span class="identifier">LOOP_THROUGH_TEXT</span><span class="plain">(</span><span class="identifier">pos</span><span class="plain">, </span><span class="identifier">S</span><span class="plain">)</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="functiontext">Str::get</span><span class="plain">(</span><span class="identifier">pos</span><span class="plain">) == </span><span class="identifier">c</span><span class="plain">)</span>
<span class="reserved">return</span><span class="plain"> </span><span class="constant">TRUE</span><span class="plain">;</span>
<span class="reserved">return</span><span class="plain"> </span><span class="constant">FALSE</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">int</span><span class="plain"> </span><span class="functiontext">Str::includes_wide_string_at</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S</span><span class="plain">, </span><span class="identifier">wchar_t</span><span class="plain"> *</span><span class="identifier">prefix</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="reserved">if</span><span class="plain"> ((</span><span class="identifier">prefix</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) || (*</span><span class="identifier">prefix</span><span class="plain"> == 0)) </span><span class="reserved">return</span><span class="plain"> </span><span class="constant">TRUE</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">S</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) </span><span class="reserved">return</span><span class="plain"> </span><span class="constant">FALSE</span><span class="plain">;</span>
<span class="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">prefix</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="functiontext">Str::get_at</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">, </span><span class="identifier">i</span><span class="plain">+</span><span class="identifier">j</span><span class="plain">) != </span><span class="identifier">prefix</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">])</span>
<span class="reserved">return</span><span class="plain"> </span><span class="constant">FALSE</span><span class="plain">;</span>
<span class="reserved">return</span><span class="plain"> </span><span class="constant">TRUE</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">int</span><span class="plain"> </span><span class="functiontext">Str::includes_wide_string_at_insensitive</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S</span><span class="plain">, </span><span class="identifier">wchar_t</span><span class="plain"> *</span><span class="identifier">prefix</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="reserved">if</span><span class="plain"> ((</span><span class="identifier">prefix</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) || (*</span><span class="identifier">prefix</span><span class="plain"> == 0)) </span><span class="reserved">return</span><span class="plain"> </span><span class="constant">TRUE</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">S</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) </span><span class="reserved">return</span><span class="plain"> </span><span class="constant">FALSE</span><span class="plain">;</span>
<span class="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">prefix</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="functiontext">Characters::tolower</span><span class="plain">(</span><span class="functiontext">Str::get_at</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">, </span><span class="identifier">i</span><span class="plain">+</span><span class="identifier">j</span><span class="plain">)) != </span><span class="functiontext">Characters::tolower</span><span class="plain">(</span><span class="identifier">prefix</span><span class="plain">[</span><span class="identifier">i</span><span class="plain">]))</span>
<span class="reserved">return</span><span class="plain"> </span><span class="constant">FALSE</span><span class="plain">;</span>
<span class="reserved">return</span><span class="plain"> </span><span class="constant">TRUE</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">int</span><span class="plain"> </span><span class="functiontext">Str::includes</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">S</span><span class="plain">, </span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">T</span><span class="plain">) {</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">LS</span><span class="plain"> = </span><span class="functiontext">Str::len</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">);</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">LT</span><span class="plain"> = </span><span class="functiontext">Str::len</span><span class="plain">(</span><span class="identifier">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">i</span><span class="plain">=0; </span><span class="identifier">i</span><span class="plain">&lt;</span><span class="identifier">LS</span><span class="plain">-</span><span class="identifier">LT</span><span class="plain">; </span><span class="identifier">i</span><span class="plain">++) {</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">failed</span><span class="plain"> = </span><span class="constant">FALSE</span><span class="plain">;</span>
<span class="reserved">for</span><span class="plain"> (</span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">j</span><span class="plain">=0; </span><span class="identifier">j</span><span class="plain">&lt;</span><span class="identifier">LT</span><span class="plain">; </span><span class="identifier">j</span><span class="plain">++)</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="functiontext">Str::get_at</span><span class="plain">(</span><span class="identifier">S</span><span class="plain">, </span><span class="identifier">i</span><span class="plain">+</span><span class="identifier">j</span><span class="plain">) != </span><span class="functiontext">Str::get_at</span><span class="plain">(</span><span class="identifier">T</span><span class="plain">, </span><span class="identifier">j</span><span class="plain">)) {</span>
<span class="identifier">failed</span><span class="plain"> = </span><span class="constant">TRUE</span><span class="plain">;</span>
<span class="reserved">break</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">failed</span><span class="plain"> == </span><span class="constant">FALSE</span><span class="plain">) </span><span class="reserved">return</span><span class="plain"> </span><span class="constant">TRUE</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">return</span><span class="plain"> </span><span class="constant">FALSE</span><span class="plain">;</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Str::substr is used in 3/fln (<a href="3-fln.html#SP3">&#167;3</a>).</p>
<p class="endnote">The function Str::includes_character appears nowhere else.</p>
<p class="endnote">The function Str::includes_wide_string_at appears nowhere else.</p>
<p class="endnote">The function Str::includes_wide_string_at_insensitive appears nowhere else.</p>
<p class="endnote">The function Str::includes appears nowhere else.</p>
<p class="inwebparagraph"><a id="SP26"></a><b>&#167;26. Shim for literal storage. </b>This is where all of those I-literals created by Inweb are stored at run-time.
Note that every instance of, say, <code class="display"><span class="extract">I"fish"</span></code> would return the same string,
that is, the same <code class="display"><span class="extract">text_stream *</span></code> value. To prevent nasty accidents, this
is marked so that the stream value, "fish", cannot be modified at run-time.
</p>
<p class="inwebparagraph">The dictionary look-up here would not be thread-safe, so it's protected by
a mutex. There's no real performance concern because the following routine
is run just once per I-literal in the source code, when the program starts up.
</p>
<pre class="display">
<span class="reserved">dictionary</span><span class="plain"> *</span><span class="identifier">string_literals_dictionary</span><span class="plain"> = </span><span class="identifier">NULL</span><span class="plain">;</span>
<span class="reserved">text_stream</span><span class="plain"> *</span><span class="functiontext">Str::literal</span><span class="plain">(</span><span class="identifier">wchar_t</span><span class="plain"> *</span><span class="identifier">wide_C_string</span><span class="plain">) {</span>
<span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">answer</span><span class="plain"> = </span><span class="identifier">NULL</span><span class="plain">;</span>
<span class="identifier">CREATE_MUTEX</span><span class="plain">(</span><span class="identifier">mutex</span><span class="plain">);</span>
<span class="identifier">LOCK_MUTEX</span><span class="plain">(</span><span class="identifier">mutex</span><span class="plain">);</span>
&lt;<span class="cwebmacro">Look in dictionary of string literals</span> <span class="cwebmacronumber">26.1</span>&gt;<span class="plain">;</span>
<span class="identifier">UNLOCK_MUTEX</span><span class="plain">(</span><span class="identifier">mutex</span><span class="plain">);</span>
<span class="reserved">return</span><span class="plain"> </span><span class="identifier">answer</span><span class="plain">;</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Str::literal appears nowhere else.</p>
<p class="inwebparagraph"><a id="SP26_1"></a><b>&#167;26.1. </b><code class="display">
&lt;<span class="cwebmacrodefn">Look in dictionary of string literals</span> <span class="cwebmacronumber">26.1</span>&gt; =
</code></p>
<pre class="displaydefn">
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">string_literals_dictionary</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">)</span>
<span class="identifier">string_literals_dictionary</span><span class="plain"> = </span><span class="functiontext">Dictionaries::new</span><span class="plain">(100, </span><span class="constant">TRUE</span><span class="plain">);</span>
<span class="identifier">answer</span><span class="plain"> = </span><span class="functiontext">Dictionaries::get_text_literal</span><span class="plain">(</span><span class="identifier">string_literals_dictionary</span><span class="plain">, </span><span class="identifier">wide_C_string</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">answer</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) {</span>
<span class="functiontext">Dictionaries::create_literal</span><span class="plain">(</span><span class="identifier">string_literals_dictionary</span><span class="plain">, </span><span class="identifier">wide_C_string</span><span class="plain">);</span>
<span class="identifier">answer</span><span class="plain"> = </span><span class="functiontext">Dictionaries::get_text_literal</span><span class="plain">(</span><span class="identifier">string_literals_dictionary</span><span class="plain">, </span><span class="identifier">wide_C_string</span><span class="plain">);</span>
<span class="identifier">WRITE_TO</span><span class="plain">(</span><span class="identifier">answer</span><span class="plain">, </span><span class="string">"%w"</span><span class="plain">, </span><span class="identifier">wide_C_string</span><span class="plain">);</span>
<span class="functiontext">Streams::mark_as_read_only</span><span class="plain">(</span><span class="identifier">answer</span><span class="plain">);</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">This code is used in <a href="#SP26">&#167;26</a>.</p>
<hr class="tocbar">
<ul class="toc"><li><a href="4-ws.html">Back to 'Wide Strings.w'</a></li><li><a href="4-tf.html">Continue with 'Text Files'</a></li></ul><hr class="tocbar">
<!--End of weave-->
</main>
</body>
</html>