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