reword open-file docs
[bpt/guile.git] / libguile / r6rs-ports.c
CommitLineData
a6c377f7 1/* Copyright (C) 2009, 2010, 2011 Free Software Foundation, Inc.
1ee2c72e
LC
2 *
3 * This library is free software; you can redistribute it and/or
53befeb7
NJ
4 * modify it under the terms of the GNU Lesser General Public License
5 * as published by the Free Software Foundation; either version 3 of
6 * the License, or (at your option) any later version.
1ee2c72e 7 *
53befeb7
NJ
8 * This library is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
1ee2c72e
LC
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with this library; if not, write to the Free Software
53befeb7
NJ
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
16 * 02110-1301 USA
1ee2c72e
LC
17 */
18
19#ifdef HAVE_CONFIG_H
20# include <config.h>
21#endif
22
23#ifdef HAVE_UNISTD_H
24# include <unistd.h>
25#endif
26
27#include <string.h>
28#include <stdio.h>
29#include <assert.h>
30
31#include "libguile/_scm.h"
32#include "libguile/bytevectors.h"
33#include "libguile/chars.h"
34#include "libguile/eval.h"
35#include "libguile/r6rs-ports.h"
36#include "libguile/strings.h"
37#include "libguile/validate.h"
38#include "libguile/values.h"
39#include "libguile/vectors.h"
40
41
42\f
43/* Unimplemented features. */
44
45
46/* Transoders are currently not implemented since Guile 1.8 is not
47 Unicode-capable. Thus, most of the code here assumes the use of the
48 binary transcoder. */
49static inline void
50transcoders_not_implemented (void)
51{
52 fprintf (stderr, "%s: warning: transcoders not implemented\n",
53 PACKAGE_NAME);
54}
55
56\f
57/* End-of-file object. */
58
59SCM_DEFINE (scm_eof_object, "eof-object", 0, 0, 0,
60 (void),
61 "Return the end-of-file object.")
62#define FUNC_NAME s_scm_eof_object
63{
64 return (SCM_EOF_VAL);
65}
66#undef FUNC_NAME
67
68\f
69/* Input ports. */
70
71#ifndef MIN
72# define MIN(a,b) ((a) < (b) ? (a) : (b))
73#endif
74
75/* Bytevector input ports or "bip" for short. */
76static scm_t_bits bytevector_input_port_type = 0;
77
78static inline SCM
79make_bip (SCM bv)
80{
81 SCM port;
82 char *c_bv;
83 unsigned c_len;
84 scm_t_port *c_port;
85 const unsigned long mode_bits = SCM_OPN | SCM_RDNG;
86
a653d32a
AR
87 scm_i_scm_pthread_mutex_lock (&scm_i_port_table_mutex);
88
1ee2c72e 89 port = scm_new_port_table_entry (bytevector_input_port_type);
96128014
LC
90 c_port = SCM_PTAB_ENTRY (port);
91
92 /* Match the expectation of `binary-port?'. */
93 c_port->encoding = NULL;
1ee2c72e
LC
94
95 /* Prevent BV from being GC'd. */
96 SCM_SETSTREAM (port, SCM_UNPACK (bv));
97
98 /* Have the port directly access the bytevector. */
99 c_bv = (char *) SCM_BYTEVECTOR_CONTENTS (bv);
100 c_len = SCM_BYTEVECTOR_LENGTH (bv);
101
1ee2c72e
LC
102 c_port->read_pos = c_port->read_buf = (unsigned char *) c_bv;
103 c_port->read_end = (unsigned char *) c_bv + c_len;
104 c_port->read_buf_size = c_len;
105
106 /* Mark PORT as open, readable and unbuffered (hmm, how elegant...). */
107 SCM_SET_CELL_TYPE (port, bytevector_input_port_type | mode_bits);
108
a653d32a
AR
109 scm_i_pthread_mutex_unlock (&scm_i_port_table_mutex);
110
1ee2c72e
LC
111 return port;
112}
113
1ee2c72e
LC
114static int
115bip_fill_input (SCM port)
116{
117 int result;
118 scm_t_port *c_port = SCM_PTAB_ENTRY (port);
119
120 if (c_port->read_pos >= c_port->read_end)
121 result = EOF;
122 else
123 result = (int) *c_port->read_pos;
124
125 return result;
126}
127
f1ce9199
LC
128static scm_t_off
129bip_seek (SCM port, scm_t_off offset, int whence)
1ee2c72e
LC
130#define FUNC_NAME "bip_seek"
131{
f1ce9199 132 scm_t_off c_result = 0;
1ee2c72e
LC
133 scm_t_port *c_port = SCM_PTAB_ENTRY (port);
134
135 switch (whence)
136 {
137 case SEEK_CUR:
138 offset += c_port->read_pos - c_port->read_buf;
139 /* Fall through. */
140
141 case SEEK_SET:
c89b4529 142 if (c_port->read_buf + offset <= c_port->read_end)
1ee2c72e
LC
143 {
144 c_port->read_pos = c_port->read_buf + offset;
145 c_result = offset;
146 }
147 else
148 scm_out_of_range (FUNC_NAME, scm_from_int (offset));
149 break;
150
151 case SEEK_END:
152 if (c_port->read_end - offset >= c_port->read_buf)
153 {
154 c_port->read_pos = c_port->read_end - offset;
155 c_result = c_port->read_pos - c_port->read_buf;
156 }
157 else
158 scm_out_of_range (FUNC_NAME, scm_from_int (offset));
159 break;
160
161 default:
162 scm_wrong_type_arg_msg (FUNC_NAME, 0, port,
163 "invalid `seek' parameter");
164 }
165
166 return c_result;
167}
168#undef FUNC_NAME
169
170
171/* Instantiate the bytevector input port type. */
172static inline void
173initialize_bytevector_input_ports (void)
174{
175 bytevector_input_port_type =
176 scm_make_port_type ("r6rs-bytevector-input-port", bip_fill_input,
177 NULL);
178
1ee2c72e
LC
179 scm_set_port_seek (bytevector_input_port_type, bip_seek);
180}
181
182
183SCM_DEFINE (scm_open_bytevector_input_port,
184 "open-bytevector-input-port", 1, 1, 0,
185 (SCM bv, SCM transcoder),
186 "Return an input port whose contents are drawn from "
187 "bytevector @var{bv}.")
188#define FUNC_NAME s_scm_open_bytevector_input_port
189{
190 SCM_VALIDATE_BYTEVECTOR (1, bv);
191 if (!SCM_UNBNDP (transcoder) && !scm_is_false (transcoder))
192 transcoders_not_implemented ();
193
194 return (make_bip (bv));
195}
196#undef FUNC_NAME
197
198\f
199/* Custom binary ports. The following routines are shared by input and
200 output custom binary ports. */
201
202#define SCM_CBP_GET_POSITION_PROC(_port) \
203 SCM_SIMPLE_VECTOR_REF (SCM_PACK (SCM_STREAM (_port)), 1)
204#define SCM_CBP_SET_POSITION_PROC(_port) \
205 SCM_SIMPLE_VECTOR_REF (SCM_PACK (SCM_STREAM (_port)), 2)
206#define SCM_CBP_CLOSE_PROC(_port) \
207 SCM_SIMPLE_VECTOR_REF (SCM_PACK (SCM_STREAM (_port)), 3)
208
f1ce9199
LC
209static scm_t_off
210cbp_seek (SCM port, scm_t_off offset, int whence)
1ee2c72e
LC
211#define FUNC_NAME "cbp_seek"
212{
213 SCM result;
f1ce9199 214 scm_t_off c_result = 0;
1ee2c72e
LC
215
216 switch (whence)
217 {
218 case SEEK_CUR:
219 {
220 SCM get_position_proc;
221
222 get_position_proc = SCM_CBP_GET_POSITION_PROC (port);
223 if (SCM_LIKELY (scm_is_true (get_position_proc)))
224 result = scm_call_0 (get_position_proc);
225 else
226 scm_wrong_type_arg_msg (FUNC_NAME, 0, port,
227 "R6RS custom binary port does not "
228 "support `port-position'");
229
230 offset += scm_to_int (result);
231 /* Fall through. */
232 }
233
234 case SEEK_SET:
235 {
236 SCM set_position_proc;
237
238 set_position_proc = SCM_CBP_SET_POSITION_PROC (port);
239 if (SCM_LIKELY (scm_is_true (set_position_proc)))
240 result = scm_call_1 (set_position_proc, scm_from_int (offset));
241 else
242 scm_wrong_type_arg_msg (FUNC_NAME, 0, port,
243 "R6RS custom binary port does not "
244 "support `set-port-position!'");
245
246 /* Assuming setting the position succeeded. */
247 c_result = offset;
248 break;
249 }
250
251 default:
252 /* `SEEK_END' cannot be supported. */
253 scm_wrong_type_arg_msg (FUNC_NAME, 0, port,
254 "R6RS custom binary ports do not "
255 "support `SEEK_END'");
256 }
257
258 return c_result;
259}
260#undef FUNC_NAME
261
262static int
263cbp_close (SCM port)
264{
265 SCM close_proc;
266
267 close_proc = SCM_CBP_CLOSE_PROC (port);
268 if (scm_is_true (close_proc))
269 /* Invoke the `close' thunk. */
270 scm_call_0 (close_proc);
271
272 return 1;
273}
274
275\f
276/* Custom binary input port ("cbip" for short). */
277
278static scm_t_bits custom_binary_input_port_type = 0;
279
280/* Size of the buffer embedded in custom binary input ports. */
281#define CBIP_BUFFER_SIZE 4096
282
283/* Return the bytevector associated with PORT. */
284#define SCM_CBIP_BYTEVECTOR(_port) \
285 SCM_SIMPLE_VECTOR_REF (SCM_PACK (SCM_STREAM (_port)), 4)
286
287/* Return the various procedures of PORT. */
288#define SCM_CBIP_READ_PROC(_port) \
289 SCM_SIMPLE_VECTOR_REF (SCM_PACK (SCM_STREAM (_port)), 0)
290
291
292static inline SCM
293make_cbip (SCM read_proc, SCM get_position_proc,
294 SCM set_position_proc, SCM close_proc)
295{
296 SCM port, bv, method_vector;
297 char *c_bv;
298 unsigned c_len;
299 scm_t_port *c_port;
300 const unsigned long mode_bits = SCM_OPN | SCM_RDNG;
301
302 /* Use a bytevector as the underlying buffer. */
303 c_len = CBIP_BUFFER_SIZE;
304 bv = scm_c_make_bytevector (c_len);
305 c_bv = (char *) SCM_BYTEVECTOR_CONTENTS (bv);
306
307 /* Store the various methods and bytevector in a vector. */
308 method_vector = scm_c_make_vector (5, SCM_BOOL_F);
309 SCM_SIMPLE_VECTOR_SET (method_vector, 4, bv);
310 SCM_SIMPLE_VECTOR_SET (method_vector, 0, read_proc);
311 SCM_SIMPLE_VECTOR_SET (method_vector, 1, get_position_proc);
312 SCM_SIMPLE_VECTOR_SET (method_vector, 2, set_position_proc);
313 SCM_SIMPLE_VECTOR_SET (method_vector, 3, close_proc);
314
a653d32a
AR
315 scm_i_pthread_mutex_lock (&scm_i_port_table_mutex);
316
1ee2c72e 317 port = scm_new_port_table_entry (custom_binary_input_port_type);
96128014
LC
318 c_port = SCM_PTAB_ENTRY (port);
319
320 /* Match the expectation of `binary-port?'. */
321 c_port->encoding = NULL;
1ee2c72e
LC
322
323 /* Attach it the method vector. */
324 SCM_SETSTREAM (port, SCM_UNPACK (method_vector));
325
326 /* Have the port directly access the buffer (bytevector). */
1ee2c72e
LC
327 c_port->read_pos = c_port->read_buf = (unsigned char *) c_bv;
328 c_port->read_end = (unsigned char *) c_bv;
329 c_port->read_buf_size = c_len;
330
331 /* Mark PORT as open, readable and unbuffered (hmm, how elegant...). */
332 SCM_SET_CELL_TYPE (port, custom_binary_input_port_type | mode_bits);
333
a653d32a
AR
334 scm_i_pthread_mutex_unlock (&scm_i_port_table_mutex);
335
1ee2c72e
LC
336 return port;
337}
338
339static int
340cbip_fill_input (SCM port)
341#define FUNC_NAME "cbip_fill_input"
342{
343 int result;
344 scm_t_port *c_port = SCM_PTAB_ENTRY (port);
345
346 again:
347 if (c_port->read_pos >= c_port->read_end)
348 {
349 /* Invoke the user's `read!' procedure. */
350 unsigned c_octets;
351 SCM bv, read_proc, octets;
352
353 /* Use the bytevector associated with PORT as the buffer passed to the
354 `read!' procedure, thereby avoiding additional allocations. */
355 bv = SCM_CBIP_BYTEVECTOR (port);
356 read_proc = SCM_CBIP_READ_PROC (port);
357
358 /* The assumption here is that C_PORT's internal buffer wasn't changed
359 behind our back. */
360 assert (c_port->read_buf ==
361 (unsigned char *) SCM_BYTEVECTOR_CONTENTS (bv));
362 assert ((unsigned) c_port->read_buf_size
363 == SCM_BYTEVECTOR_LENGTH (bv));
364
365 octets = scm_call_3 (read_proc, bv, SCM_INUM0,
366 SCM_I_MAKINUM (CBIP_BUFFER_SIZE));
367 c_octets = scm_to_uint (octets);
368
369 c_port->read_pos = (unsigned char *) SCM_BYTEVECTOR_CONTENTS (bv);
370 c_port->read_end = (unsigned char *) c_port->read_pos + c_octets;
371
372 if (c_octets > 0)
373 goto again;
374 else
375 result = EOF;
376 }
377 else
378 result = (int) *c_port->read_pos;
379
380 return result;
381}
382#undef FUNC_NAME
383
384
385SCM_DEFINE (scm_make_custom_binary_input_port,
386 "make-custom-binary-input-port", 5, 0, 0,
387 (SCM id, SCM read_proc, SCM get_position_proc,
388 SCM set_position_proc, SCM close_proc),
389 "Return a new custom binary input port whose input is drained "
390 "by invoking @var{read_proc} and passing it a bytevector, an "
391 "index where octets should be written, and an octet count.")
392#define FUNC_NAME s_scm_make_custom_binary_input_port
393{
394 SCM_VALIDATE_STRING (1, id);
395 SCM_VALIDATE_PROC (2, read_proc);
396
397 if (!scm_is_false (get_position_proc))
398 SCM_VALIDATE_PROC (3, get_position_proc);
399
400 if (!scm_is_false (set_position_proc))
401 SCM_VALIDATE_PROC (4, set_position_proc);
402
403 if (!scm_is_false (close_proc))
404 SCM_VALIDATE_PROC (5, close_proc);
405
406 return (make_cbip (read_proc, get_position_proc, set_position_proc,
407 close_proc));
408}
409#undef FUNC_NAME
410
411
412/* Instantiate the custom binary input port type. */
413static inline void
414initialize_custom_binary_input_ports (void)
415{
416 custom_binary_input_port_type =
417 scm_make_port_type ("r6rs-custom-binary-input-port",
418 cbip_fill_input, NULL);
419
1ee2c72e
LC
420 scm_set_port_seek (custom_binary_input_port_type, cbp_seek);
421 scm_set_port_close (custom_binary_input_port_type, cbp_close);
422}
423
424
425\f
426/* Binary input. */
427
428/* We currently don't support specific binary input ports. */
429#define SCM_VALIDATE_BINARY_INPUT_PORT SCM_VALIDATE_OPINPORT
430
431SCM_DEFINE (scm_get_u8, "get-u8", 1, 0, 0,
432 (SCM port),
433 "Read an octet from @var{port}, a binary input port, "
434 "blocking as necessary.")
435#define FUNC_NAME s_scm_get_u8
436{
437 SCM result;
438 int c_result;
439
440 SCM_VALIDATE_BINARY_INPUT_PORT (1, port);
441
8aa47f26 442 c_result = scm_get_byte_or_eof (port);
1ee2c72e
LC
443 if (c_result == EOF)
444 result = SCM_EOF_VAL;
445 else
446 result = SCM_I_MAKINUM ((unsigned char) c_result);
447
448 return result;
449}
450#undef FUNC_NAME
451
452SCM_DEFINE (scm_lookahead_u8, "lookahead-u8", 1, 0, 0,
453 (SCM port),
454 "Like @code{get-u8} but does not update @var{port} to "
455 "point past the octet.")
456#define FUNC_NAME s_scm_lookahead_u8
457{
8aa47f26 458 int u8;
1ee2c72e
LC
459 SCM result;
460
461 SCM_VALIDATE_BINARY_INPUT_PORT (1, port);
462
452c5ad9 463 u8 = scm_peek_byte_or_eof (port);
8aa47f26 464 if (u8 == EOF)
1ee2c72e 465 result = SCM_EOF_VAL;
8aa47f26 466 else
452c5ad9 467 result = SCM_I_MAKINUM ((scm_t_uint8) u8);
1ee2c72e
LC
468
469 return result;
470}
471#undef FUNC_NAME
472
473SCM_DEFINE (scm_get_bytevector_n, "get-bytevector-n", 2, 0, 0,
474 (SCM port, SCM count),
475 "Read @var{count} octets from @var{port}, blocking as "
476 "necessary and return a bytevector containing the octets "
477 "read. If fewer bytes are available, a bytevector smaller "
478 "than @var{count} is returned.")
479#define FUNC_NAME s_scm_get_bytevector_n
480{
481 SCM result;
482 char *c_bv;
483 unsigned c_count;
484 size_t c_read;
485
486 SCM_VALIDATE_BINARY_INPUT_PORT (1, port);
487 c_count = scm_to_uint (count);
488
489 result = scm_c_make_bytevector (c_count);
490 c_bv = (char *) SCM_BYTEVECTOR_CONTENTS (result);
491
492 if (SCM_LIKELY (c_count > 0))
493 /* XXX: `scm_c_read ()' does not update the port position. */
494 c_read = scm_c_read (port, c_bv, c_count);
495 else
496 /* Don't invoke `scm_c_read ()' since it may block. */
497 c_read = 0;
498
499 if ((c_read == 0) && (c_count > 0))
500 {
501 if (SCM_EOF_OBJECT_P (scm_peek_char (port)))
502 result = SCM_EOF_VAL;
503 else
504 result = scm_null_bytevector;
505 }
506 else
507 {
508 if (c_read < c_count)
509 result = scm_c_shrink_bytevector (result, c_read);
510 }
511
512 return result;
513}
514#undef FUNC_NAME
515
516SCM_DEFINE (scm_get_bytevector_n_x, "get-bytevector-n!", 4, 0, 0,
517 (SCM port, SCM bv, SCM start, SCM count),
518 "Read @var{count} bytes from @var{port} and store them "
519 "in @var{bv} starting at index @var{start}. Return either "
520 "the number of bytes actually read or the end-of-file "
521 "object.")
522#define FUNC_NAME s_scm_get_bytevector_n_x
523{
524 SCM result;
525 char *c_bv;
526 unsigned c_start, c_count, c_len;
527 size_t c_read;
528
529 SCM_VALIDATE_BINARY_INPUT_PORT (1, port);
530 SCM_VALIDATE_BYTEVECTOR (2, bv);
531 c_start = scm_to_uint (start);
532 c_count = scm_to_uint (count);
533
534 c_bv = (char *) SCM_BYTEVECTOR_CONTENTS (bv);
535 c_len = SCM_BYTEVECTOR_LENGTH (bv);
536
537 if (SCM_UNLIKELY (c_start + c_count > c_len))
538 scm_out_of_range (FUNC_NAME, count);
539
540 if (SCM_LIKELY (c_count > 0))
541 c_read = scm_c_read (port, c_bv + c_start, c_count);
542 else
543 /* Don't invoke `scm_c_read ()' since it may block. */
544 c_read = 0;
545
546 if ((c_read == 0) && (c_count > 0))
547 {
548 if (SCM_EOF_OBJECT_P (scm_peek_char (port)))
549 result = SCM_EOF_VAL;
550 else
551 result = SCM_I_MAKINUM (0);
552 }
553 else
554 result = scm_from_size_t (c_read);
555
556 return result;
557}
558#undef FUNC_NAME
559
560
561SCM_DEFINE (scm_get_bytevector_some, "get-bytevector-some", 1, 0, 0,
562 (SCM port),
563 "Read from @var{port}, blocking as necessary, until data "
564 "are available or and end-of-file is reached. Return either "
565 "a new bytevector containing the data read or the "
566 "end-of-file object.")
567#define FUNC_NAME s_scm_get_bytevector_some
568{
569 /* Read at least one byte, unless the end-of-file is already reached, and
570 read while characters are available (buffered). */
571
572 SCM result;
573 char *c_bv;
574 unsigned c_len;
575 size_t c_total;
576
577 SCM_VALIDATE_BINARY_INPUT_PORT (1, port);
578
579 c_len = 4096;
05762e72 580 c_bv = (char *) scm_gc_malloc_pointerless (c_len, SCM_GC_BYTEVECTOR);
1ee2c72e
LC
581 c_total = 0;
582
583 do
584 {
585 int c_chr;
586
587 if (c_total + 1 > c_len)
588 {
589 /* Grow the bytevector. */
590 c_bv = (char *) scm_gc_realloc (c_bv, c_len, c_len * 2,
591 SCM_GC_BYTEVECTOR);
592 c_len *= 2;
593 }
594
595 /* We can't use `scm_c_read ()' since it blocks. */
596 c_chr = scm_getc (port);
597 if (c_chr != EOF)
598 {
599 c_bv[c_total] = (char) c_chr;
600 c_total++;
601 }
602 }
603 while ((scm_is_true (scm_char_ready_p (port)))
604 && (!SCM_EOF_OBJECT_P (scm_peek_char (port))));
605
606 if (c_total == 0)
607 {
608 result = SCM_EOF_VAL;
609 scm_gc_free (c_bv, c_len, SCM_GC_BYTEVECTOR);
610 }
611 else
612 {
613 if (c_len > c_total)
614 {
615 /* Shrink the bytevector. */
616 c_bv = (char *) scm_gc_realloc (c_bv, c_len, c_total,
617 SCM_GC_BYTEVECTOR);
618 c_len = (unsigned) c_total;
619 }
620
621 result = scm_c_take_bytevector ((signed char *) c_bv, c_len);
622 }
623
624 return result;
625}
626#undef FUNC_NAME
627
628SCM_DEFINE (scm_get_bytevector_all, "get-bytevector-all", 1, 0, 0,
629 (SCM port),
630 "Read from @var{port}, blocking as necessary, until "
631 "the end-of-file is reached. Return either "
632 "a new bytevector containing the data read or the "
633 "end-of-file object (if no data were available).")
634#define FUNC_NAME s_scm_get_bytevector_all
635{
636 SCM result;
637 char *c_bv;
638 unsigned c_len, c_count;
639 size_t c_read, c_total;
640
641 SCM_VALIDATE_BINARY_INPUT_PORT (1, port);
642
643 c_len = c_count = 4096;
05762e72 644 c_bv = (char *) scm_gc_malloc_pointerless (c_len, SCM_GC_BYTEVECTOR);
1ee2c72e
LC
645 c_total = c_read = 0;
646
647 do
648 {
649 if (c_total + c_read > c_len)
650 {
651 /* Grow the bytevector. */
652 c_bv = (char *) scm_gc_realloc (c_bv, c_len, c_len * 2,
653 SCM_GC_BYTEVECTOR);
654 c_count = c_len;
655 c_len *= 2;
656 }
657
658 /* `scm_c_read ()' blocks until C_COUNT bytes are available or EOF is
659 reached. */
660 c_read = scm_c_read (port, c_bv + c_total, c_count);
661 c_total += c_read, c_count -= c_read;
662 }
663 while (!SCM_EOF_OBJECT_P (scm_peek_char (port)));
664
665 if (c_total == 0)
666 {
667 result = SCM_EOF_VAL;
668 scm_gc_free (c_bv, c_len, SCM_GC_BYTEVECTOR);
669 }
670 else
671 {
672 if (c_len > c_total)
673 {
674 /* Shrink the bytevector. */
675 c_bv = (char *) scm_gc_realloc (c_bv, c_len, c_total,
676 SCM_GC_BYTEVECTOR);
677 c_len = (unsigned) c_total;
678 }
679
680 result = scm_c_take_bytevector ((signed char *) c_bv, c_len);
681 }
682
683 return result;
684}
685#undef FUNC_NAME
686
687
688\f
689/* Binary output. */
690
691/* We currently don't support specific binary input ports. */
692#define SCM_VALIDATE_BINARY_OUTPUT_PORT SCM_VALIDATE_OPOUTPORT
693
694
695SCM_DEFINE (scm_put_u8, "put-u8", 2, 0, 0,
696 (SCM port, SCM octet),
697 "Write @var{octet} to binary port @var{port}.")
698#define FUNC_NAME s_scm_put_u8
699{
700 scm_t_uint8 c_octet;
701
702 SCM_VALIDATE_BINARY_OUTPUT_PORT (1, port);
703 c_octet = scm_to_uint8 (octet);
704
705 scm_putc ((char) c_octet, port);
706
707 return SCM_UNSPECIFIED;
708}
709#undef FUNC_NAME
710
711SCM_DEFINE (scm_put_bytevector, "put-bytevector", 2, 2, 0,
712 (SCM port, SCM bv, SCM start, SCM count),
713 "Write the contents of @var{bv} to @var{port}, optionally "
714 "starting at index @var{start} and limiting to @var{count} "
715 "octets.")
716#define FUNC_NAME s_scm_put_bytevector
717{
718 char *c_bv;
719 unsigned c_start, c_count, c_len;
720
721 SCM_VALIDATE_BINARY_OUTPUT_PORT (1, port);
722 SCM_VALIDATE_BYTEVECTOR (2, bv);
723
724 c_len = SCM_BYTEVECTOR_LENGTH (bv);
725 c_bv = (char *) SCM_BYTEVECTOR_CONTENTS (bv);
726
d223c3fc 727 if (!scm_is_eq (start, SCM_UNDEFINED))
1ee2c72e
LC
728 {
729 c_start = scm_to_uint (start);
730
d223c3fc 731 if (!scm_is_eq (count, SCM_UNDEFINED))
1ee2c72e
LC
732 {
733 c_count = scm_to_uint (count);
734 if (SCM_UNLIKELY (c_start + c_count > c_len))
735 scm_out_of_range (FUNC_NAME, count);
736 }
737 else
738 {
739 if (SCM_UNLIKELY (c_start >= c_len))
740 scm_out_of_range (FUNC_NAME, start);
741 else
742 c_count = c_len - c_start;
743 }
744 }
745 else
746 c_start = 0, c_count = c_len;
747
748 scm_c_write (port, c_bv + c_start, c_count);
749
750 return SCM_UNSPECIFIED;
751}
752#undef FUNC_NAME
753
754
755\f
756/* Bytevector output port ("bop" for short). */
757
758/* Implementation of "bops".
759
760 Each bop has an internal buffer, of type `scm_t_bop_buffer', attached to
761 it. The procedure returned along with the output port is actually an
762 applicable SMOB. The SMOB holds a reference to the port. When applied,
763 the SMOB swallows the port's internal buffer, turning it into a
764 bytevector, and resets it.
765
766 XXX: Access to a bop's internal buffer is not thread-safe. */
767
768static scm_t_bits bytevector_output_port_type = 0;
769
770SCM_SMOB (bytevector_output_port_procedure,
771 "r6rs-bytevector-output-port-procedure",
772 0);
773
774#define SCM_GC_BOP "r6rs-bytevector-output-port"
775#define SCM_BOP_BUFFER_INITIAL_SIZE 4096
776
777/* Representation of a bop's internal buffer. */
778typedef struct
779{
780 size_t total_len;
781 size_t len;
782 size_t pos;
783 char *buffer;
784} scm_t_bop_buffer;
785
786
787/* Accessing a bop's buffer. */
788#define SCM_BOP_BUFFER(_port) \
789 ((scm_t_bop_buffer *) SCM_STREAM (_port))
790#define SCM_SET_BOP_BUFFER(_port, _buf) \
791 (SCM_SETSTREAM ((_port), (scm_t_bits) (_buf)))
792
793
794static inline void
795bop_buffer_init (scm_t_bop_buffer *buf)
796{
797 buf->total_len = buf->len = buf->pos = 0;
798 buf->buffer = NULL;
799}
800
801static inline void
802bop_buffer_grow (scm_t_bop_buffer *buf, size_t min_size)
803{
804 char *new_buf;
805 size_t new_size;
806
807 for (new_size = buf->total_len
808 ? buf->total_len : SCM_BOP_BUFFER_INITIAL_SIZE;
809 new_size < min_size;
810 new_size *= 2);
811
812 if (buf->buffer)
813 new_buf = scm_gc_realloc ((void *) buf->buffer, buf->total_len,
814 new_size, SCM_GC_BOP);
815 else
05762e72 816 new_buf = scm_gc_malloc_pointerless (new_size, SCM_GC_BOP);
1ee2c72e
LC
817
818 buf->buffer = new_buf;
819 buf->total_len = new_size;
820}
821
822static inline SCM
823make_bop (void)
824{
825 SCM port, bop_proc;
826 scm_t_port *c_port;
827 scm_t_bop_buffer *buf;
828 const unsigned long mode_bits = SCM_OPN | SCM_WRTNG;
829
a653d32a
AR
830 scm_i_pthread_mutex_lock (&scm_i_port_table_mutex);
831
1ee2c72e 832 port = scm_new_port_table_entry (bytevector_output_port_type);
96128014
LC
833 c_port = SCM_PTAB_ENTRY (port);
834
835 /* Match the expectation of `binary-port?'. */
836 c_port->encoding = NULL;
1ee2c72e
LC
837
838 buf = (scm_t_bop_buffer *) scm_gc_malloc (sizeof (* buf), SCM_GC_BOP);
839 bop_buffer_init (buf);
840
1ee2c72e
LC
841 c_port->write_buf = c_port->write_pos = c_port->write_end = NULL;
842 c_port->write_buf_size = 0;
843
844 SCM_SET_BOP_BUFFER (port, buf);
845
846 /* Mark PORT as open and writable. */
847 SCM_SET_CELL_TYPE (port, bytevector_output_port_type | mode_bits);
848
a653d32a
AR
849 scm_i_pthread_mutex_unlock (&scm_i_port_table_mutex);
850
1ee2c72e 851 /* Make the bop procedure. */
a653d32a 852 SCM_NEWSMOB (bop_proc, bytevector_output_port_procedure, buf);
1ee2c72e
LC
853
854 return (scm_values (scm_list_2 (port, bop_proc)));
855}
856
1ee2c72e
LC
857/* Write SIZE octets from DATA to PORT. */
858static void
859bop_write (SCM port, const void *data, size_t size)
860{
861 scm_t_bop_buffer *buf;
862
863 buf = SCM_BOP_BUFFER (port);
864
865 if (buf->pos + size > buf->total_len)
866 bop_buffer_grow (buf, buf->pos + size);
867
868 memcpy (buf->buffer + buf->pos, data, size);
869 buf->pos += size;
870 buf->len = (buf->len > buf->pos) ? buf->len : buf->pos;
871}
872
f1ce9199
LC
873static scm_t_off
874bop_seek (SCM port, scm_t_off offset, int whence)
1ee2c72e
LC
875#define FUNC_NAME "bop_seek"
876{
877 scm_t_bop_buffer *buf;
878
879 buf = SCM_BOP_BUFFER (port);
880 switch (whence)
881 {
882 case SEEK_CUR:
f1ce9199 883 offset += (scm_t_off) buf->pos;
1ee2c72e
LC
884 /* Fall through. */
885
886 case SEEK_SET:
887 if (offset < 0 || (unsigned) offset > buf->len)
888 scm_out_of_range (FUNC_NAME, scm_from_int (offset));
889 else
890 buf->pos = offset;
891 break;
892
893 case SEEK_END:
894 if (offset < 0 || (unsigned) offset >= buf->len)
895 scm_out_of_range (FUNC_NAME, scm_from_int (offset));
896 else
897 buf->pos = buf->len - (offset + 1);
898 break;
899
900 default:
901 scm_wrong_type_arg_msg (FUNC_NAME, 0, port,
902 "invalid `seek' parameter");
903 }
904
905 return buf->pos;
906}
907#undef FUNC_NAME
908
909/* Fetch data from a bop. */
910SCM_SMOB_APPLY (bytevector_output_port_procedure,
911 bop_proc_apply, 0, 0, 0, (SCM bop_proc))
912{
a653d32a 913 SCM bv;
1ee2c72e
LC
914 scm_t_bop_buffer *buf, result_buf;
915
a653d32a 916 buf = (scm_t_bop_buffer *) SCM_SMOB_DATA (bop_proc);
1ee2c72e
LC
917
918 result_buf = *buf;
919 bop_buffer_init (buf);
920
921 if (result_buf.len == 0)
922 bv = scm_c_take_bytevector (NULL, 0);
923 else
924 {
925 if (result_buf.total_len > result_buf.len)
926 /* Shrink the buffer. */
927 result_buf.buffer = scm_gc_realloc ((void *) result_buf.buffer,
928 result_buf.total_len,
929 result_buf.len,
930 SCM_GC_BOP);
931
932 bv = scm_c_take_bytevector ((signed char *) result_buf.buffer,
933 result_buf.len);
934 }
935
936 return bv;
937}
938
1ee2c72e
LC
939SCM_DEFINE (scm_open_bytevector_output_port,
940 "open-bytevector-output-port", 0, 1, 0,
941 (SCM transcoder),
942 "Return two values: an output port and a procedure. The latter "
943 "should be called with zero arguments to obtain a bytevector "
944 "containing the data accumulated by the port.")
945#define FUNC_NAME s_scm_open_bytevector_output_port
946{
947 if (!SCM_UNBNDP (transcoder) && !scm_is_false (transcoder))
948 transcoders_not_implemented ();
949
950 return (make_bop ());
951}
952#undef FUNC_NAME
953
954static inline void
955initialize_bytevector_output_ports (void)
956{
957 bytevector_output_port_type =
958 scm_make_port_type ("r6rs-bytevector-output-port",
959 NULL, bop_write);
960
961 scm_set_port_seek (bytevector_output_port_type, bop_seek);
1ee2c72e
LC
962}
963
964\f
965/* Custom binary output port ("cbop" for short). */
966
967static scm_t_bits custom_binary_output_port_type;
968
969/* Return the various procedures of PORT. */
970#define SCM_CBOP_WRITE_PROC(_port) \
971 SCM_SIMPLE_VECTOR_REF (SCM_PACK (SCM_STREAM (_port)), 0)
972
973
974static inline SCM
975make_cbop (SCM write_proc, SCM get_position_proc,
976 SCM set_position_proc, SCM close_proc)
977{
978 SCM port, method_vector;
979 scm_t_port *c_port;
980 const unsigned long mode_bits = SCM_OPN | SCM_WRTNG;
981
982 /* Store the various methods and bytevector in a vector. */
983 method_vector = scm_c_make_vector (4, SCM_BOOL_F);
984 SCM_SIMPLE_VECTOR_SET (method_vector, 0, write_proc);
985 SCM_SIMPLE_VECTOR_SET (method_vector, 1, get_position_proc);
986 SCM_SIMPLE_VECTOR_SET (method_vector, 2, set_position_proc);
987 SCM_SIMPLE_VECTOR_SET (method_vector, 3, close_proc);
988
a653d32a
AR
989 scm_i_pthread_mutex_lock (&scm_i_port_table_mutex);
990
1ee2c72e 991 port = scm_new_port_table_entry (custom_binary_output_port_type);
96128014
LC
992 c_port = SCM_PTAB_ENTRY (port);
993
994 /* Match the expectation of `binary-port?'. */
995 c_port->encoding = NULL;
1ee2c72e
LC
996
997 /* Attach it the method vector. */
998 SCM_SETSTREAM (port, SCM_UNPACK (method_vector));
999
1000 /* Have the port directly access the buffer (bytevector). */
1ee2c72e
LC
1001 c_port->write_buf = c_port->write_pos = c_port->write_end = NULL;
1002 c_port->write_buf_size = c_port->read_buf_size = 0;
1003
1004 /* Mark PORT as open, writable and unbuffered. */
1005 SCM_SET_CELL_TYPE (port, custom_binary_output_port_type | mode_bits);
1006
a653d32a
AR
1007 scm_i_pthread_mutex_unlock (&scm_i_port_table_mutex);
1008
1ee2c72e
LC
1009 return port;
1010}
1011
1012/* Write SIZE octets from DATA to PORT. */
1013static void
1014cbop_write (SCM port, const void *data, size_t size)
1015#define FUNC_NAME "cbop_write"
1016{
1017 long int c_result;
1018 size_t c_written;
1019 SCM bv, write_proc, result;
1020
1021 /* XXX: Allocating a new bytevector at each `write' call is inefficient,
1022 but necessary since (1) we don't control the lifetime of the buffer
1023 pointed to by DATA, and (2) the `write!' procedure could capture the
1024 bytevector it is passed. */
1025 bv = scm_c_make_bytevector (size);
1026 memcpy (SCM_BYTEVECTOR_CONTENTS (bv), data, size);
1027
1028 write_proc = SCM_CBOP_WRITE_PROC (port);
1029
1030 /* Since the `write' procedure of Guile's ports has type `void', it must
1031 try hard to write exactly SIZE bytes, regardless of how many bytes the
1032 sink can handle. */
1033 for (c_written = 0;
1034 c_written < size;
1035 c_written += c_result)
1036 {
1037 result = scm_call_3 (write_proc, bv,
1038 scm_from_size_t (c_written),
1039 scm_from_size_t (size - c_written));
1040
1041 c_result = scm_to_long (result);
1042 if (SCM_UNLIKELY (c_result < 0
1043 || (size_t) c_result > (size - c_written)))
1044 scm_wrong_type_arg_msg (FUNC_NAME, 0, result,
1045 "R6RS custom binary output port `write!' "
1046 "returned a incorrect integer");
1047 }
1048}
1049#undef FUNC_NAME
1050
1051
1052SCM_DEFINE (scm_make_custom_binary_output_port,
1053 "make-custom-binary-output-port", 5, 0, 0,
1054 (SCM id, SCM write_proc, SCM get_position_proc,
1055 SCM set_position_proc, SCM close_proc),
1056 "Return a new custom binary output port whose output is drained "
1057 "by invoking @var{write_proc} and passing it a bytevector, an "
1058 "index where octets should be written, and an octet count.")
1059#define FUNC_NAME s_scm_make_custom_binary_output_port
1060{
1061 SCM_VALIDATE_STRING (1, id);
1062 SCM_VALIDATE_PROC (2, write_proc);
1063
1064 if (!scm_is_false (get_position_proc))
1065 SCM_VALIDATE_PROC (3, get_position_proc);
1066
1067 if (!scm_is_false (set_position_proc))
1068 SCM_VALIDATE_PROC (4, set_position_proc);
1069
1070 if (!scm_is_false (close_proc))
1071 SCM_VALIDATE_PROC (5, close_proc);
1072
1073 return (make_cbop (write_proc, get_position_proc, set_position_proc,
1074 close_proc));
1075}
1076#undef FUNC_NAME
1077
1078
1079/* Instantiate the custom binary output port type. */
1080static inline void
1081initialize_custom_binary_output_ports (void)
1082{
1083 custom_binary_output_port_type =
1084 scm_make_port_type ("r6rs-custom-binary-output-port",
1085 NULL, cbop_write);
1086
1ee2c72e
LC
1087 scm_set_port_seek (custom_binary_output_port_type, cbp_seek);
1088 scm_set_port_close (custom_binary_output_port_type, cbp_close);
1089}
1090
1091\f
1044537d
AR
1092/* Transcoded ports ("tp" for short). */
1093static scm_t_bits transcoded_port_type = 0;
1094
1095#define TP_INPUT_BUFFER_SIZE 4096
1096
1097#define SCM_TP_BINARY_PORT(_port) SCM_PACK (SCM_STREAM (_port))
1098
1099static inline SCM
1100make_tp (SCM binary_port, unsigned long mode)
1101{
1102 SCM port;
1103 scm_t_port *c_port;
1104 const unsigned long mode_bits = SCM_OPN | mode;
1105
1106 scm_i_pthread_mutex_lock (&scm_i_port_table_mutex);
1107
1108 port = scm_new_port_table_entry (transcoded_port_type);
1109
1110 SCM_SETSTREAM (port, SCM_UNPACK (binary_port));
1111
1112 SCM_SET_CELL_TYPE (port, transcoded_port_type | mode_bits);
1113
1114 if (SCM_INPUT_PORT_P (port))
1115 {
1116 c_port = SCM_PTAB_ENTRY (port);
1117 c_port->read_buf = scm_gc_malloc_pointerless (TP_INPUT_BUFFER_SIZE,
1118 "port buffer");
1119 c_port->read_pos = c_port->read_end = c_port->read_buf;
1120 c_port->read_buf_size = TP_INPUT_BUFFER_SIZE;
1121
1122 SCM_SET_CELL_WORD_0 (port, SCM_CELL_WORD_0 (port) & ~SCM_BUF0);
1123 }
1124
1125 scm_i_pthread_mutex_unlock (&scm_i_port_table_mutex);
1126
1127 return port;
1128}
1129
1130static void
1131tp_write (SCM port, const void *data, size_t size)
1132{
1133 scm_c_write (SCM_TP_BINARY_PORT (port), data, size);
1134}
1135
1136static int
1137tp_fill_input (SCM port)
1138{
1139 size_t count;
1140 scm_t_port *c_port = SCM_PTAB_ENTRY (port);
1141 SCM bport = SCM_TP_BINARY_PORT (port);
1142 scm_t_port *c_bport = SCM_PTAB_ENTRY (bport);
1143
1144 /* We can't use `scm_c_read' here, since it blocks until the whole
1145 block has been read or EOF. */
1146
1147 if (c_bport->rw_active == SCM_PORT_WRITE)
1148 scm_force_output (bport);
1149
1150 if (c_bport->read_pos >= c_bport->read_end)
1151 scm_fill_input (bport);
1152
1153 count = c_bport->read_end - c_bport->read_pos;
1154 if (count > c_port->read_buf_size)
1155 count = c_port->read_buf_size;
1156
1157 memcpy (c_port->read_buf, c_bport->read_pos, count);
1158 c_bport->read_pos += count;
1159
1160 if (c_bport->rw_random)
1161 c_bport->rw_active = SCM_PORT_READ;
1162
1163 if (count == 0)
1164 return EOF;
1165 else
1166 {
1167 c_port->read_pos = c_port->read_buf;
1168 c_port->read_end = c_port->read_buf + count;
1169 return *c_port->read_buf;
1170 }
1171}
1172
1173static void
1174tp_flush (SCM port)
1175{
1176 SCM binary_port = SCM_TP_BINARY_PORT (port);
1177 scm_t_port *c_port = SCM_PTAB_ENTRY (port);
1178 size_t count = c_port->write_pos - c_port->write_buf;
1179
dfb572a7
AR
1180 /* As the runtime will try to flush all ports upon exit, we test for
1181 the underlying port still being open here. Otherwise, when you
1182 would explicitly close the underlying port and the transcoded port
1183 still had data outstanding, you'd get an exception on Guile exit.
1184
1185 We just throw away the data when the underlying port is closed. */
1186
1187 if (SCM_OPOUTPORTP (binary_port))
1188 scm_c_write (binary_port, c_port->write_buf, count);
1044537d
AR
1189
1190 c_port->write_pos = c_port->write_buf;
1191 c_port->rw_active = SCM_PORT_NEITHER;
1192
dfb572a7
AR
1193 if (SCM_OPOUTPORTP (binary_port))
1194 scm_force_output (binary_port);
1044537d
AR
1195}
1196
1197static int
1198tp_close (SCM port)
1199{
1200 if (SCM_OUTPUT_PORT_P (port))
1201 tp_flush (port);
1202 return scm_is_true (scm_close_port (SCM_TP_BINARY_PORT (port))) ? 0 : -1;
1203}
1204
1205static inline void
1206initialize_transcoded_ports (void)
1207{
1208 transcoded_port_type =
1209 scm_make_port_type ("r6rs-transcoded-port", tp_fill_input, tp_write);
1210
1211 scm_set_port_flush (transcoded_port_type, tp_flush);
1212 scm_set_port_close (transcoded_port_type, tp_close);
1213}
1214
1215SCM_DEFINE (scm_i_make_transcoded_port,
1216 "%make-transcoded-port", 1, 0, 0,
1217 (SCM port),
1218 "Return a new port which reads and writes to @var{port}")
1219#define FUNC_NAME s_scm_i_make_transcoded_port
1220{
1221 SCM result;
1222 unsigned long mode = 0;
1223
1224 SCM_VALIDATE_PORT (SCM_ARG1, port);
1225
1226 if (scm_is_true (scm_output_port_p (port)))
1227 mode |= SCM_WRTNG;
1228 else if (scm_is_true (scm_input_port_p (port)))
1229 mode |= SCM_RDNG;
1230
1231 result = make_tp (port, mode);
1232
1233 /* FIXME: We should actually close `port' "in a special way" here,
1234 according to R6RS. As there is no way to do that in Guile without
1235 rendering the underlying port unusable for our purposes as well, we
1236 just leave it open. */
1237
1238 return result;
1239}
1240#undef FUNC_NAME
1241
1242\f
a6c377f7
AR
1243/* Textual I/O */
1244
1245SCM_DEFINE (scm_get_string_n_x,
1246 "get-string-n!", 4, 0, 0,
1247 (SCM port, SCM str, SCM start, SCM count),
1248 "Read up to @var{count} characters from @var{port} into "
1249 "@var{str}, starting at @var{start}. If no characters "
1250 "can be read before the end of file is encountered, the end "
1251 "of file object is returned. Otherwise, the number of "
1252 "characters read is returned.")
1253#define FUNC_NAME s_scm_get_string_n_x
1254{
1255 size_t c_start, c_count, c_len, c_end, j;
1256 scm_t_wchar c;
1257
1258 SCM_VALIDATE_OPINPORT (1, port);
1259 SCM_VALIDATE_STRING (2, str);
1260 c_len = scm_c_string_length (str);
1261 c_start = scm_to_size_t (start);
1262 c_count = scm_to_size_t (count);
1263 c_end = c_start + c_count;
1264
1265 if (SCM_UNLIKELY (c_end > c_len))
1266 scm_out_of_range (FUNC_NAME, count);
1267
1268 for (j = c_start; j < c_end; j++)
1269 {
1270 c = scm_getc (port);
1271 if (c == EOF)
1272 {
1273 size_t chars_read = j - c_start;
1274 return chars_read == 0 ? SCM_EOF_VAL : scm_from_size_t (chars_read);
1275 }
1276 scm_c_string_set_x (str, j, SCM_MAKE_CHAR (c));
1277 }
1278 return count;
1279}
1280#undef FUNC_NAME
1281
1282\f
1ee2c72e
LC
1283/* Initialization. */
1284
c0062328
LC
1285void
1286scm_register_r6rs_ports (void)
1287{
1288 scm_c_register_extension ("libguile-" SCM_EFFECTIVE_VERSION,
1289 "scm_init_r6rs_ports",
1290 (scm_t_extension_init_func) scm_init_r6rs_ports,
1291 NULL);
1292}
1293
1ee2c72e
LC
1294void
1295scm_init_r6rs_ports (void)
1296{
62e9a9b7 1297#include "libguile/r6rs-ports.x"
1ee2c72e
LC
1298
1299 initialize_bytevector_input_ports ();
1300 initialize_custom_binary_input_ports ();
1301 initialize_bytevector_output_ports ();
1302 initialize_custom_binary_output_ports ();
1044537d 1303 initialize_transcoded_ports ();
1ee2c72e 1304}