forked from scheme-requests-for-implementation/srfi-81
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsrfi-81.html
124 lines (109 loc) · 39.5 KB
/
srfi-81.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
<html>
<head>
<title>SRFI 81: Port I/O</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="/srfi.css" type="text/css" />
</head>
<body>
<H1>Title</H1>
Port I/O
<H1>Author</H1>
Michael Sperber
<H1>Status</H1>
This SRFI is currently in ``withdrawn'' status. For an explanation of
each status that a SRFI can hold, see
<A HREF="http://srfi.schemers.org/srfi-process.html">here</A>. You can
access the discussion via <A HREF="mail-archive/maillist.html">the
archive of the mailing list</A>.
<UL>
<LI>Received: 2005-10-08
<LI>Draft: 2005-11-24
<LI>Withdrawn: 2006-11-20
</UL>
<h1>Abstract</h1>
<p>This SRFI defines an I/O layer similar in nature to the ports subsystem in R5RS, and provides conventional, imperative buffered input and output.</p><p>The layer architecture is similar to the upper three layers of the I/O subsystem in <a href="http://www.standardml.org/Basis/">The Standard ML Basis Library</a>.</p><p>In particular, the subsystem fulfills the following requirements:</p><ul><li>buffered reading and writing</li><li>binary and text I/O, mixed if needed</li><li>the ability to create arbitrary I/O ports from readers and writers</li></ul><p>It builds on the Primitive I/O layer specified in <a href="http://srfi.schemers.org/srfi-79/">SRFI 79 (Primitive I/O)</a>.</p><h1>Rationale</h1><p>This SRFI is meant as a compelling replacement for the R5RS I/O subsystem.</p><p>The design of this SRFI is driven by the requirements mentioned in the abstract on the one hand. Moreover, it is meant to fully integrate into a three-layer I/O subsystem where ports can be built on top of streams, as specified in <a href="http://srfi.schemers.org/srfi-80/">SRFI 80 (Stream I/O)</a>. Ports can be implemented independently, however.</p><h1>Specification</h1><h2>Prerequisites</h2><p>This SRFI is meant for Scheme implementations with the following prerequisites:</p><ul><li><a href="http://srfi.schemers.org/srfi-34/">SRFI 34 (Exception Handling for Programs)</a></li><li><a href="http://srfi.schemers.org/srfi-35/">SRFI 35 (Conditions)</a></li><li><a href="http://srfi.schemers.org/srfi-74/">SRFI 74 (Octet-Addressed Binary Blocks)</a></li><li><a href="http://srfi.schemers.org/srfi-75/">SRFI 75 (R6RS Unicode data)</a></li><li><a href="http://srfi.schemers.org/srfi-79/">SRFI 79 (Primitive I/O)</a></li></ul><h2>Unicode support</h2><p>This SRFI assumes that the <code>char</code> datatype in Scheme corresponds to Unicode scalar values. This, in turn, means that strings are represented as vectors of scalar values. (Note that this is consistent with <a href="http://srfi.schemers.org/srfi-14/">SRFI 14 (Character-set library)</a> and <a href="http://srfi.schemers.org/srfi-75/">SRFI 75 (R6RS Unicode data)</a>.) It may be possible to make this SRFI work in an ASCII- or Latin-1-only system, but I have not made any special provisions to ensure this.</p><h2>Filenames</h2><p>Filenames in this SRFI are the same as in <a href="http://srfi.schemers.org/srfi-79/">SRFI 79 (Primitive I/O)</a>.</p><h2>General remarks</h2><p>For procedures that have no "natural" return value, this SRFI often uses the sentence</p><p><em>The return values are unspecified.</em></p><p>This means that number of return values and the return values are unspecified. However, the number of return values is such that it is accepted by a continuation created by <code>begin</code>. Specifically, on Scheme implementations where continuations created by <code>begin</code> accept an arbitrary number of arguments (this includes most implementations), it is suggested that the procedure return zero return values.</p><h2>Blobs</h2><p>The specification frequently refers to <i>blobs</i>. These are as specified in <a href="http://srfi.schemers.org/srfi-74/">SRFI 74 (Octet-Addressed Binary Blocks)</a>.</p><h2>File options</h2><p>File options are as in <a href="http://srfi.schemers.org/srfi-79/">SRFI 79 (Primitive I/O)</a>.</p><h2>Buffer modes</h2><p>Each output port has an associated <i>buffer mode</i> that defines when an output operation will flush the buffer associated with the output stream. The possible buffer modes are <code>none</code> for no buffering, <code>line</code> for flushing upon newlines, and <code>block</code> for block-based buffering.</p><p>While this SRFI does not require buffer modes to form a distinct type, implementors are encouraged to make them a distinct type.</p><p>In systems implementing <a href="http://srfi.schemers.org/srfi-80/">SRFI 80 (Stream I/O)</a>, the buffer modes may or may not be identical to those defined here.</p><dl><dt><span class="syntax def"><code>(buffer-mode </code><var><name></var><code>)</code></span> (syntax)</dt><dd><p><var>Name</var> must be one of the identifiers <code>none</code>, <code>line</code>, and <code>block</code>. This returns a buffer-mode object denoting the associated buffer mode. There is only one such object for each mode, so a program can compare them using <code>eq?</code>.</p></dd><dt class="proc def"><code>(buffer-mode? </code><var>obj</var><code>)</code></dt><dd><p>This returns <code>#t</code> if the argument is a buffer-mode object, <code>#f</code> otherwise.</p></dd></dl><h2>Text Transcoders</h2><p>This part of the SRFI provides pre-packaged functionality for encoding and decoding text in some common encodings. A <i>transcoder</i> is an opaque object encapsulating a specific text encoding. This SRFI specifies how to obtain a transcoder given a text encoder/decoder (or <i>codec</i> for short) and a specified newline encoding.</p><p>In systems implementing <a href="http://srfi.schemers.org/srfi-80/">SRFI 80 (Stream I/O)</a>, the transcoders specified here may or may not be identical to those defined there.</p><dl><dt><span class="syntax def"><code>(transcoder </code><code>(codec <var><codec></var>)</code> <code>(eol-style <var><eol-style></var>)</code><code>)</code></span> (syntax)</dt><dd><p>This constructs a transcoder object from a specified codec and a specified end-of-line style. The <code>codec</code> and the <code>eol-style</code> clauses are both optional. If present, <var>codec</var> and <var>eol-style</var>, must be expressions that evaluate to a codec and an eol-style object, respectively. If not present, the codec defaults to "no codec" (corresponding to UTF-8), and the eol-style object defaults to the platform's standard EOL convention.</p><p>Any operands to a <code>transcoder</code> form that do not match the above syntax may be platform-specific extensions. The implementation is free to ignore them, but must not signal an error.</p></dd><dt><span class="syntax def"><code>(update-transcoder </code><var><old></var> <code>(codec <var><codec></var>)</code> <code>(eol-style <var><eol-style></var>)</code><code>)</code></span> (syntax)</dt><dd><p>This form returns a new transcoder object constructed from an old one, with the <code>codec</code> and <code>eol-style</code> fields replaced by the specified values. (Again, the <code>codec</code> and the <code>eol-style</code> clauses are both optional. Also, unrecognized operands can be ignored, but cannot signal an error.)</p></dd><dt><span class="syntax def"><code>(eol-style </code><code>lf</code><code>)</code></span> (syntax)</dt><dt><span class="syntax def"><code>(eol-style </code><code>crlf</code><code>)</code></span> (syntax)</dt><dt><span class="syntax def"><code>(eol-style </code><code>cr</code><code>)</code></span> (syntax)</dt><dd><p>These forms evaluate to end-of-line-style objects - <code>lf</code> stands for using U+000A, <code>crlf</code> stands for using U+000D U+000A, and <code>cr</code> stands for using U+000D as end-of-line.</p></dd><dt class="def variable"><code>latin-1-codec</code></dt><dt class="def variable"><code>utf-16le-codec</code></dt><dt class="def variable"><code>utf-16be-codec</code></dt><dt class="def variable"><code>utf-32le-codec</code></dt><dt class="def variable"><code>utf-32be-codec</code></dt>
<dd><p>These are predefined codecs for the ISO8859-1, UTF-16LE, UTF-16BE, UTF32-LE, and UTF-32BE encodings.</p></dd>
</dl>
<h2>Input and Output Ports</h2>
<p>This SRFI provides buffered I/O based on <i>ports</i>. Ports, like the streams in <a href="http://srfi.schemers.org/srfi-80/">SRFI 80 (Stream I/O)</a>, allow buffered I/O on the underlying data sources and destinations. Input ports, like output ports, are imperative; a read operation destructively removes data from the port. The port layer is very similar, but not identical, to the R5RS I/O system.</p><p>The Port I/O layer introduces one condition type of its own.</p>
<pre>(define-condition-type &i/o-port-error &i/o-error
i/o-port-error?
(port i/o-error-port))
</pre><dl><dd><p>This condition type allows specifying with what particular port an I/O error is associated. The <code>port</code> field has purely informational purpose. Conditions raised by Port I/O procedures <em>may</em> include an <code>&i/o-port-error</code> condition, but are not required to do so.</p></dd></dl>
<p>The specifications of some of the procedures that return ports contain the sentence:</p><blockquote>The input (or output) port may or may not be a stream port.</blockquote><p>This is a reference to <a href="http://srfi.schemers.org/srfi-82/">SRFI 82 (Stream Ports)</a> and ensures compatibility with that SRFI. However, this SRFI in no way depends on SRFI 82.</p><h3>Input ports</h3><dl><dt class="proc def"><code>(input-port? </code><var>obj</var><code>)</code></dt><dd><p>This returns <code>#t</code> if the argument is an input port, <code>#f</code> otherwise.</p></dd><dt class="proc def"><code>(read-blob-some </code><var>input-port</var><code>)</code></dt><dd><p>If any data is available in <var>input-port</var> before the next end of file, this returns a freshly allocated blob of non-zero size containing that data, and updates <var>input-port</var> to point exactly past the data read. If an end of file has been reached, it returns <code>#f</code>, and the input port is updated to point just past the end of file.</p></dd><dt class="proc def"><code>(read-u8 </code><var>input-port</var><code>)</code></dt><dd><p>If an octet is available before the next end of file, this returns that octet as an exact integer, and updates <var>input-port</var> to point exactly past the octet read. If an end of file has been reached, it returns <code>#f</code>, and the input port is updated to point just past the end of file.</p></dd><dt class="proc def"><code>(read-blob-n </code><var>input-port</var> <var>n</var><code>)</code></dt><dd><p><var>N</var> must be an exact, non-negative integer, specifying the number of octets to be read. This tries to read <var>n</var> octets. If <var>n</var> or more octets are available before the next end of file, it returns a blob of size <var>n</var>. If fewer octets are available before the next end of file, it returns a blob containing those octets. Subsequently, the input port is updated to point exactly past the data read. If end of file has been reached, this returns <code>#f</code>, and the input port is updated to point just past the end of file.</p></dd><dt class="proc def"><code>(read-blob-n! </code><var>input-port</var> <var>blob</var> <var>start</var> <var>count</var><code>)</code></dt><dd><p><var>Count</var> must be an exact, non-negative integer, specifying the number of octets to be read. <var>Blob</var> must be a blob with at least <code>(+ <var>start</var> <var>count</var>)</code> elements. This tries to read <var>count</var> octets. If <var>count</var> or more octets are available before the next end of file, they are written into <var>blob</var> starting at index <var>start</var>, and it returns <var>count</var>. If fewer octets are available before the next end of file, it writes the available octets into <var>blob</var> starting at index <var>start</var>, and it returns the number of octets actually read. In either case, the input port is updated to point exactly past the data read. If end of file has been reached, this returns <code>#f</code>, and it updates the input port to point just past the end of file. This procedure will block until either data is available or end of file is reached.</p></dd><dt class="proc def"><code>(read-blob-all </code><var>input-port</var><code>)</code></dt><dd><p>If data is available before the next end of file, this returns a blob containing all octets until that end of file. If not, <code>read-blob-all</code> returns <code>#f</code>. The input port is updated to point just past the end of file. Note that this function may block indefinitely on ports connected to interactive devices, even though data is available.</p></dd><dt class="proc def"><code>(read-string </code><var>input-port</var><code>)</code></dt><dd><p>If any data representing a string is available before the next end of file, this returns a string of non-zero size containing the UTF-8 decoding of that data. The input port is updated to point just past the data read. If an end of file has been reached, it returns <code>#f</code>, and the input port is updated to point just past the end of file. This procedure will block until either data is available or end of file is reached.</p></dd><dt class="proc def"><code>(read-char </code><var>input-port</var><code>)</code></dt><dd><p>If a char is available before the next end of file, this returns that char, and the input port is updated to point past the data read. If an end of file has been reached, this returns <code>#f</code>, and the input code returned points just past the end of file. This procedure will block until either data is available or end of file is reached.</p></dd><dt class="proc def"><code>(read-string-n </code><var>input-port</var> <var>n</var><code>)</code></dt><dd><p><var>N</var> must be an exact, non-negative integer, specifying the number of chars to be read. It tries to read <var>n</var> chars. If <var>n</var> or more chars are available before the next end of file, it returns a string of size <var>n</var> consisting of those chars. If fewer chars are available before the next end of file, it returns a string containing those chars. In either case, the input port is updated to point exactly past the data read. If end of file has been reached, it returns <code>#f</code>, and the input port is updated to point just past the end of file. This procedure will block until either data is available or end of file is reached.</p></dd><dt class="proc def"><code>(read-string-n! </code><var>input-port</var> <var>string</var> <var>start</var> <var>count</var><code>)</code></dt><dd><p><var>Count</var> must be an exact, non-negative integer, specifying the number of chars to be read. The input stream returned points exactly past the data read. It tries to read <var>count</var> chars. If <var>count</var> or more chars are available before the next end of file, they are written into <var>string</var> starting at index <var>start</var>, and it returns <var>count</var> as the value. If fewer chars are available before the next end of file, it writes the available chars into <var>string</var> starting at index <var>start</var>, and it returns the number of chars actually read as the value. If end of file has been reached, it returns <code>#f</code>, and the input port is updated to point just past the end of file. This procedure will block until either data is available or end of file is reached.</p></dd><dt class="proc def"><code>(read-string-all </code><var>input-port</var><code>)</code></dt><dd><p>If data is available before the next end of file, the value returned is a string containing all the text until the next end of file. If no data is available, the value is <code>#f</code>. The input port is updated to point just past the end of file. Note that this function may block indefinitely on interactive ports.</p></dd><dt class="proc def"><code>(peek-u8 </code><var>input-port</var><code>)</code></dt><dd><p>This is the same as <code>read-u8</code>, but does not advance the port.</p></dd><dt class="proc def"><code>(peek-char </code><var>input-port</var><code>)</code></dt><dd><p>This is the same as <code>read-char</code>, but does not advance the port.</p></dd><dt class="proc def"><code>(port-eof? </code><var>input-port</var><code>)</code></dt><dd><p>Returns <code>#t</code> if the port is currently pointing at an end-of-file, <code>#f</code> otherwise.</p></dd><dt class="proc def"><code>(input-port-position </code><var>input-port</var><code>)</code></dt><dd><p>This returns the octet position corresponding to the next octet read from the input port. This procedure raises an <code>&i/o-operation-not-available-error</code> condition if the port does not support the operation. It is an error to apply this procedure to a closed port, a transcoded port, or a stream input port on a truncated stream, or a translated stream.</p></dd><dt class="proc def"><code>(set-input-port-position! </code><var>input-port</var> <var>pos</var><code>)</code></dt><dd><p><var>Pos</var> must be a non-negative exact integer. This sets the current octet position of <var>input-port</var> to <var>pos</var>. This procedure raises an <code>&i/o-operation-not-available-error</code> condition if the stream does not support the operation. It is an error to apply this procedure to a closed port, a transcoded port, or a stream output port on a terminated stream, or a translated stream.</p></dd><dt class="proc def"><code>(transcode-input-port! </code><var>input-port</var> <var>transcoder</var><code>)</code></dt><dd><p>This transcodes <var>input-port</var> according to the encoding specified by <var>transcoder</var>, assuming <var>input-port</var> was previously not transcoded. The port will henceforth translate the data arriving at the port into UTF-8 with end-of-line encoded by U+000A.</p><p>It is an error for <var>input-port</var> to be transcoded upon the call to <code>transcode-input-port!</code>.</p></dd><dt class="proc def"><code>(close-input-port </code><var>input-port</var><code>)</code></dt><dd><p>This closes <var>input-port</var>, rendering the port incapable of accepting data. This has no effect if the port has already been closed. The return values are unspecified.</p></dd><dt class="proc def"><code>(open-file-input-port </code><var>filename</var><code>)</code></dt><dt class="proc def"><code>(open-file-input-port </code><var>filename</var> <var>file-options</var><code>)</code></dt><dt class="proc def"><code>(open-file-input-port </code><var>filename</var> <var>file-options</var> <var>transcoder</var><code>)</code></dt><dd><p>This returns an input port for the named file. The input port may or may not be a stream port. The <var>file-options</var> object defaults to <code>(file-options)</code>if not present. It may determine various aspects of the returned port, see the <a href="#file-options">section on file options</a>. If a transcoder <var>transcoder</var> is specified, the port is appropriately transcoded.</p></dd><dt class="proc def"><code>(open-blob-input-port </code><var>blob</var><code>)</code></dt><dt class="proc def"><code>(open-blob-input-port </code><var>blob</var> <var>transcoder</var><code>)</code></dt><dd><p>This returns an input port, associated with a blob stream on the blob <var>blob</var>. The input port may or may not be a stream port. If a transcoder <var>transcoder</var> is specified, the port is appropriately transcoded.</p></dd><dt class="proc def"><code>(open-string-input-port </code><var>string</var><code>)</code></dt><dt class="proc def"><code>(open-string-input-port </code><var>string</var> <var>transcoder</var><code>)</code></dt><dd><p>This returns an input port, associated with a blob stream on the UTF-8 encoding of string <var>string</var>. The input port may or may not be a stream port. If a transcoder <var>transcoder</var> is specified, the port is appropriately transcoded.</p></dd><dt class="proc def"><code>(call-with-input-port </code><var>input-port</var> <var>proc</var><code>)</code></dt><dd><p>This calls <var>proc</var> with <var>input-port</var> as an argument. If <var>proc</var> returns, then the port is closed automatically and the values returned by <var>proc</var> are returned. If <var>proc</var> does not return, then the port will not be closed automatically, unless it is possible to prove that the port will never again be used for a read operation.</p></dd><dt class="proc def"><code>(standard-input-port)</code></dt><dd><p>Returns an input port connected to standard input, possibly a fresh one on each call. Note that a program that may run in a system where other programs also read concurrently from a port returned by <code>standard-input-port</code> should not keep the returned port object live for too long without reading from it. Specifically, it may be a stream port connected to a standard-input stream, and standard input read in other parts of the program may accumulate buffer space.</p></dd>
</dl>
<h3>Output ports</h3>
<dl><dt class="proc def"><code>(output-port? </code><var>obj</var><code>)</code></dt><dd><p>This returns <code>#t</code> if the argument is an output port, <code>#f</code> otherwise.</p></dd><dt class="proc def"><code>(write-blob </code><var>output-port</var> <var>blob</var><code>)</code></dt><dt class="proc def"><code>(write-blob </code><var>output-port</var> <var>blob</var> <var>start</var><code>)</code></dt><dt class="proc def"><code>(write-blob </code><var>output-port</var> <var>blob</var> <var>start</var> <var>count</var><code>)</code></dt><dd><p><var>Start</var> and <var>count</var> must be non-negative exact integers that default to 0 and <code>(- (blob-length <var>blob</var>) <var>start</var>)</code>, respectively. This writes the <var>count</var> octets in blob <var>blob</var> starting at index <var>start</var> to the output port. It is an error if the blob actually has size less than <var>start</var> + <var>count</var>. The return values are unspecified.</p></dd></dl>
<dl>
<dt class="proc def"><code>(write-u8 </code><var>output-port</var> <var>octet</var><code>)</code></dt><dd><p>This writes the octet <var>octet</var> to the output port. The return values are unspecified.</p></dd><dt class="proc def"><code>(write-string-n </code><var>output-port</var> <var>string</var><code>)</code></dt><dt class="proc def"><code>(write-string-n </code><var>output-port</var> <var>string</var> <var>start</var><code>)</code></dt><dt class="proc def"><code>(write-string-n </code><var>output-port</var> <var>string</var> <var>start</var> <var>count</var><code>)</code></dt><dd><p><var>Start</var> and <var>count</var> must be non-negative exact integers. <var>Start</var> defaults to 0. <var>Count</var> defaults to <code>(- (string-length <var>blob</var>) <var>start</var>)</code>. This writes the UTF-8 encoding of the substring <code>(substring <var>string</var> (+ <var>start</var> <var>count</var>))</code> to the port. The return values are unspecified.</p></dd><dt class="proc def"><code>(write-char </code><var>output-port</var> <var>char</var><code>)</code></dt><dd><p>This writes the UTF-8 encoding of the char <var>char</var> to the port. The return values are unspecified.</p></dd><dt class="proc def"><code>(newline </code><var>output-port</var><code>)</code></dt><dd><p>This is equivalent to <code>(write-char #\newline <var>output-port</var>)</code>. The return values are unspecified.</p></dd><dt class="proc def"><code>(flush-output-port </code><var>output-port</var><code>)</code></dt><dd><p>This flushes any output from the buffer of <var>output-stream</var> to the underlying data or device. The return values are unspecified.</p></dd><dt class="proc def"><code>(output-port-buffer-mode </code><var>output-port</var><code>)</code></dt><dd><p>This returns the buffer-mode object of <var>output-port</var>.</p></dd><dt class="proc def"><code>(set-output-port-buffer-mode! </code><var>output-port</var> <var>buffer-mode</var><code>)</code></dt><dd><p>If the current buffer mode of <var>output-port</var> is something other than <code>none</code> and <var>buffer-mode</var> is the <code>none</code> buffer-mode object, this will first flush the output port. Then, it sets the buffer-mode object associated with <var>output-port</var> to <var>buffer-mode</var>. The return values are unspecified.</p></dd><dt class="proc def"><code>(output-port-position </code><var>output-port</var><code>)</code></dt><dd><p>This returns the position corresponding to the next octet written to the output port. This procedure raises an <code>&i/o-operation-not-available-error</code> condition if the port does not support the operation. It is an error to apply this procedure to a closed port, a transcoded port, or a stream output port on a terminated stream, or a translated stream.</p></dd><dt class="proc def"><code>(set-output-port-position! </code><var>output-port</var> <var>pos</var><code>)</code></dt><dd><p><var>Pos</var> must be a non-negative exact integer. This flushes the output port and sets its current octet position to <var>pos</var>. This procedure raises an <code>&i/o-operation-not-available-error</code> condition if the port does not support the operation. It is an error to apply this procedure to a closed port, a transcoded port, or a stream output port on a terminated stream, or a translated stream.</p></dd><dt class="proc def"><code>(transcode-output-port! </code><var>output-port</var> <var>transcoder</var><code>)</code></dt><dd><p>This transcodes <var>output-port</var>, translating the data fed into <var>output-port</var> into the encoding specified by <var>transcoder</var>, assuming it is encoded as UTF-8 with end-of-line encoded by U+000A.</p><p>This assumes <var>output-port</var> was previously not transcoded. It is an error for <var>output-port</var> to be transcoded upon the call to <code>transcode-output-port!</code>.</p></dd><dt class="proc def"><code>(close-output-port </code><var>output-port</var><code>)</code></dt><dd><p>This closes <var>output-port</var>, rendering the port incapable of delivering data. This has no effect if the port has already been closed. The return values are unspecified.</p></dd><dt class="proc def"><code>(open-file-output-port </code><var>filename</var><code>)</code></dt><dt class="proc def"><code>(open-file-output-port </code><var>filename</var> <var>file-options</var><code>)</code></dt><dt class="proc def"><code>(open-file-output-port </code><var>filename</var> <var>file-options</var> <var>transcoder</var><code>)</code></dt><dd><p>This returns an output port for the named file and the specified options (which defaults to <code>file-options</code>.) The output port may or may not be a stream port. If a transcoder <var>transcoder</var> is specified, the port is appropriately transcoded.</p></dd><dt class="proc def"><code>(call-with-blob-output-port </code><var>proc</var><code>)</code></dt><dt class="proc def"><code>(call-with-blob-output-port </code><var>proc</var> <var>transcoder</var><code>)</code></dt><dd><p><var>Proc</var> is a procedure accepting one argument. This creates an unbuffered output port connected to a blob writer, and calls <var>proc</var> with that output port as an argument. The output port may or may not be a stream port. The call to <code>call-with-blob-output-port</code> returns the blob associated with the port when <var>proc</var> returns. If a transcoder <var>transcoder</var> is specified, the port is appropriately transcoded.</p></dd><dt class="proc def"><code>(call-with-string-output-port </code><var>proc</var><code>)</code></dt><dt class="proc def"><code>(call-with-string-output-port </code><var>proc</var> <var>transcoder</var><code>)</code></dt><dd><p><var>Proc</var> is a procedure accepting one argument. This creates an unbuffered output connected to a blob writer, and calls <var>proc</var> with that port as an argument. The output port may or may not be a stream port. The call to <code>call-with-string-output-port</code> returns the UTF-8 decoding of the blob associated with the port when <var>proc</var> returns. If a transcoder <var>transcoder</var> is specified, the port is appropriately transcoded.</p></dd><dt class="proc def"><code>(call-with-output-port </code><var>output-port</var> <var>proc</var><code>)</code></dt><dd><p>This calls <var>proc</var> with <var>output-port</var> as an argument. If <var>proc</var> returns, then the port is closed automatically and the values returned by <var>proc</var> are returned. If <var>proc</var> does not return, then the port will not be closed automatically, unless it is possible to prove that the port will never again be used for a write operation.</p></dd><dt class="proc def"><code>(standard-output-port)</code></dt><dt class="proc def"><code>(standard-error-port)</code></dt><dd><p>Returns a port connected to the standard output or standard error, respectively.</p></dd></dl><h3>Opening files for reading and writing</h3><dl><dt class="proc def"><code>(open-file-input+output-ports </code><var>filename</var><code>)</code></dt><dt class="proc def"><code>(open-file-input+output-ports </code><var>filename</var> <var>file-options</var><code>)</code></dt><dt class="proc def"><code>(open-file-input+output-ports </code><var>filename</var> <var>file-options</var> <var>transcoder</var><code>)</code></dt><dd><p>This returns two values, an input port and an output port for the named file and the specified options (which defaults to <code>(file-options)</code>.) The ports may or may not be stream ports. If a transcoder <var>transcoder</var> is specified, the ports are appropriately transcoded.</p></dd></dl><h3>Ports from readers and writers</h3><dl><dt class="proc def"><code>(open-reader-input-port </code><var>reader</var><code>)</code></dt><dt class="proc def"><code>(open-reader-input-port </code><var>reader</var> <var>transcoder</var><code>)</code></dt><dd><p>This returns an input port connected to the reader <var>reader</var>.If a transcoder <var>transcoder</var> is specified, the port is appropriately transcoded.</p></dd><dt class="proc def"><code>(open-writer-output-port </code><var>writer</var> <var>buffer-mode</var><code>)</code></dt><dt class="proc def"><code>(open-writer-output-port </code><var>writer</var> <var>buffer-mode</var> <var>transcoder</var><code>)</code></dt><dd><p>This returns an output port connected to the writer <var>writer</var> with buffering according to <var>buffer-mode</var>.If a transcoder <var>transcoder</var> is specified, the port is appropriately transcoded.</p></dd></dl><h1>Design rationale</h1><h3>Encoding</h3><p>Many I/O system implementations allow associating an encoding with a port, allowing the direct use of several different encodings with ports. The problem with this approach is that the encoding/decoding defines a mapping from binary data to text or vice versa. Because of this asymmetry, such mappings do not compose. The result is usually complications and restrictions in the I/O API, such as the inability to mix text or binary data.</p><p>This SRFI avoids this problem by specifying that textual I/O always uses UTF-8. This means that, if the target or source of an I/O port is to use a different encoding, a translated port needs to be used, for which this SRFI offers the required facilities. This means that text decoders or encoders are expressed as binary-to-binary mappings, and as such compose.</p><h3><code>display</code> vs <code>write</code></h3><p>R5RS calls the procedures for writing something to an output port <code>write-<something></code>. In a previous revision of this SRFI, all were called <code>display-<something></code>. R5RS doesn't offer a consistent rule for naming, as the <code>display</code> and <code>write-char</code> procedures behave identically on character arguments, wherease <code>write</code> and <code>write-char</code> do not.</p><p>Historically, it seems that the <a href="http://zurich.ai.mit.edu/pipermail/rrrs-authors/1985-March.txt">original proposal for the I/O subsystem in RnRS</a> indeed called the procedure <code>display-char</code>. I do not know why it was renamed---probably for compatibility with Common Lisp, which also has <code>write-char</code>.</p><p>While the procedures in this SRFI follow a consistent naming scheme, consistency is an issue for what's <code>read</code> and <code>write</code>in R5RS. The naming scheme proposed here suggests they be called <code>read-datum</code> and <code>write-datum</code>.</p><h3><code>char-ready?</code></h3><p>This SRFI intentionally does not provide <code>char-ready?</code>, which is part of R5RS. The original intention of the procedure seems to have been to interface with something like Unix <code>select(2)</code>. With multi-byte encodings such as UTF-8, this is no longer sufficient: the procedure would really have to look at the actual input data in order to determine whether a complete character is actually present. This makes realistic implementations of <code>char-ready?</code> inconsistent with the user's expectations. A procedure <code>byte-ready?</code> would be more consistent. On the other hand, such a procedure is rarely useful in real-world programs, hard to specify to the point where it would be portably usable, and complicates all layers of the I/O system, as readers would have to provide an additional member procedure to enable its implementation. Moreover, a <code>select(2)</code>-like implementation is not possible on all plattforms and all types of ports. Consequently, <code>char-ready?</code> and <code>byte-ready?</code> are not part of this SRFI.</p><h3><code>display</code></h3><p>This SRFI does not provide <code>display</code>, which is part of R5RS. <code>Display</code> is woefully underspecified, and mostly used for debug output. It seems <code>display</code> should be replaced by a procedure for formatted output, possibly augmented by handling of dynamically-bound "current ports".</p><h3>Optional ports and argument order</h3><p>The argument order of the procedures in this SRFI is different from R5RS: The port is always at the beginning, and it is mandatory.For a rationale, see <a href="http://srfi.schemers.org/srfi-68/mail-archive/msg00031.html">the message by Taylor Campbell</a> on the subject.</p><h3>No distinct end of file object</h3><p>In R5RS, the distinct type of end of file objects is primarily for the benefit of <code>read</code>, where end of file must be denoted by an object that <code>read</code> cannot normally return as a result of parsing the input. However, it does not seem necessary to drag in the complications of this separate object into the other I/O operations, where <code>#f</code> is perfectly adequate to represent end of file.</p><h1>Reference Implementation</h1><p><a href="https://srfi.schemers.org/srfi-79/srfi-79.tgz">Here</a> is a tarball containing a reference implementation of this SRFI, along with implementations for <a href="http://srfi.schemers.org/srfi-79/">SRFI 79 (Primitive I/O)</a>, <a href="http://srfi.schemers.org/srfi-80/">SRFI 80 (Stream I/O)</a>, and <a href="http://srfi.schemers.org/srfi-82/">SRFI 82 (Stream-Port I/O)</a>. It only runs on a version of Scheme 48 that has not been released at the time of writing in this SRFI.</p><p>However, its actual dependencies on Scheme 48 idiosyncracies are few. Chief are its use of the module system, which is easily replaced by another, and the implementation of Unicode. To implement primitive readers and writers on files, the code only relies on suitable library procedures to open the files, and <code>read-byte</code> and <code>write-byte</code> procedures to read or write single bytes from a (R5RS) port, as well as a <code>force-output</code> procedure to flush a port.</p><p>The reference implementation has not been highly tuned, but I have spent a modest amount of time making the code deal with buffers in an economic manner. Because of this, the code is more complicated than it needs to be, but hopefully also more usable as a basis for implementing this SRFI in actual Scheme systems.</p><h1>Examples</h1><p>Many examples are adapted from <a href="http://www.standardml.org/Basis/">The Standard ML Basis Library</a> edited by Emden R. Gansner and John H. Reppy. Cambrige University Press, 2004.</p><p>The code makes liberal use of SRFIs <a href="http://srfi.schemers.org/srfi-1/"> SRFI 1 (List Library)</a>, <a href="http://srfi.schemers.org/srfi-11/">SRFI 11 (Syntax for receiving multiple values)</a>, <a href="http://srfi.schemers.org/srfi-26/">SRFI 26 (Notation for Specializing Parameters without Currying)</a>.</p><p>The tarball with the reference implementation contains these examples along with test cases for them.</p><pre>(define three-lines-string
(call-with-string-output-port
(lambda (port)
(write-string port "foo") (newline port)
(write-string port "bar") (newline port)
(write-string port "baz") (newline port))))
</pre><p>Read a file directly:</p><pre>(define (get-contents filename)
(call-with-input-port (open-file-input-port filename)
read-blob-all))
</pre><p>Read a file octet-by-octet:</p><pre>(define (get-contents-2 filename)
(call-with-input-port (open-file-input-port filename)
(lambda (port)
(let loop ((accum '()))
(let ((thing (read-u8 port)))
(if (not thing)
(list->blob (reverse accum))
(loop (cons thing accum))))))))
(define (list->blob l)
(let ((blob (make-blob (length l))))
(let loop ((i 0) (l l))
(if (null? l)
blob
(begin
(blob-u8-set! blob i (car l))
(loop (+ 1 i) (cdr l)))))))
</pre><p>Read file chunk-by-chunk:</p><pre>(define (get-contents-3 filename)
(call-with-input-port (open-file-input-port filename)
(lambda (port)
(let loop ((accum '()))
(cond
((read-blob-some port)
=> (lambda (blob)
(loop (cons blob accum))))
(else
(concatenate-blobs (reverse accum))))))))
(define (concatenate-blobs list)
(let* ((size (fold + 0 (map blob-length list)))
(result (make-blob size)))
(let loop ((index 0)
(blobs list))
(if (null? blobs)
result
(let* ((b (car blobs))
(size (blob-length b)))
(blob-copy! b 0 result index size)
(loop (+ index size)
(cdr blobs)))))))
</pre><h1>Acknowledgements</h1><p>Sebastian Egner provided valuable comments on a draft of this SRFI. The posters to the <a href="http://srfi.schemers.org/srfi-68/">SRFI 68 (Comprehensive I/O)</a> provided many very valuable comments. Donovan Kolbly did thorough pre-draft editing. Any remaining mistakes are mine.</p><h1>References</h1><ul><li><a href="http://www.standardml.org/Basis/">The Standard ML Basis Library</a> edited by Emden R. Gansner and John H. Reppy. Cambrige University Press, 2004.</li></ul><H1>Copyright</H1>
Copyright (C) Michael Sperber (2005). All Rights Reserved.
<p>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
<p>
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
<p>
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
<hr>
<address>Editor: <a href="mailto:srfi minus editors at srfi dot schemers dot org">Donovan Kolbly</a></address>
</body></html>