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