Improve the usage of variable names in Scheme docstrings.
[bpt/guile.git] / module / rnrs / io / ports.scm
1 ;;;; ports.scm --- R6RS port API -*- coding: utf-8 -*-
2
3 ;;;; Copyright (C) 2009, 2010, 2011 Free Software Foundation, Inc.
4 ;;;;
5 ;;;; This library is free software; you can redistribute it and/or
6 ;;;; modify it under the terms of the GNU Lesser General Public
7 ;;;; License as published by the Free Software Foundation; either
8 ;;;; version 3 of the License, or (at your option) any later version.
9 ;;;;
10 ;;;; This library is distributed in the hope that it will be useful,
11 ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 ;;;; Lesser General Public License for more details.
14 ;;;;
15 ;;;; You should have received a copy of the GNU Lesser General Public
16 ;;;; License along with this library; if not, write to the Free Software
17 ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
19 ;;; Author: Ludovic Courtès <ludo@gnu.org>
20
21 ;;; Commentary:
22 ;;;
23 ;;; The I/O port API of the R6RS is provided by this module. In many areas
24 ;;; it complements or refines Guile's own historical port API. For instance,
25 ;;; it allows for binary I/O with bytevectors.
26 ;;;
27 ;;; Code:
28
29 (library (rnrs io ports (6))
30 (export eof-object eof-object?
31
32 ;; auxiliary types
33 file-options buffer-mode buffer-mode?
34 eol-style native-eol-style error-handling-mode
35 make-transcoder transcoder-codec transcoder-eol-style
36 transcoder-error-handling-mode native-transcoder
37 latin-1-codec utf-8-codec utf-16-codec
38
39 ;; input & output ports
40 port? input-port? output-port?
41 port-eof?
42 port-transcoder binary-port? textual-port? transcoded-port
43 port-position set-port-position!
44 port-has-port-position? port-has-set-port-position!?
45 call-with-port close-port
46
47 ;; input ports
48 open-bytevector-input-port
49 open-string-input-port
50 open-file-input-port
51 make-custom-binary-input-port
52
53 ;; binary input
54 get-u8 lookahead-u8
55 get-bytevector-n get-bytevector-n!
56 get-bytevector-some get-bytevector-all
57
58 ;; output ports
59 open-bytevector-output-port
60 open-string-output-port
61 open-file-output-port
62 make-custom-binary-output-port
63 call-with-bytevector-output-port
64 call-with-string-output-port
65 make-custom-textual-output-port
66 flush-output-port
67
68 ;; binary output
69 put-u8 put-bytevector
70
71 ;; textual input
72 get-char get-datum get-line get-string-all get-string-n get-string-n!
73 lookahead-char
74
75 ;; textual output
76 put-char put-datum put-string
77
78 ;; standard ports
79 standard-input-port standard-output-port standard-error-port
80 current-input-port current-output-port current-error-port
81
82 ;; condition types
83 &i/o i/o-error? make-i/o-error
84 &i/o-read i/o-read-error? make-i/o-read-error
85 &i/o-write i/o-write-error? make-i/o-write-error
86 &i/o-invalid-position i/o-invalid-position-error?
87 make-i/o-invalid-position-error
88 &i/o-filename i/o-filename-error? make-i/o-filename-error
89 i/o-error-filename
90 &i/o-file-protection i/o-file-protection-error?
91 make-i/o-file-protection-error
92 &i/o-file-is-read-only i/o-file-is-read-only-error?
93 make-i/o-file-is-read-only-error
94 &i/o-file-already-exists i/o-file-already-exists-error?
95 make-i/o-file-already-exists-error
96 &i/o-file-does-not-exist i/o-file-does-not-exist-error?
97 make-i/o-file-does-not-exist-error
98 &i/o-port i/o-port-error? make-i/o-port-error
99 i/o-error-port
100 &i/o-decoding-error i/o-decoding-error?
101 make-i/o-decoding-error
102 &i/o-encoding-error i/o-encoding-error?
103 make-i/o-encoding-error i/o-encoding-error-char)
104 (import (ice-9 binary-ports)
105 (only (rnrs base) assertion-violation)
106 (rnrs enums)
107 (rnrs records syntactic)
108 (rnrs exceptions)
109 (rnrs conditions)
110 (rnrs files) ;for the condition types
111 (srfi srfi-8)
112 (ice-9 rdelim)
113 (except (guile) raise display)
114 (prefix (only (guile) display)
115 guile:))
116
117
118 \f
119 ;;;
120 ;;; Auxiliary types
121 ;;;
122
123 (define-enumeration file-option
124 (no-create no-fail no-truncate)
125 file-options)
126
127 (define-enumeration buffer-mode
128 (none line block)
129 buffer-modes)
130
131 (define (buffer-mode? symbol)
132 (enum-set-member? symbol (enum-set-universe (buffer-modes))))
133
134 (define-enumeration eol-style
135 (lf cr crlf nel crnel ls none)
136 eol-styles)
137
138 (define (native-eol-style)
139 (eol-style none))
140
141 (define-enumeration error-handling-mode
142 (ignore raise replace)
143 error-handling-modes)
144
145 (define-record-type (transcoder %make-transcoder transcoder?)
146 (fields codec eol-style error-handling-mode))
147
148 (define* (make-transcoder codec
149 #:optional
150 (eol-style (native-eol-style))
151 (handling-mode (error-handling-mode replace)))
152 (%make-transcoder codec eol-style handling-mode))
153
154 (define (native-transcoder)
155 (make-transcoder (or (fluid-ref %default-port-encoding)
156 (latin-1-codec))))
157
158 (define (latin-1-codec)
159 "ISO-8859-1")
160
161 (define (utf-8-codec)
162 "UTF-8")
163
164 (define (utf-16-codec)
165 "UTF-16")
166
167 \f
168 ;;;
169 ;;; Internal helpers
170 ;;;
171
172 (define (with-i/o-filename-conditions filename thunk)
173 (with-throw-handler 'system-error
174 thunk
175 (lambda args
176 (let ((errno (system-error-errno args)))
177 (let ((construct-condition
178 (cond ((= errno EACCES)
179 make-i/o-file-protection-error)
180 ((= errno EEXIST)
181 make-i/o-file-already-exists-error)
182 ((= errno ENOENT)
183 make-i/o-file-does-not-exist-error)
184 ((= errno EROFS)
185 make-i/o-file-is-read-only-error)
186 (else
187 make-i/o-filename-error))))
188 (raise (construct-condition filename)))))))
189
190 (define (with-i/o-port-error port make-primary-condition thunk)
191 (with-throw-handler 'system-error
192 thunk
193 (lambda args
194 (let ((errno (system-error-errno args)))
195 (if (memv errno (list EIO EFBIG ENOSPC EPIPE))
196 (raise (condition (make-primary-condition)
197 (make-i/o-port-error port)))
198 (apply throw args))))))
199
200 (define-syntax with-textual-output-conditions
201 (syntax-rules ()
202 ((_ port body0 body ...)
203 (with-i/o-port-error port make-i/o-write-error
204 (lambda () (with-i/o-encoding-error body0 body ...))))))
205
206 (define-syntax with-textual-input-conditions
207 (syntax-rules ()
208 ((_ port body0 body ...)
209 (with-i/o-port-error port make-i/o-read-error
210 (lambda () (with-i/o-decoding-error body0 body ...))))))
211
212 \f
213 ;;;
214 ;;; Input and output ports.
215 ;;;
216
217 (define (port-transcoder port)
218 "Return the transcoder object associated with @var{port}, or @code{#f}
219 if the port has no transcoder."
220 (cond ((port-encoding port)
221 => (lambda (encoding)
222 (make-transcoder
223 encoding
224 (native-eol-style)
225 (case (port-conversion-strategy port)
226 ((error) 'raise)
227 ((substitute) 'replace)
228 (else
229 (assertion-violation 'port-transcoder
230 "unsupported error handling mode"))))))
231 (else
232 #f)))
233
234 (define (binary-port? port)
235 "Returns @code{#t} if @var{port} does not have an associated encoding,
236 @code{#f} otherwise."
237 (not (port-encoding port)))
238
239 (define (textual-port? port)
240 "Always returns @code{#t}, as all ports can be used for textual I/O in
241 Guile."
242 #t)
243
244 (define (port-eof? port)
245 (eof-object? (if (binary-port? port)
246 (lookahead-u8 port)
247 (lookahead-char port))))
248
249 (define (transcoded-port port transcoder)
250 "Return a new textual port based on @var{port}, using
251 @var{transcoder} to encode and decode data written to or
252 read from its underlying binary port @var{port}."
253 ;; Hackily get at %make-transcoded-port.
254 (let ((result ((@@ (ice-9 binary-ports) %make-transcoded-port) port)))
255 (set-port-encoding! result (transcoder-codec transcoder))
256 (case (transcoder-error-handling-mode transcoder)
257 ((raise)
258 (set-port-conversion-strategy! result 'error))
259 ((replace)
260 (set-port-conversion-strategy! result 'substitute))
261 (else
262 (error "unsupported error handling mode"
263 (transcoder-error-handling-mode transcoder))))
264 result))
265
266 (define (port-position port)
267 "Return the offset (an integer) indicating where the next octet will be
268 read from/written to in @var{port}."
269
270 ;; FIXME: We should raise an `&assertion' error when not supported.
271 (seek port 0 SEEK_CUR))
272
273 (define (set-port-position! port offset)
274 "Set the position where the next octet will be read from/written to
275 @var{port}."
276
277 ;; FIXME: We should raise an `&assertion' error when not supported.
278 (seek port offset SEEK_SET))
279
280 (define (port-has-port-position? port)
281 "Return @code{#t} is @var{port} supports @code{port-position}."
282 (and (false-if-exception (port-position port)) #t))
283
284 (define (port-has-set-port-position!? port)
285 "Return @code{#t} is @var{port} supports @code{set-port-position!}."
286 (and (false-if-exception (set-port-position! port (port-position port)))
287 #t))
288
289 (define (call-with-port port proc)
290 "Call @var{proc}, passing it @var{port} and closing @var{port} upon exit of
291 @var{proc}. Return the return values of @var{proc}."
292 (call-with-values
293 (lambda () (proc port))
294 (lambda vals
295 (close-port port)
296 (apply values vals))))
297
298 (define* (call-with-bytevector-output-port proc #:optional (transcoder #f))
299 (receive (port extract) (open-bytevector-output-port transcoder)
300 (call-with-port port proc)
301 (extract)))
302
303 (define (open-string-input-port str)
304 "Open an input port that will read from @var{str}."
305 (with-fluids ((%default-port-encoding "UTF-8"))
306 (open-input-string str)))
307
308 (define* (open-file-input-port filename
309 #:optional
310 (file-options (file-options))
311 (buffer-mode (buffer-mode block))
312 maybe-transcoder)
313 (let ((port (with-i/o-filename-conditions filename
314 (lambda ()
315 (with-fluids ((%default-port-encoding #f))
316 (open filename O_RDONLY))))))
317 (cond (maybe-transcoder
318 (set-port-encoding! port (transcoder-codec maybe-transcoder))))
319 port))
320
321 (define (open-string-output-port)
322 "Return two values: an output port that will collect characters written to it
323 as a string, and a thunk to retrieve the characters associated with that port."
324 (let ((port (with-fluids ((%default-port-encoding "UTF-8"))
325 (open-output-string))))
326 (values port
327 (lambda () (get-output-string port)))))
328
329 (define* (open-file-output-port filename
330 #:optional
331 (file-options (file-options))
332 (buffer-mode (buffer-mode block))
333 maybe-transcoder)
334 (let* ((flags (logior O_WRONLY
335 (if (enum-set-member? 'no-create file-options)
336 0
337 O_CREAT)
338 (if (enum-set-member? 'no-truncate file-options)
339 0
340 O_TRUNC)
341 (if (enum-set-member? 'no-fail file-options)
342 0
343 O_EXCL)))
344 (port (with-i/o-filename-conditions filename
345 (lambda ()
346 (with-fluids ((%default-port-encoding #f))
347 (open filename flags))))))
348 (cond (maybe-transcoder
349 (set-port-encoding! port (transcoder-codec maybe-transcoder))))
350 port))
351
352 (define (call-with-string-output-port proc)
353 "Call @var{proc}, passing it a string output port. When @var{proc} returns,
354 return the characters accumulated in that port."
355 (let ((port (open-output-string)))
356 (proc port)
357 (get-output-string port)))
358
359 (define (make-custom-textual-output-port id
360 write!
361 get-position
362 set-position!
363 close)
364 (make-soft-port (vector (lambda (c) (write! (string c) 0 1))
365 (lambda (s) (write! s 0 (string-length s)))
366 #f ;flush
367 #f ;read character
368 close)
369 "w"))
370
371 (define (flush-output-port port)
372 (force-output port))
373
374 \f
375 ;;;
376 ;;; Textual output.
377 ;;;
378
379 (define-condition-type &i/o-encoding &i/o-port
380 make-i/o-encoding-error i/o-encoding-error?
381 (char i/o-encoding-error-char))
382
383 (define-syntax with-i/o-encoding-error
384 (syntax-rules ()
385 "Convert Guile throws to `encoding-error' to `&i/o-encoding-error'."
386 ((_ body ...)
387 ;; XXX: This is heavyweight for small functions like `put-char'.
388 (with-throw-handler 'encoding-error
389 (lambda ()
390 (begin body ...))
391 (lambda (key subr message errno port chr)
392 (raise (make-i/o-encoding-error port chr)))))))
393
394 (define (put-char port char)
395 (with-textual-output-conditions port (write-char char port)))
396
397 (define (put-datum port datum)
398 (with-textual-output-conditions port (write datum port)))
399
400 (define* (put-string port s #:optional start count)
401 (with-textual-output-conditions port
402 (cond ((not (string? s))
403 (assertion-violation 'put-string "expected string" s))
404 ((and start count)
405 (display (substring/shared s start (+ start count)) port))
406 (start
407 (display (substring/shared s start (string-length s)) port))
408 (else
409 (display s port)))))
410
411 ;; Defined here to be able to make use of `with-i/o-encoding-error', but
412 ;; not exported from here, but from `(rnrs io simple)'.
413 (define* (display object #:optional (port (current-output-port)))
414 (with-textual-output-conditions port (guile:display object port)))
415
416 \f
417 ;;;
418 ;;; Textual input.
419 ;;;
420
421 (define-condition-type &i/o-decoding &i/o-port
422 make-i/o-decoding-error i/o-decoding-error?)
423
424 (define-syntax with-i/o-decoding-error
425 (syntax-rules ()
426 "Convert Guile throws to `decoding-error' to `&i/o-decoding-error'."
427 ((_ body ...)
428 ;; XXX: This is heavyweight for small functions like `get-char' and
429 ;; `lookahead-char'.
430 (with-throw-handler 'decoding-error
431 (lambda ()
432 (begin body ...))
433 (lambda (key subr message errno port)
434 (raise (make-i/o-decoding-error port)))))))
435
436 (define (get-char port)
437 (with-textual-input-conditions port (read-char port)))
438
439 (define (get-datum port)
440 (with-textual-input-conditions port (read port)))
441
442 (define (get-line port)
443 (with-textual-input-conditions port (read-line port 'trim)))
444
445 (define (get-string-all port)
446 (with-textual-input-conditions port (read-delimited "" port 'concat)))
447
448 (define (get-string-n port count)
449 "Read up to @var{count} characters from @var{port}.
450 If no characters could be read before encountering the end of file,
451 return the end-of-file object, otherwise return a string containing
452 the characters read."
453 (let* ((s (make-string count))
454 (rv (get-string-n! port s 0 count)))
455 (cond ((eof-object? rv) rv)
456 ((= rv count) s)
457 (else (substring/shared s 0 rv)))))
458
459 (define (lookahead-char port)
460 (with-textual-input-conditions port (peek-char port)))
461
462 \f
463 ;;;
464 ;;; Standard ports.
465 ;;;
466
467 (define (standard-input-port)
468 (with-fluids ((%default-port-encoding #f))
469 (dup->inport 0)))
470
471 (define (standard-output-port)
472 (with-fluids ((%default-port-encoding #f))
473 (dup->outport 1)))
474
475 (define (standard-error-port)
476 (with-fluids ((%default-port-encoding #f))
477 (dup->outport 2)))
478
479 )
480
481 ;;; ports.scm ends here