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