inweb-bootstrap/docs/foundation-module/3-fln.html
2019-02-10 23:08:53 +00:00

358 lines
51 KiB
HTML

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>3/pth</title>
<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>
<!--Weave of '3/fln' generated by 7-->
<ul class="crumbs"><li><a href="../webs.html">&#9733;</a></li><li><a href="index.html">foundation</a></li><li><a href="index.html#3">Chapter 3: The Operating System</a></li><li><b>Filenames</b></li></ul><p class="purpose">Given the different environments in which we might be running, and the large range of files we may need from within a project folder, it's useful to have a section of code simply to deduce filenames.</p>
<ul class="toc"><li><a href="#SP1">&#167;1. Storage</a></li><li><a href="#SP2">&#167;2. Creation</a></li><li><a href="#SP3">&#167;3. Strings to filenames</a></li><li><a href="#SP4">&#167;4. The writer</a></li><li><a href="#SP6">&#167;6. Reading off the folder</a></li><li><a href="#SP7">&#167;7. Reading off the leafname</a></li><li><a href="#SP8">&#167;8. Filename extensions</a></li><li><a href="#SP9">&#167;9. Guessing file formats</a></li><li><a href="#SP10">&#167;10. Opening</a></li><li><a href="#SP11">&#167;11. Comparing</a></li></ul><hr class="tocbar">
<p class="inwebparagraph"><a id="SP1"></a><b>&#167;1. Storage. </b>Filename objects behave much like pathname ones, but they have their own
structure in order that the two are distinct C types. Individual filenames
are a single instance of the following. (Note that the text part is stored
as Unicode code points, regardless of what text encoding the locale has.)
</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">filename</span><span class="plain"> {</span>
<span class="reserved">struct</span><span class="plain"> </span><span class="reserved">pathname</span><span class="plain"> *</span><span class="identifier">pathname_of_location</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">leafname</span><span class="plain">;</span>
<span class="constant">MEMORY_MANAGEMENT</span>
<span class="plain">} </span><span class="reserved">filename</span><span class="plain">;</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The structure filename is private to this section.</p>
<p class="inwebparagraph"><a id="SP2"></a><b>&#167;2. Creation. </b>A filename is made by supplying a pathname and a leafname.
</p>
<pre class="display">
<span class="reserved">filename</span><span class="plain"> *</span><span class="functiontext">Filenames::in_folder</span><span class="plain">(</span><span class="reserved">pathname</span><span class="plain"> *</span><span class="identifier">P</span><span class="plain">, </span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">file_name</span><span class="plain">) {</span>
<span class="reserved">return</span><span class="plain"> </span><span class="functiontext">Filenames::primitive</span><span class="plain">(</span><span class="identifier">file_name</span><span class="plain">, 0, </span><span class="functiontext">Str::len</span><span class="plain">(</span><span class="identifier">file_name</span><span class="plain">), </span><span class="identifier">P</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="reserved">filename</span><span class="plain"> *</span><span class="functiontext">Filenames::primitive</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">from</span><span class="plain">, </span><span class="reserved">int</span><span class="plain"> </span><span class="identifier">to</span><span class="plain">, </span><span class="reserved">pathname</span><span class="plain"> *</span><span class="identifier">P</span><span class="plain">) {</span>
<span class="reserved">filename</span><span class="plain"> *</span><span class="identifier">F</span><span class="plain"> = </span><span class="identifier">CREATE</span><span class="plain">(</span><span class="reserved">filename</span><span class="plain">);</span>
<span class="identifier">F</span><span class="plain">-</span><span class="element">&gt;pathname_of_location</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">to</span><span class="plain">-</span><span class="identifier">from</span><span class="plain"> &lt;= 0)</span>
<span class="identifier">internal_error</span><span class="plain">(</span><span class="string">"empty intermediate pathname"</span><span class="plain">);</span>
<span class="identifier">F</span><span class="plain">-</span><span class="element">&gt;leafname</span><span class="plain"> = </span><span class="functiontext">Str::new_with_capacity</span><span class="plain">(</span><span class="identifier">to</span><span class="plain">-</span><span class="identifier">from</span><span class="plain">+1);</span>
<span class="reserved">string_position</span><span class="plain"> </span><span class="identifier">pos</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">from</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="plain">; </span><span class="identifier">i</span><span class="plain"> &lt; </span><span class="identifier">to</span><span class="plain">; </span><span class="identifier">i</span><span class="plain">++, </span><span class="identifier">pos</span><span class="plain"> = </span><span class="functiontext">Str::forward</span><span class="plain">(</span><span class="identifier">pos</span><span class="plain">))</span>
<span class="identifier">PUT_TO</span><span class="plain">(</span><span class="identifier">F</span><span class="plain">-</span><span class="element">&gt;leafname</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="reserved">return</span><span class="plain"> </span><span class="identifier">F</span><span class="plain">;</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Filenames::in_folder is used in <a href="#SP7">&#167;7</a>, <a href="#SP8">&#167;8</a>, 3/cla (<a href="3-cla.html#SP8_1_1">&#167;8.1.1</a>), 5/ee (<a href="5-ee.html#SP6">&#167;6</a>, <a href="5-ee.html#SP6_1">&#167;6.1</a>, <a href="5-ee.html#SP6_2">&#167;6.2</a>, <a href="5-ee.html#SP6_3">&#167;6.3</a>, <a href="5-ee.html#SP7_2">&#167;7.2</a>, <a href="5-ee.html#SP7_3">&#167;7.3</a>, <a href="5-ee.html#SP7_4">&#167;7.4</a>).</p>
<p class="endnote">The function Filenames::primitive is used in <a href="#SP3">&#167;3</a>.</p>
<p class="inwebparagraph"><a id="SP3"></a><b>&#167;3. Strings to filenames. </b>The following takes a textual name and returns a filename.
</p>
<pre class="display">
<span class="reserved">filename</span><span class="plain"> *</span><span class="functiontext">Filenames::from_text</span><span class="plain">(</span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">path</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">pos</span><span class="plain"> = -1;</span>
<span class="identifier">LOOP_THROUGH_TEXT</span><span class="plain">(</span><span class="identifier">at</span><span class="plain">, </span><span class="identifier">path</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">at</span><span class="plain">) == </span><span class="constant">FOLDER_SEPARATOR</span><span class="plain">) </span><span class="identifier">pos</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="plain">}</span>
<span class="reserved">pathname</span><span class="plain"> *</span><span class="identifier">P</span><span class="plain"> = </span><span class="identifier">NULL</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">pos</span><span class="plain"> &gt;= 0) {</span>
<span class="identifier">TEMPORARY_TEXT</span><span class="plain">(</span><span class="identifier">PT</span><span class="plain">);</span>
<span class="functiontext">Str::substr</span><span class="plain">(</span><span class="identifier">PT</span><span class="plain">, </span><span class="functiontext">Str::at</span><span class="plain">(</span><span class="identifier">path</span><span class="plain">, 0), </span><span class="functiontext">Str::at</span><span class="plain">(</span><span class="identifier">path</span><span class="plain">, </span><span class="identifier">pos</span><span class="plain">));</span>
<span class="identifier">P</span><span class="plain"> = </span><span class="functiontext">Pathnames::from_text</span><span class="plain">(</span><span class="identifier">PT</span><span class="plain">);</span>
<span class="identifier">DISCARD_TEXT</span><span class="plain">(</span><span class="identifier">PT</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="reserved">return</span><span class="plain"> </span><span class="functiontext">Filenames::primitive</span><span class="plain">(</span><span class="identifier">path</span><span class="plain">, </span><span class="identifier">pos</span><span class="plain">+1, </span><span class="functiontext">Str::len</span><span class="plain">(</span><span class="identifier">path</span><span class="plain">), </span><span class="identifier">P</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="reserved">filename</span><span class="plain"> *</span><span class="functiontext">Filenames::from_text_relative</span><span class="plain">(</span><span class="reserved">pathname</span><span class="plain"> *</span><span class="identifier">from</span><span class="plain">, </span><span class="reserved">text_stream</span><span class="plain"> *</span><span class="identifier">path</span><span class="plain">) {</span>
<span class="reserved">filename</span><span class="plain"> *</span><span class="identifier">F</span><span class="plain"> = </span><span class="functiontext">Filenames::from_text</span><span class="plain">(</span><span class="identifier">path</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">from</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">F</span><span class="plain">-</span><span class="element">&gt;pathname_of_location</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) </span><span class="identifier">F</span><span class="plain">-</span><span class="element">&gt;pathname_of_location</span><span class="plain"> = </span><span class="identifier">from</span><span class="plain">;</span>
<span class="reserved">else</span><span class="plain"> {</span>
<span class="reserved">pathname</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="element">&gt;pathname_of_location</span><span class="plain">;</span>
<span class="reserved">while</span><span class="plain"> ((</span><span class="identifier">P</span><span class="plain">) &amp;&amp; (</span><span class="identifier">P</span><span class="plain">-</span><span class="element">&gt;pathname_of_parent</span><span class="plain">)) </span><span class="identifier">P</span><span class="plain"> = </span><span class="identifier">P</span><span class="plain">-</span><span class="element">&gt;pathname_of_parent</span><span class="plain">;</span>
<span class="identifier">P</span><span class="plain">-</span><span class="element">&gt;pathname_of_parent</span><span class="plain"> = </span><span class="identifier">from</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="reserved">return</span><span class="plain"> </span><span class="identifier">F</span><span class="plain">;</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Filenames::from_text is used in 3/pth (<a href="3-pth.html#SP3">&#167;3</a>).</p>
<p class="endnote">The function Filenames::from_text_relative appears nowhere else.</p>
<p class="inwebparagraph"><a id="SP4"></a><b>&#167;4. The writer. </b>And conversely:
</p>
<pre class="display">
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Filenames::writer</span><span class="plain">(</span><span class="constant">OUTPUT_STREAM</span><span class="plain">, </span><span class="reserved">char</span><span class="plain"> *</span><span class="identifier">format_string</span><span class="plain">, </span><span class="reserved">void</span><span class="plain"> *</span><span class="identifier">vF</span><span class="plain">) {</span>
<span class="reserved">filename</span><span class="plain"> *</span><span class="identifier">F</span><span class="plain"> = (</span><span class="reserved">filename</span><span class="plain"> *) </span><span class="identifier">vF</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">F</span><span class="plain"> == </span><span class="identifier">NULL</span><span class="plain">) </span><span class="identifier">WRITE</span><span class="plain">(</span><span class="string">"&lt;no file&gt;"</span><span class="plain">);</span>
<span class="reserved">else</span><span class="plain"> {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">F</span><span class="plain">-</span><span class="element">&gt;pathname_of_location</span><span class="plain">) {</span>
<span class="functiontext">Pathnames::writer</span><span class="plain">(</span><span class="identifier">OUT</span><span class="plain">, </span><span class="identifier">format_string</span><span class="plain">, (</span><span class="reserved">void</span><span class="plain"> *) </span><span class="identifier">F</span><span class="plain">-</span><span class="element">&gt;pathname_of_location</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">format_string</span><span class="plain">[0] == </span><span class="character">'/'</span><span class="plain">) </span><span class="identifier">PUT</span><span class="plain">(</span><span class="character">'/'</span><span class="plain">);</span>
<span class="reserved">else</span><span class="plain"> </span><span class="identifier">PUT</span><span class="plain">(</span><span class="constant">FOLDER_SEPARATOR</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="identifier">WRITE</span><span class="plain">(</span><span class="string">"%S"</span><span class="plain">, </span><span class="identifier">F</span><span class="plain">-</span><span class="element">&gt;leafname</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Filenames::writer is used in 1/fnd (<a href="1-fnd.html#SP8_1">&#167;8.1</a>).</p>
<p class="inwebparagraph"><a id="SP5"></a><b>&#167;5. </b>And again relative to a given pathname:
</p>
<pre class="display">
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Filenames::to_text_relative</span><span class="plain">(</span><span class="constant">OUTPUT_STREAM</span><span class="plain">, </span><span class="reserved">filename</span><span class="plain"> *</span><span class="identifier">F</span><span class="plain">, </span><span class="reserved">pathname</span><span class="plain"> *</span><span class="identifier">P</span><span class="plain">) {</span>
<span class="identifier">TEMPORARY_TEXT</span><span class="plain">(</span><span class="identifier">ft</span><span class="plain">);</span>
<span class="identifier">TEMPORARY_TEXT</span><span class="plain">(</span><span class="identifier">pt</span><span class="plain">);</span>
<span class="identifier">WRITE_TO</span><span class="plain">(</span><span class="identifier">ft</span><span class="plain">, </span><span class="string">"%f"</span><span class="plain">, </span><span class="identifier">F</span><span class="plain">);</span>
<span class="identifier">WRITE_TO</span><span class="plain">(</span><span class="identifier">pt</span><span class="plain">, </span><span class="string">"%p"</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">n</span><span class="plain"> = </span><span class="functiontext">Str::len</span><span class="plain">(</span><span class="identifier">pt</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> ((</span><span class="functiontext">Str::prefix_eq</span><span class="plain">(</span><span class="identifier">ft</span><span class="plain">, </span><span class="identifier">pt</span><span class="plain">, </span><span class="identifier">n</span><span class="plain">)) &amp;&amp; (</span><span class="functiontext">Str::get_at</span><span class="plain">(</span><span class="identifier">ft</span><span class="plain">, </span><span class="identifier">n</span><span class="plain">)==</span><span class="constant">FOLDER_SEPARATOR</span><span class="plain">)) {</span>
<span class="functiontext">Str::delete_n_characters</span><span class="plain">(</span><span class="identifier">ft</span><span class="plain">, </span><span class="identifier">n</span><span class="plain">+1);</span>
<span class="identifier">WRITE</span><span class="plain">(</span><span class="string">"%S"</span><span class="plain">, </span><span class="identifier">ft</span><span class="plain">);</span>
<span class="plain">} </span><span class="reserved">else</span><span class="plain"> </span><span class="identifier">internal_error</span><span class="plain">(</span><span class="string">"filename not relative to pathname"</span><span class="plain">);</span>
<span class="identifier">DISCARD_TEXT</span><span class="plain">(</span><span class="identifier">ft</span><span class="plain">);</span>
<span class="identifier">DISCARD_TEXT</span><span class="plain">(</span><span class="identifier">pt</span><span class="plain">);</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Filenames::to_text_relative appears nowhere else.</p>
<p class="inwebparagraph"><a id="SP6"></a><b>&#167;6. Reading off the folder. </b></p>
<pre class="display">
<span class="reserved">pathname</span><span class="plain"> *</span><span class="functiontext">Filenames::get_path_to</span><span class="plain">(</span><span class="reserved">filename</span><span class="plain"> *</span><span class="identifier">F</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">F</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">NULL</span><span class="plain">;</span>
<span class="reserved">return</span><span class="plain"> </span><span class="identifier">F</span><span class="plain">-</span><span class="element">&gt;pathname_of_location</span><span class="plain">;</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Filenames::get_path_to is used in 3/pth (<a href="3-pth.html#SP3">&#167;3</a>).</p>
<p class="inwebparagraph"><a id="SP7"></a><b>&#167;7. Reading off the leafname. </b></p>
<pre class="display">
<span class="reserved">filename</span><span class="plain"> *</span><span class="functiontext">Filenames::without_path</span><span class="plain">(</span><span class="reserved">filename</span><span class="plain"> *</span><span class="identifier">F</span><span class="plain">) {</span>
<span class="reserved">return</span><span class="plain"> </span><span class="functiontext">Filenames::in_folder</span><span class="plain">(</span><span class="identifier">NULL</span><span class="plain">, </span><span class="identifier">F</span><span class="plain">-</span><span class="element">&gt;leafname</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="reserved">text_stream</span><span class="plain"> *</span><span class="functiontext">Filenames::get_leafname</span><span class="plain">(</span><span class="reserved">filename</span><span class="plain"> *</span><span class="identifier">F</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">F</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">NULL</span><span class="plain">;</span>
<span class="reserved">return</span><span class="plain"> </span><span class="identifier">F</span><span class="plain">-</span><span class="element">&gt;leafname</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Filenames::write_unextended_leafname</span><span class="plain">(</span><span class="constant">OUTPUT_STREAM</span><span class="plain">, </span><span class="reserved">filename</span><span class="plain"> *</span><span class="identifier">F</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">F</span><span class="plain">-</span><span class="element">&gt;leafname</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::get</span><span class="plain">(</span><span class="identifier">pos</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">c</span><span class="plain"> == </span><span class="character">'.'</span><span class="plain">) </span><span class="reserved">return</span><span class="plain">;</span>
<span class="identifier">PUT</span><span class="plain">(</span><span class="identifier">c</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Filenames::without_path appears nowhere else.</p>
<p class="endnote">The function Filenames::get_leafname is used in 5/ee (<a href="5-ee.html#SP7_2_2_1">&#167;7.2.2.1</a>, <a href="5-ee.html#SP7_2_2_2">&#167;7.2.2.2</a>, <a href="5-ee.html#SP7_2_4">&#167;7.2.4</a>, <a href="5-ee.html#SP7_3_2">&#167;7.3.2</a>, <a href="5-ee.html#SP7_3_2_1">&#167;7.3.2.1</a>, <a href="5-ee.html#SP7_3_2_2_1">&#167;7.3.2.2.1</a>, <a href="5-ee.html#SP7_4">&#167;7.4</a>).</p>
<p class="endnote">The function Filenames::write_unextended_leafname is used in 5/ee (<a href="5-ee.html#SP5">&#167;5</a>).</p>
<p class="inwebparagraph"><a id="SP8"></a><b>&#167;8. Filename extensions. </b>The following is cautiously written because of an oddity in Windows's handling
of filenames, which are allowed to have trailing dots or spaces, in a way
which isn't necessarily visible to the user, who may have added these by
an accidental brush of the keyboard. Thus <code class="display"><span class="extract">frog.jpg .</span></code> should be treated
as equivalent to <code class="display"><span class="extract">frog.jpg</span></code> when deciding the likely file format.
</p>
<pre class="display">
<span class="reserved">void</span><span class="plain"> </span><span class="functiontext">Filenames::write_extension</span><span class="plain">(</span><span class="constant">OUTPUT_STREAM</span><span class="plain">, </span><span class="reserved">filename</span><span class="plain"> *</span><span class="identifier">F</span><span class="plain">) {</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">on</span><span class="plain"> = </span><span class="constant">FALSE</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">F</span><span class="plain">-</span><span class="element">&gt;leafname</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::get</span><span class="plain">(</span><span class="identifier">pos</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">c</span><span class="plain"> == </span><span class="character">'.'</span><span class="plain">) </span><span class="identifier">on</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">on</span><span class="plain">) </span><span class="identifier">PUT</span><span class="plain">(</span><span class="identifier">c</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="plain">}</span>
<span class="reserved">filename</span><span class="plain"> *</span><span class="functiontext">Filenames::set_extension</span><span class="plain">(</span><span class="reserved">filename</span><span class="plain"> *</span><span class="identifier">F</span><span class="plain">, </span><span class="reserved">char</span><span class="plain"> *</span><span class="identifier">extension</span><span class="plain">) {</span>
<span class="identifier">TEMPORARY_TEXT</span><span class="plain">(</span><span class="identifier">NEWLEAF</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">F</span><span class="plain">-</span><span class="element">&gt;leafname</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::get</span><span class="plain">(</span><span class="identifier">pos</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">c</span><span class="plain"> == </span><span class="character">'.'</span><span class="plain">) </span><span class="reserved">break</span><span class="plain">;</span>
<span class="identifier">PUT_TO</span><span class="plain">(</span><span class="identifier">NEWLEAF</span><span class="plain">, </span><span class="identifier">c</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">extension</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">extension</span><span class="plain">[0] == </span><span class="character">'.'</span><span class="plain">) </span><span class="identifier">extension</span><span class="plain">++;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">extension</span><span class="plain">[0]) </span><span class="identifier">WRITE_TO</span><span class="plain">(</span><span class="identifier">NEWLEAF</span><span class="plain">, </span><span class="string">".%s"</span><span class="plain">, </span><span class="identifier">extension</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="reserved">filename</span><span class="plain"> *</span><span class="identifier">N</span><span class="plain"> = </span><span class="functiontext">Filenames::in_folder</span><span class="plain">(</span><span class="identifier">F</span><span class="plain">-</span><span class="element">&gt;pathname_of_location</span><span class="plain">, </span><span class="identifier">NEWLEAF</span><span class="plain">);</span>
<span class="identifier">DISCARD_TEXT</span><span class="plain">(</span><span class="identifier">NEWLEAF</span><span class="plain">);</span>
<span class="reserved">return</span><span class="plain"> </span><span class="identifier">N</span><span class="plain">;</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Filenames::write_extension is used in <a href="#SP9">&#167;9</a>.</p>
<p class="endnote">The function Filenames::set_extension appears nowhere else.</p>
<p class="inwebparagraph"><a id="SP9"></a><b>&#167;9. Guessing file formats. </b>The following guesses the file format from its file extension:
</p>
<pre class="definitions">
<span class="definitionkeyword">define</span> <span class="constant">FORMAT_PERHAPS_HTML</span><span class="plain"> 1</span>
<span class="definitionkeyword">define</span> <span class="constant">FORMAT_PERHAPS_JPEG</span><span class="plain"> 2</span>
<span class="definitionkeyword">define</span> <span class="constant">FORMAT_PERHAPS_PNG</span><span class="plain"> 3</span>
<span class="definitionkeyword">define</span> <span class="constant">FORMAT_PERHAPS_OGG</span><span class="plain"> 4</span>
<span class="definitionkeyword">define</span> <span class="constant">FORMAT_PERHAPS_AIFF</span><span class="plain"> 5</span>
<span class="definitionkeyword">define</span> <span class="constant">FORMAT_PERHAPS_MIDI</span><span class="plain"> 6</span>
<span class="definitionkeyword">define</span> <span class="constant">FORMAT_PERHAPS_MOD</span><span class="plain"> 7</span>
<span class="definitionkeyword">define</span> <span class="constant">FORMAT_PERHAPS_GLULX</span><span class="plain"> 8</span>
<span class="definitionkeyword">define</span> <span class="constant">FORMAT_PERHAPS_ZCODE</span><span class="plain"> 9</span>
<span class="definitionkeyword">define</span> <span class="constant">FORMAT_PERHAPS_SVG</span><span class="plain"> 10</span>
<span class="definitionkeyword">define</span> <span class="constant">FORMAT_PERHAPS_GIF</span><span class="plain"> 11</span>
<span class="definitionkeyword">define</span> <span class="constant">FORMAT_UNRECOGNISED</span><span class="plain"> 0</span>
</pre>
<pre class="display">
<span class="reserved">int</span><span class="plain"> </span><span class="functiontext">Filenames::guess_format</span><span class="plain">(</span><span class="reserved">filename</span><span class="plain"> *</span><span class="identifier">F</span><span class="plain">) {</span>
<span class="identifier">TEMPORARY_TEXT</span><span class="plain">(</span><span class="identifier">EXT</span><span class="plain">);</span>
<span class="functiontext">Filenames::write_extension</span><span class="plain">(</span><span class="identifier">EXT</span><span class="plain">, </span><span class="identifier">F</span><span class="plain">);</span>
<span class="identifier">TEMPORARY_TEXT</span><span class="plain">(</span><span class="identifier">NORMALISED</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">EXT</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::get</span><span class="plain">(</span><span class="identifier">pos</span><span class="plain">);</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">c</span><span class="plain"> != </span><span class="character">' '</span><span class="plain">) </span><span class="identifier">PUT_TO</span><span class="plain">(</span><span class="identifier">NORMALISED</span><span class="plain">, </span><span class="functiontext">Characters::tolower</span><span class="plain">(</span><span class="identifier">c</span><span class="plain">));</span>
<span class="plain">}</span>
<span class="identifier">DISCARD_TEXT</span><span class="plain">(</span><span class="identifier">EXT</span><span class="plain">);</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">verdict</span><span class="plain"> = </span><span class="constant">FORMAT_UNRECOGNISED</span><span class="plain">;</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="functiontext">Str::eq_wide_string</span><span class="plain">(</span><span class="identifier">NORMALISED</span><span class="plain">, </span><span class="identifier">L</span><span class="string">".html"</span><span class="plain">)) </span><span class="identifier">verdict</span><span class="plain"> = </span><span class="constant">FORMAT_PERHAPS_HTML</span><span class="plain">;</span>
<span class="reserved">else</span><span class="plain"> </span><span class="reserved">if</span><span class="plain"> (</span><span class="functiontext">Str::eq_wide_string</span><span class="plain">(</span><span class="identifier">NORMALISED</span><span class="plain">, </span><span class="identifier">L</span><span class="string">".htm"</span><span class="plain">)) </span><span class="identifier">verdict</span><span class="plain"> = </span><span class="constant">FORMAT_PERHAPS_HTML</span><span class="plain">;</span>
<span class="reserved">else</span><span class="plain"> </span><span class="reserved">if</span><span class="plain"> (</span><span class="functiontext">Str::eq_wide_string</span><span class="plain">(</span><span class="identifier">NORMALISED</span><span class="plain">, </span><span class="identifier">L</span><span class="string">".jpg"</span><span class="plain">)) </span><span class="identifier">verdict</span><span class="plain"> = </span><span class="constant">FORMAT_PERHAPS_JPEG</span><span class="plain">;</span>
<span class="reserved">else</span><span class="plain"> </span><span class="reserved">if</span><span class="plain"> (</span><span class="functiontext">Str::eq_wide_string</span><span class="plain">(</span><span class="identifier">NORMALISED</span><span class="plain">, </span><span class="identifier">L</span><span class="string">".jpeg"</span><span class="plain">)) </span><span class="identifier">verdict</span><span class="plain"> = </span><span class="constant">FORMAT_PERHAPS_JPEG</span><span class="plain">;</span>
<span class="reserved">else</span><span class="plain"> </span><span class="reserved">if</span><span class="plain"> (</span><span class="functiontext">Str::eq_wide_string</span><span class="plain">(</span><span class="identifier">NORMALISED</span><span class="plain">, </span><span class="identifier">L</span><span class="string">".png"</span><span class="plain">)) </span><span class="identifier">verdict</span><span class="plain"> = </span><span class="constant">FORMAT_PERHAPS_PNG</span><span class="plain">;</span>
<span class="reserved">else</span><span class="plain"> </span><span class="reserved">if</span><span class="plain"> (</span><span class="functiontext">Str::eq_wide_string</span><span class="plain">(</span><span class="identifier">NORMALISED</span><span class="plain">, </span><span class="identifier">L</span><span class="string">".ogg"</span><span class="plain">)) </span><span class="identifier">verdict</span><span class="plain"> = </span><span class="constant">FORMAT_PERHAPS_OGG</span><span class="plain">;</span>
<span class="reserved">else</span><span class="plain"> </span><span class="reserved">if</span><span class="plain"> (</span><span class="functiontext">Str::eq_wide_string</span><span class="plain">(</span><span class="identifier">NORMALISED</span><span class="plain">, </span><span class="identifier">L</span><span class="string">".aiff"</span><span class="plain">)) </span><span class="identifier">verdict</span><span class="plain"> = </span><span class="constant">FORMAT_PERHAPS_AIFF</span><span class="plain">;</span>
<span class="reserved">else</span><span class="plain"> </span><span class="reserved">if</span><span class="plain"> (</span><span class="functiontext">Str::eq_wide_string</span><span class="plain">(</span><span class="identifier">NORMALISED</span><span class="plain">, </span><span class="identifier">L</span><span class="string">".aif"</span><span class="plain">)) </span><span class="identifier">verdict</span><span class="plain"> = </span><span class="constant">FORMAT_PERHAPS_AIFF</span><span class="plain">;</span>
<span class="reserved">else</span><span class="plain"> </span><span class="reserved">if</span><span class="plain"> (</span><span class="functiontext">Str::eq_wide_string</span><span class="plain">(</span><span class="identifier">NORMALISED</span><span class="plain">, </span><span class="identifier">L</span><span class="string">".midi"</span><span class="plain">)) </span><span class="identifier">verdict</span><span class="plain"> = </span><span class="constant">FORMAT_PERHAPS_MIDI</span><span class="plain">;</span>
<span class="reserved">else</span><span class="plain"> </span><span class="reserved">if</span><span class="plain"> (</span><span class="functiontext">Str::eq_wide_string</span><span class="plain">(</span><span class="identifier">NORMALISED</span><span class="plain">, </span><span class="identifier">L</span><span class="string">".mid"</span><span class="plain">)) </span><span class="identifier">verdict</span><span class="plain"> = </span><span class="constant">FORMAT_PERHAPS_MIDI</span><span class="plain">;</span>
<span class="reserved">else</span><span class="plain"> </span><span class="reserved">if</span><span class="plain"> (</span><span class="functiontext">Str::eq_wide_string</span><span class="plain">(</span><span class="identifier">NORMALISED</span><span class="plain">, </span><span class="identifier">L</span><span class="string">".mod"</span><span class="plain">)) </span><span class="identifier">verdict</span><span class="plain"> = </span><span class="constant">FORMAT_PERHAPS_MOD</span><span class="plain">;</span>
<span class="reserved">else</span><span class="plain"> </span><span class="reserved">if</span><span class="plain"> (</span><span class="functiontext">Str::eq_wide_string</span><span class="plain">(</span><span class="identifier">NORMALISED</span><span class="plain">, </span><span class="identifier">L</span><span class="string">".svg"</span><span class="plain">)) </span><span class="identifier">verdict</span><span class="plain"> = </span><span class="constant">FORMAT_PERHAPS_SVG</span><span class="plain">;</span>
<span class="reserved">else</span><span class="plain"> </span><span class="reserved">if</span><span class="plain"> (</span><span class="functiontext">Str::eq_wide_string</span><span class="plain">(</span><span class="identifier">NORMALISED</span><span class="plain">, </span><span class="identifier">L</span><span class="string">".gif"</span><span class="plain">)) </span><span class="identifier">verdict</span><span class="plain"> = </span><span class="constant">FORMAT_PERHAPS_GIF</span><span class="plain">;</span>
<span class="reserved">else</span><span class="plain"> </span><span class="reserved">if</span><span class="plain"> (</span><span class="functiontext">Str::len</span><span class="plain">(</span><span class="identifier">NORMALISED</span><span class="plain">) &gt; 0) {</span>
<span class="reserved">if</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">NORMALISED</span><span class="plain">, 0)) == </span><span class="character">'.'</span><span class="plain">) &amp;&amp;</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">NORMALISED</span><span class="plain">, 1)) == </span><span class="character">'z'</span><span class="plain">) &amp;&amp;</span>
<span class="plain">(</span><span class="functiontext">Characters::isdigit</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">NORMALISED</span><span class="plain">, 2)))) &amp;&amp;</span>
<span class="plain">(</span><span class="functiontext">Str::len</span><span class="plain">(</span><span class="identifier">NORMALISED</span><span class="plain">) == 3))</span>
<span class="identifier">verdict</span><span class="plain"> = </span><span class="constant">FORMAT_PERHAPS_ZCODE</span><span class="plain">;</span>
<span class="reserved">else</span><span class="plain"> </span><span class="reserved">if</span><span class="plain"> (</span><span class="functiontext">Str::get</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">NORMALISED</span><span class="plain">))) == </span><span class="character">'x'</span><span class="plain">)</span>
<span class="identifier">verdict</span><span class="plain"> = </span><span class="constant">FORMAT_PERHAPS_GLULX</span><span class="plain">;</span>
<span class="plain">}</span>
<span class="identifier">DISCARD_TEXT</span><span class="plain">(</span><span class="identifier">NORMALISED</span><span class="plain">);</span>
<span class="reserved">return</span><span class="plain"> </span><span class="identifier">verdict</span><span class="plain">;</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Filenames::guess_format is used in 5/ee (<a href="5-ee.html#SP7_2_2_3">&#167;7.2.2.3</a>).</p>
<p class="inwebparagraph"><a id="SP10"></a><b>&#167;10. Opening. </b>These files are wrappers for <code class="display"><span class="extract">fopen</span></code>, the traditional C library call, but
referring to the file by filename structure rather than a textual name. Note
that we must transcode the filename to whatever the locale expects before
we call <code class="display"><span class="extract">fopen</span></code>, which is the main reason for the wrapper.
</p>
<pre class="display">
<span class="reserved">FILE</span><span class="plain"> *</span><span class="functiontext">Filenames::fopen</span><span class="plain">(</span><span class="reserved">filename</span><span class="plain"> *</span><span class="identifier">F</span><span class="plain">, </span><span class="reserved">char</span><span class="plain"> *</span><span class="identifier">usage</span><span class="plain">) {</span>
<span class="reserved">char</span><span class="plain"> </span><span class="identifier">transcoded_pathname</span><span class="plain">[4*</span><span class="constant">MAX_FILENAME_LENGTH</span><span class="plain">];</span>
<span class="identifier">TEMPORARY_TEXT</span><span class="plain">(</span><span class="identifier">FN</span><span class="plain">);</span>
<span class="identifier">WRITE_TO</span><span class="plain">(</span><span class="identifier">FN</span><span class="plain">, </span><span class="string">"%f"</span><span class="plain">, </span><span class="identifier">F</span><span class="plain">);</span>
<span class="functiontext">Str::copy_to_locale_string</span><span class="plain">(</span><span class="identifier">transcoded_pathname</span><span class="plain">, </span><span class="identifier">FN</span><span class="plain">, 4*</span><span class="constant">MAX_FILENAME_LENGTH</span><span class="plain">);</span>
<span class="identifier">DISCARD_TEXT</span><span class="plain">(</span><span class="identifier">FN</span><span class="plain">);</span>
<span class="reserved">return</span><span class="plain"> </span><span class="identifier">fopen</span><span class="plain">(</span><span class="identifier">transcoded_pathname</span><span class="plain">, </span><span class="identifier">usage</span><span class="plain">);</span>
<span class="plain">}</span>
<span class="reserved">FILE</span><span class="plain"> *</span><span class="functiontext">Filenames::fopen_caseless</span><span class="plain">(</span><span class="reserved">filename</span><span class="plain"> *</span><span class="identifier">F</span><span class="plain">, </span><span class="reserved">char</span><span class="plain"> *</span><span class="identifier">usage</span><span class="plain">) {</span>
<span class="reserved">char</span><span class="plain"> </span><span class="identifier">transcoded_pathname</span><span class="plain">[4*</span><span class="constant">MAX_FILENAME_LENGTH</span><span class="plain">];</span>
<span class="identifier">TEMPORARY_TEXT</span><span class="plain">(</span><span class="identifier">FN</span><span class="plain">);</span>
<span class="identifier">WRITE_TO</span><span class="plain">(</span><span class="identifier">FN</span><span class="plain">, </span><span class="string">"%f"</span><span class="plain">, </span><span class="identifier">F</span><span class="plain">);</span>
<span class="functiontext">Str::copy_to_locale_string</span><span class="plain">(</span><span class="identifier">transcoded_pathname</span><span class="plain">, </span><span class="identifier">FN</span><span class="plain">, 4*</span><span class="constant">MAX_FILENAME_LENGTH</span><span class="plain">);</span>
<span class="identifier">DISCARD_TEXT</span><span class="plain">(</span><span class="identifier">FN</span><span class="plain">);</span>
<span class="reserved">return</span><span class="plain"> </span><span class="functiontext">CIFilingSystem::fopen</span><span class="plain">(</span><span class="identifier">transcoded_pathname</span><span class="plain">, </span><span class="identifier">usage</span><span class="plain">);</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Filenames::fopen is used in 2/str (<a href="2-str.html#SP24">&#167;24</a>, <a href="2-str.html#SP25">&#167;25</a>), 4/tf (<a href="4-tf.html#SP1">&#167;1</a>, <a href="4-tf.html#SP5_1">&#167;5.1</a>), 6/bf (<a href="6-bf.html#SP8">&#167;8</a>).</p>
<p class="endnote">The function Filenames::fopen_caseless appears nowhere else.</p>
<p class="inwebparagraph"><a id="SP11"></a><b>&#167;11. Comparing. </b>Not as easy as it seems. The following is a slow but effective way to
compare two filenames by seeing if they have the same canonical form
when printed out.
</p>
<pre class="display">
<span class="reserved">int</span><span class="plain"> </span><span class="functiontext">Filenames::eq</span><span class="plain">(</span><span class="reserved">filename</span><span class="plain"> *</span><span class="identifier">F1</span><span class="plain">, </span><span class="reserved">filename</span><span class="plain"> *</span><span class="identifier">F2</span><span class="plain">) {</span>
<span class="reserved">if</span><span class="plain"> (</span><span class="identifier">F1</span><span class="plain"> == </span><span class="identifier">F2</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="identifier">TEMPORARY_TEXT</span><span class="plain">(</span><span class="identifier">T1</span><span class="plain">);</span>
<span class="identifier">TEMPORARY_TEXT</span><span class="plain">(</span><span class="identifier">T2</span><span class="plain">);</span>
<span class="identifier">WRITE_TO</span><span class="plain">(</span><span class="identifier">T1</span><span class="plain">, </span><span class="string">"%f"</span><span class="plain">, </span><span class="identifier">F1</span><span class="plain">);</span>
<span class="identifier">WRITE_TO</span><span class="plain">(</span><span class="identifier">T2</span><span class="plain">, </span><span class="string">"%f"</span><span class="plain">, </span><span class="identifier">F2</span><span class="plain">);</span>
<span class="reserved">int</span><span class="plain"> </span><span class="identifier">rv</span><span class="plain"> = </span><span class="functiontext">Str::eq</span><span class="plain">(</span><span class="identifier">T1</span><span class="plain">, </span><span class="identifier">T2</span><span class="plain">);</span>
<span class="identifier">DISCARD_TEXT</span><span class="plain">(</span><span class="identifier">T1</span><span class="plain">);</span>
<span class="identifier">DISCARD_TEXT</span><span class="plain">(</span><span class="identifier">T2</span><span class="plain">);</span>
<span class="reserved">return</span><span class="plain"> </span><span class="identifier">rv</span><span class="plain">;</span>
<span class="plain">}</span>
</pre>
<p class="inwebparagraph"></p>
<p class="endnote">The function Filenames::eq appears nowhere else.</p>
<!--End of weave: 264 lines from a web of 9279-->
</body>
</html>