Temporarily fix `unistr.in.h' to allow compilation with `-Wundef'.
[bpt/guile.git] / libguile / r6rs-ports.c
CommitLineData
3fa88220 1/* Copyright (C) 2009, 2010 Free Software Foundation, Inc.
1ee2c72e
LC
2 *
3 * This library is free software; you can redistribute it and/or
53befeb7
NJ
4 * modify it under the terms of the GNU Lesser General Public License
5 * as published by the Free Software Foundation; either version 3 of
6 * the License, or (at your option) any later version.
1ee2c72e 7 *
53befeb7
NJ
8 * This library is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
1ee2c72e
LC
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with this library; if not, write to the Free Software
53befeb7
NJ
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
16 * 02110-1301 USA
1ee2c72e
LC
17 */
18
19#ifdef HAVE_CONFIG_H
20# include <config.h>
21#endif
22
23#ifdef HAVE_UNISTD_H
24# include <unistd.h>
25#endif
26
27#include <string.h>
28#include <stdio.h>
29#include <assert.h>
30
31#include "libguile/_scm.h"
32#include "libguile/bytevectors.h"
33#include "libguile/chars.h"
34#include "libguile/eval.h"
35#include "libguile/r6rs-ports.h"
36#include "libguile/strings.h"
37#include "libguile/validate.h"
38#include "libguile/values.h"
39#include "libguile/vectors.h"
40
41
42\f
43/* Unimplemented features. */
44
45
46/* Transoders are currently not implemented since Guile 1.8 is not
47 Unicode-capable. Thus, most of the code here assumes the use of the
48 binary transcoder. */
49static inline void
50transcoders_not_implemented (void)
51{
52 fprintf (stderr, "%s: warning: transcoders not implemented\n",
53 PACKAGE_NAME);
54}
55
56\f
57/* End-of-file object. */
58
59SCM_DEFINE (scm_eof_object, "eof-object", 0, 0, 0,
60 (void),
61 "Return the end-of-file object.")
62#define FUNC_NAME s_scm_eof_object
63{
64 return (SCM_EOF_VAL);
65}
66#undef FUNC_NAME
67
68\f
69/* Input ports. */
70
71#ifndef MIN
72# define MIN(a,b) ((a) < (b) ? (a) : (b))
73#endif
74
75/* Bytevector input ports or "bip" for short. */
76static scm_t_bits bytevector_input_port_type = 0;
77
78static inline SCM
79make_bip (SCM bv)
80{
81 SCM port;
82 char *c_bv;
83 unsigned c_len;
84 scm_t_port *c_port;
85 const unsigned long mode_bits = SCM_OPN | SCM_RDNG;
86
a653d32a
AR
87 scm_i_scm_pthread_mutex_lock (&scm_i_port_table_mutex);
88
1ee2c72e
LC
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
a653d32a
AR
106 scm_i_pthread_mutex_unlock (&scm_i_port_table_mutex);
107
1ee2c72e
LC
108 return port;
109}
110
1ee2c72e
LC
111static int
112bip_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
f1ce9199
LC
125static scm_t_off
126bip_seek (SCM port, scm_t_off offset, int whence)
1ee2c72e
LC
127#define FUNC_NAME "bip_seek"
128{
f1ce9199 129 scm_t_off c_result = 0;
1ee2c72e
LC
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. */
169static inline void
170initialize_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
1ee2c72e
LC
176 scm_set_port_seek (bytevector_input_port_type, bip_seek);
177}
178
179
180SCM_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
f1ce9199
LC
206static scm_t_off
207cbp_seek (SCM port, scm_t_off offset, int whence)
1ee2c72e
LC
208#define FUNC_NAME "cbp_seek"
209{
210 SCM result;
f1ce9199 211 scm_t_off c_result = 0;
1ee2c72e
LC
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
259static int
260cbp_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
275static 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
289static inline SCM
290make_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
a653d32a
AR
312 scm_i_pthread_mutex_lock (&scm_i_port_table_mutex);
313
1ee2c72e
LC
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
a653d32a
AR
328 scm_i_pthread_mutex_unlock (&scm_i_port_table_mutex);
329
1ee2c72e
LC
330 return port;
331}
332
333static int
334cbip_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
379SCM_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. */
407static inline void
408initialize_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
1ee2c72e
LC
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
425SCM_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_getc (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
446SCM_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 SCM result;
453
454 SCM_VALIDATE_BINARY_INPUT_PORT (1, port);
455
456 result = scm_peek_char (port);
457 if (SCM_CHARP (result))
3fa88220 458 result = SCM_I_MAKINUM ((unsigned char) SCM_CHAR (result));
1ee2c72e
LC
459 else
460 result = SCM_EOF_VAL;
461
462 return result;
463}
464#undef FUNC_NAME
465
466SCM_DEFINE (scm_get_bytevector_n, "get-bytevector-n", 2, 0, 0,
467 (SCM port, SCM count),
468 "Read @var{count} octets from @var{port}, blocking as "
469 "necessary and return a bytevector containing the octets "
470 "read. If fewer bytes are available, a bytevector smaller "
471 "than @var{count} is returned.")
472#define FUNC_NAME s_scm_get_bytevector_n
473{
474 SCM result;
475 char *c_bv;
476 unsigned c_count;
477 size_t c_read;
478
479 SCM_VALIDATE_BINARY_INPUT_PORT (1, port);
480 c_count = scm_to_uint (count);
481
482 result = scm_c_make_bytevector (c_count);
483 c_bv = (char *) SCM_BYTEVECTOR_CONTENTS (result);
484
485 if (SCM_LIKELY (c_count > 0))
486 /* XXX: `scm_c_read ()' does not update the port position. */
487 c_read = scm_c_read (port, c_bv, c_count);
488 else
489 /* Don't invoke `scm_c_read ()' since it may block. */
490 c_read = 0;
491
492 if ((c_read == 0) && (c_count > 0))
493 {
494 if (SCM_EOF_OBJECT_P (scm_peek_char (port)))
495 result = SCM_EOF_VAL;
496 else
497 result = scm_null_bytevector;
498 }
499 else
500 {
501 if (c_read < c_count)
502 result = scm_c_shrink_bytevector (result, c_read);
503 }
504
505 return result;
506}
507#undef FUNC_NAME
508
509SCM_DEFINE (scm_get_bytevector_n_x, "get-bytevector-n!", 4, 0, 0,
510 (SCM port, SCM bv, SCM start, SCM count),
511 "Read @var{count} bytes from @var{port} and store them "
512 "in @var{bv} starting at index @var{start}. Return either "
513 "the number of bytes actually read or the end-of-file "
514 "object.")
515#define FUNC_NAME s_scm_get_bytevector_n_x
516{
517 SCM result;
518 char *c_bv;
519 unsigned c_start, c_count, c_len;
520 size_t c_read;
521
522 SCM_VALIDATE_BINARY_INPUT_PORT (1, port);
523 SCM_VALIDATE_BYTEVECTOR (2, bv);
524 c_start = scm_to_uint (start);
525 c_count = scm_to_uint (count);
526
527 c_bv = (char *) SCM_BYTEVECTOR_CONTENTS (bv);
528 c_len = SCM_BYTEVECTOR_LENGTH (bv);
529
530 if (SCM_UNLIKELY (c_start + c_count > c_len))
531 scm_out_of_range (FUNC_NAME, count);
532
533 if (SCM_LIKELY (c_count > 0))
534 c_read = scm_c_read (port, c_bv + c_start, c_count);
535 else
536 /* Don't invoke `scm_c_read ()' since it may block. */
537 c_read = 0;
538
539 if ((c_read == 0) && (c_count > 0))
540 {
541 if (SCM_EOF_OBJECT_P (scm_peek_char (port)))
542 result = SCM_EOF_VAL;
543 else
544 result = SCM_I_MAKINUM (0);
545 }
546 else
547 result = scm_from_size_t (c_read);
548
549 return result;
550}
551#undef FUNC_NAME
552
553
554SCM_DEFINE (scm_get_bytevector_some, "get-bytevector-some", 1, 0, 0,
555 (SCM port),
556 "Read from @var{port}, blocking as necessary, until data "
557 "are available or and end-of-file is reached. Return either "
558 "a new bytevector containing the data read or the "
559 "end-of-file object.")
560#define FUNC_NAME s_scm_get_bytevector_some
561{
562 /* Read at least one byte, unless the end-of-file is already reached, and
563 read while characters are available (buffered). */
564
565 SCM result;
566 char *c_bv;
567 unsigned c_len;
568 size_t c_total;
569
570 SCM_VALIDATE_BINARY_INPUT_PORT (1, port);
571
572 c_len = 4096;
05762e72 573 c_bv = (char *) scm_gc_malloc_pointerless (c_len, SCM_GC_BYTEVECTOR);
1ee2c72e
LC
574 c_total = 0;
575
576 do
577 {
578 int c_chr;
579
580 if (c_total + 1 > c_len)
581 {
582 /* Grow the bytevector. */
583 c_bv = (char *) scm_gc_realloc (c_bv, c_len, c_len * 2,
584 SCM_GC_BYTEVECTOR);
585 c_len *= 2;
586 }
587
588 /* We can't use `scm_c_read ()' since it blocks. */
589 c_chr = scm_getc (port);
590 if (c_chr != EOF)
591 {
592 c_bv[c_total] = (char) c_chr;
593 c_total++;
594 }
595 }
596 while ((scm_is_true (scm_char_ready_p (port)))
597 && (!SCM_EOF_OBJECT_P (scm_peek_char (port))));
598
599 if (c_total == 0)
600 {
601 result = SCM_EOF_VAL;
602 scm_gc_free (c_bv, c_len, SCM_GC_BYTEVECTOR);
603 }
604 else
605 {
606 if (c_len > c_total)
607 {
608 /* Shrink the bytevector. */
609 c_bv = (char *) scm_gc_realloc (c_bv, c_len, c_total,
610 SCM_GC_BYTEVECTOR);
611 c_len = (unsigned) c_total;
612 }
613
614 result = scm_c_take_bytevector ((signed char *) c_bv, c_len);
615 }
616
617 return result;
618}
619#undef FUNC_NAME
620
621SCM_DEFINE (scm_get_bytevector_all, "get-bytevector-all", 1, 0, 0,
622 (SCM port),
623 "Read from @var{port}, blocking as necessary, until "
624 "the end-of-file is reached. Return either "
625 "a new bytevector containing the data read or the "
626 "end-of-file object (if no data were available).")
627#define FUNC_NAME s_scm_get_bytevector_all
628{
629 SCM result;
630 char *c_bv;
631 unsigned c_len, c_count;
632 size_t c_read, c_total;
633
634 SCM_VALIDATE_BINARY_INPUT_PORT (1, port);
635
636 c_len = c_count = 4096;
05762e72 637 c_bv = (char *) scm_gc_malloc_pointerless (c_len, SCM_GC_BYTEVECTOR);
1ee2c72e
LC
638 c_total = c_read = 0;
639
640 do
641 {
642 if (c_total + c_read > c_len)
643 {
644 /* Grow the bytevector. */
645 c_bv = (char *) scm_gc_realloc (c_bv, c_len, c_len * 2,
646 SCM_GC_BYTEVECTOR);
647 c_count = c_len;
648 c_len *= 2;
649 }
650
651 /* `scm_c_read ()' blocks until C_COUNT bytes are available or EOF is
652 reached. */
653 c_read = scm_c_read (port, c_bv + c_total, c_count);
654 c_total += c_read, c_count -= c_read;
655 }
656 while (!SCM_EOF_OBJECT_P (scm_peek_char (port)));
657
658 if (c_total == 0)
659 {
660 result = SCM_EOF_VAL;
661 scm_gc_free (c_bv, c_len, SCM_GC_BYTEVECTOR);
662 }
663 else
664 {
665 if (c_len > c_total)
666 {
667 /* Shrink the bytevector. */
668 c_bv = (char *) scm_gc_realloc (c_bv, c_len, c_total,
669 SCM_GC_BYTEVECTOR);
670 c_len = (unsigned) c_total;
671 }
672
673 result = scm_c_take_bytevector ((signed char *) c_bv, c_len);
674 }
675
676 return result;
677}
678#undef FUNC_NAME
679
680
681\f
682/* Binary output. */
683
684/* We currently don't support specific binary input ports. */
685#define SCM_VALIDATE_BINARY_OUTPUT_PORT SCM_VALIDATE_OPOUTPORT
686
687
688SCM_DEFINE (scm_put_u8, "put-u8", 2, 0, 0,
689 (SCM port, SCM octet),
690 "Write @var{octet} to binary port @var{port}.")
691#define FUNC_NAME s_scm_put_u8
692{
693 scm_t_uint8 c_octet;
694
695 SCM_VALIDATE_BINARY_OUTPUT_PORT (1, port);
696 c_octet = scm_to_uint8 (octet);
697
698 scm_putc ((char) c_octet, port);
699
700 return SCM_UNSPECIFIED;
701}
702#undef FUNC_NAME
703
704SCM_DEFINE (scm_put_bytevector, "put-bytevector", 2, 2, 0,
705 (SCM port, SCM bv, SCM start, SCM count),
706 "Write the contents of @var{bv} to @var{port}, optionally "
707 "starting at index @var{start} and limiting to @var{count} "
708 "octets.")
709#define FUNC_NAME s_scm_put_bytevector
710{
711 char *c_bv;
712 unsigned c_start, c_count, c_len;
713
714 SCM_VALIDATE_BINARY_OUTPUT_PORT (1, port);
715 SCM_VALIDATE_BYTEVECTOR (2, bv);
716
717 c_len = SCM_BYTEVECTOR_LENGTH (bv);
718 c_bv = (char *) SCM_BYTEVECTOR_CONTENTS (bv);
719
720 if (start != SCM_UNDEFINED)
721 {
722 c_start = scm_to_uint (start);
723
724 if (count != SCM_UNDEFINED)
725 {
726 c_count = scm_to_uint (count);
727 if (SCM_UNLIKELY (c_start + c_count > c_len))
728 scm_out_of_range (FUNC_NAME, count);
729 }
730 else
731 {
732 if (SCM_UNLIKELY (c_start >= c_len))
733 scm_out_of_range (FUNC_NAME, start);
734 else
735 c_count = c_len - c_start;
736 }
737 }
738 else
739 c_start = 0, c_count = c_len;
740
741 scm_c_write (port, c_bv + c_start, c_count);
742
743 return SCM_UNSPECIFIED;
744}
745#undef FUNC_NAME
746
747
748\f
749/* Bytevector output port ("bop" for short). */
750
751/* Implementation of "bops".
752
753 Each bop has an internal buffer, of type `scm_t_bop_buffer', attached to
754 it. The procedure returned along with the output port is actually an
755 applicable SMOB. The SMOB holds a reference to the port. When applied,
756 the SMOB swallows the port's internal buffer, turning it into a
757 bytevector, and resets it.
758
759 XXX: Access to a bop's internal buffer is not thread-safe. */
760
761static scm_t_bits bytevector_output_port_type = 0;
762
763SCM_SMOB (bytevector_output_port_procedure,
764 "r6rs-bytevector-output-port-procedure",
765 0);
766
767#define SCM_GC_BOP "r6rs-bytevector-output-port"
768#define SCM_BOP_BUFFER_INITIAL_SIZE 4096
769
770/* Representation of a bop's internal buffer. */
771typedef struct
772{
773 size_t total_len;
774 size_t len;
775 size_t pos;
776 char *buffer;
777} scm_t_bop_buffer;
778
779
780/* Accessing a bop's buffer. */
781#define SCM_BOP_BUFFER(_port) \
782 ((scm_t_bop_buffer *) SCM_STREAM (_port))
783#define SCM_SET_BOP_BUFFER(_port, _buf) \
784 (SCM_SETSTREAM ((_port), (scm_t_bits) (_buf)))
785
786
787static inline void
788bop_buffer_init (scm_t_bop_buffer *buf)
789{
790 buf->total_len = buf->len = buf->pos = 0;
791 buf->buffer = NULL;
792}
793
794static inline void
795bop_buffer_grow (scm_t_bop_buffer *buf, size_t min_size)
796{
797 char *new_buf;
798 size_t new_size;
799
800 for (new_size = buf->total_len
801 ? buf->total_len : SCM_BOP_BUFFER_INITIAL_SIZE;
802 new_size < min_size;
803 new_size *= 2);
804
805 if (buf->buffer)
806 new_buf = scm_gc_realloc ((void *) buf->buffer, buf->total_len,
807 new_size, SCM_GC_BOP);
808 else
05762e72 809 new_buf = scm_gc_malloc_pointerless (new_size, SCM_GC_BOP);
1ee2c72e
LC
810
811 buf->buffer = new_buf;
812 buf->total_len = new_size;
813}
814
815static inline SCM
816make_bop (void)
817{
818 SCM port, bop_proc;
819 scm_t_port *c_port;
820 scm_t_bop_buffer *buf;
821 const unsigned long mode_bits = SCM_OPN | SCM_WRTNG;
822
a653d32a
AR
823 scm_i_pthread_mutex_lock (&scm_i_port_table_mutex);
824
1ee2c72e
LC
825 port = scm_new_port_table_entry (bytevector_output_port_type);
826
827 buf = (scm_t_bop_buffer *) scm_gc_malloc (sizeof (* buf), SCM_GC_BOP);
828 bop_buffer_init (buf);
829
830 c_port = SCM_PTAB_ENTRY (port);
831 c_port->write_buf = c_port->write_pos = c_port->write_end = NULL;
832 c_port->write_buf_size = 0;
833
834 SCM_SET_BOP_BUFFER (port, buf);
835
836 /* Mark PORT as open and writable. */
837 SCM_SET_CELL_TYPE (port, bytevector_output_port_type | mode_bits);
838
a653d32a
AR
839 scm_i_pthread_mutex_unlock (&scm_i_port_table_mutex);
840
1ee2c72e 841 /* Make the bop procedure. */
a653d32a 842 SCM_NEWSMOB (bop_proc, bytevector_output_port_procedure, buf);
1ee2c72e
LC
843
844 return (scm_values (scm_list_2 (port, bop_proc)));
845}
846
1ee2c72e
LC
847/* Write SIZE octets from DATA to PORT. */
848static void
849bop_write (SCM port, const void *data, size_t size)
850{
851 scm_t_bop_buffer *buf;
852
853 buf = SCM_BOP_BUFFER (port);
854
855 if (buf->pos + size > buf->total_len)
856 bop_buffer_grow (buf, buf->pos + size);
857
858 memcpy (buf->buffer + buf->pos, data, size);
859 buf->pos += size;
860 buf->len = (buf->len > buf->pos) ? buf->len : buf->pos;
861}
862
f1ce9199
LC
863static scm_t_off
864bop_seek (SCM port, scm_t_off offset, int whence)
1ee2c72e
LC
865#define FUNC_NAME "bop_seek"
866{
867 scm_t_bop_buffer *buf;
868
869 buf = SCM_BOP_BUFFER (port);
870 switch (whence)
871 {
872 case SEEK_CUR:
f1ce9199 873 offset += (scm_t_off) buf->pos;
1ee2c72e
LC
874 /* Fall through. */
875
876 case SEEK_SET:
877 if (offset < 0 || (unsigned) offset > buf->len)
878 scm_out_of_range (FUNC_NAME, scm_from_int (offset));
879 else
880 buf->pos = offset;
881 break;
882
883 case SEEK_END:
884 if (offset < 0 || (unsigned) offset >= buf->len)
885 scm_out_of_range (FUNC_NAME, scm_from_int (offset));
886 else
887 buf->pos = buf->len - (offset + 1);
888 break;
889
890 default:
891 scm_wrong_type_arg_msg (FUNC_NAME, 0, port,
892 "invalid `seek' parameter");
893 }
894
895 return buf->pos;
896}
897#undef FUNC_NAME
898
899/* Fetch data from a bop. */
900SCM_SMOB_APPLY (bytevector_output_port_procedure,
901 bop_proc_apply, 0, 0, 0, (SCM bop_proc))
902{
a653d32a 903 SCM bv;
1ee2c72e
LC
904 scm_t_bop_buffer *buf, result_buf;
905
a653d32a 906 buf = (scm_t_bop_buffer *) SCM_SMOB_DATA (bop_proc);
1ee2c72e
LC
907
908 result_buf = *buf;
909 bop_buffer_init (buf);
910
911 if (result_buf.len == 0)
912 bv = scm_c_take_bytevector (NULL, 0);
913 else
914 {
915 if (result_buf.total_len > result_buf.len)
916 /* Shrink the buffer. */
917 result_buf.buffer = scm_gc_realloc ((void *) result_buf.buffer,
918 result_buf.total_len,
919 result_buf.len,
920 SCM_GC_BOP);
921
922 bv = scm_c_take_bytevector ((signed char *) result_buf.buffer,
923 result_buf.len);
924 }
925
926 return bv;
927}
928
1ee2c72e
LC
929SCM_DEFINE (scm_open_bytevector_output_port,
930 "open-bytevector-output-port", 0, 1, 0,
931 (SCM transcoder),
932 "Return two values: an output port and a procedure. The latter "
933 "should be called with zero arguments to obtain a bytevector "
934 "containing the data accumulated by the port.")
935#define FUNC_NAME s_scm_open_bytevector_output_port
936{
937 if (!SCM_UNBNDP (transcoder) && !scm_is_false (transcoder))
938 transcoders_not_implemented ();
939
940 return (make_bop ());
941}
942#undef FUNC_NAME
943
944static inline void
945initialize_bytevector_output_ports (void)
946{
947 bytevector_output_port_type =
948 scm_make_port_type ("r6rs-bytevector-output-port",
949 NULL, bop_write);
950
951 scm_set_port_seek (bytevector_output_port_type, bop_seek);
1ee2c72e
LC
952}
953
954\f
955/* Custom binary output port ("cbop" for short). */
956
957static scm_t_bits custom_binary_output_port_type;
958
959/* Return the various procedures of PORT. */
960#define SCM_CBOP_WRITE_PROC(_port) \
961 SCM_SIMPLE_VECTOR_REF (SCM_PACK (SCM_STREAM (_port)), 0)
962
963
964static inline SCM
965make_cbop (SCM write_proc, SCM get_position_proc,
966 SCM set_position_proc, SCM close_proc)
967{
968 SCM port, method_vector;
969 scm_t_port *c_port;
970 const unsigned long mode_bits = SCM_OPN | SCM_WRTNG;
971
972 /* Store the various methods and bytevector in a vector. */
973 method_vector = scm_c_make_vector (4, SCM_BOOL_F);
974 SCM_SIMPLE_VECTOR_SET (method_vector, 0, write_proc);
975 SCM_SIMPLE_VECTOR_SET (method_vector, 1, get_position_proc);
976 SCM_SIMPLE_VECTOR_SET (method_vector, 2, set_position_proc);
977 SCM_SIMPLE_VECTOR_SET (method_vector, 3, close_proc);
978
a653d32a
AR
979 scm_i_pthread_mutex_lock (&scm_i_port_table_mutex);
980
1ee2c72e
LC
981 port = scm_new_port_table_entry (custom_binary_output_port_type);
982
983 /* Attach it the method vector. */
984 SCM_SETSTREAM (port, SCM_UNPACK (method_vector));
985
986 /* Have the port directly access the buffer (bytevector). */
987 c_port = SCM_PTAB_ENTRY (port);
988 c_port->write_buf = c_port->write_pos = c_port->write_end = NULL;
989 c_port->write_buf_size = c_port->read_buf_size = 0;
990
991 /* Mark PORT as open, writable and unbuffered. */
992 SCM_SET_CELL_TYPE (port, custom_binary_output_port_type | mode_bits);
993
a653d32a
AR
994 scm_i_pthread_mutex_unlock (&scm_i_port_table_mutex);
995
1ee2c72e
LC
996 return port;
997}
998
999/* Write SIZE octets from DATA to PORT. */
1000static void
1001cbop_write (SCM port, const void *data, size_t size)
1002#define FUNC_NAME "cbop_write"
1003{
1004 long int c_result;
1005 size_t c_written;
1006 SCM bv, write_proc, result;
1007
1008 /* XXX: Allocating a new bytevector at each `write' call is inefficient,
1009 but necessary since (1) we don't control the lifetime of the buffer
1010 pointed to by DATA, and (2) the `write!' procedure could capture the
1011 bytevector it is passed. */
1012 bv = scm_c_make_bytevector (size);
1013 memcpy (SCM_BYTEVECTOR_CONTENTS (bv), data, size);
1014
1015 write_proc = SCM_CBOP_WRITE_PROC (port);
1016
1017 /* Since the `write' procedure of Guile's ports has type `void', it must
1018 try hard to write exactly SIZE bytes, regardless of how many bytes the
1019 sink can handle. */
1020 for (c_written = 0;
1021 c_written < size;
1022 c_written += c_result)
1023 {
1024 result = scm_call_3 (write_proc, bv,
1025 scm_from_size_t (c_written),
1026 scm_from_size_t (size - c_written));
1027
1028 c_result = scm_to_long (result);
1029 if (SCM_UNLIKELY (c_result < 0
1030 || (size_t) c_result > (size - c_written)))
1031 scm_wrong_type_arg_msg (FUNC_NAME, 0, result,
1032 "R6RS custom binary output port `write!' "
1033 "returned a incorrect integer");
1034 }
1035}
1036#undef FUNC_NAME
1037
1038
1039SCM_DEFINE (scm_make_custom_binary_output_port,
1040 "make-custom-binary-output-port", 5, 0, 0,
1041 (SCM id, SCM write_proc, SCM get_position_proc,
1042 SCM set_position_proc, SCM close_proc),
1043 "Return a new custom binary output port whose output is drained "
1044 "by invoking @var{write_proc} and passing it a bytevector, an "
1045 "index where octets should be written, and an octet count.")
1046#define FUNC_NAME s_scm_make_custom_binary_output_port
1047{
1048 SCM_VALIDATE_STRING (1, id);
1049 SCM_VALIDATE_PROC (2, write_proc);
1050
1051 if (!scm_is_false (get_position_proc))
1052 SCM_VALIDATE_PROC (3, get_position_proc);
1053
1054 if (!scm_is_false (set_position_proc))
1055 SCM_VALIDATE_PROC (4, set_position_proc);
1056
1057 if (!scm_is_false (close_proc))
1058 SCM_VALIDATE_PROC (5, close_proc);
1059
1060 return (make_cbop (write_proc, get_position_proc, set_position_proc,
1061 close_proc));
1062}
1063#undef FUNC_NAME
1064
1065
1066/* Instantiate the custom binary output port type. */
1067static inline void
1068initialize_custom_binary_output_ports (void)
1069{
1070 custom_binary_output_port_type =
1071 scm_make_port_type ("r6rs-custom-binary-output-port",
1072 NULL, cbop_write);
1073
1ee2c72e
LC
1074 scm_set_port_seek (custom_binary_output_port_type, cbp_seek);
1075 scm_set_port_close (custom_binary_output_port_type, cbp_close);
1076}
1077
1078\f
1044537d
AR
1079/* Transcoded ports ("tp" for short). */
1080static scm_t_bits transcoded_port_type = 0;
1081
1082#define TP_INPUT_BUFFER_SIZE 4096
1083
1084#define SCM_TP_BINARY_PORT(_port) SCM_PACK (SCM_STREAM (_port))
1085
1086static inline SCM
1087make_tp (SCM binary_port, unsigned long mode)
1088{
1089 SCM port;
1090 scm_t_port *c_port;
1091 const unsigned long mode_bits = SCM_OPN | mode;
1092
1093 scm_i_pthread_mutex_lock (&scm_i_port_table_mutex);
1094
1095 port = scm_new_port_table_entry (transcoded_port_type);
1096
1097 SCM_SETSTREAM (port, SCM_UNPACK (binary_port));
1098
1099 SCM_SET_CELL_TYPE (port, transcoded_port_type | mode_bits);
1100
1101 if (SCM_INPUT_PORT_P (port))
1102 {
1103 c_port = SCM_PTAB_ENTRY (port);
1104 c_port->read_buf = scm_gc_malloc_pointerless (TP_INPUT_BUFFER_SIZE,
1105 "port buffer");
1106 c_port->read_pos = c_port->read_end = c_port->read_buf;
1107 c_port->read_buf_size = TP_INPUT_BUFFER_SIZE;
1108
1109 SCM_SET_CELL_WORD_0 (port, SCM_CELL_WORD_0 (port) & ~SCM_BUF0);
1110 }
1111
1112 scm_i_pthread_mutex_unlock (&scm_i_port_table_mutex);
1113
1114 return port;
1115}
1116
1117static void
1118tp_write (SCM port, const void *data, size_t size)
1119{
1120 scm_c_write (SCM_TP_BINARY_PORT (port), data, size);
1121}
1122
1123static int
1124tp_fill_input (SCM port)
1125{
1126 size_t count;
1127 scm_t_port *c_port = SCM_PTAB_ENTRY (port);
1128 SCM bport = SCM_TP_BINARY_PORT (port);
1129 scm_t_port *c_bport = SCM_PTAB_ENTRY (bport);
1130
1131 /* We can't use `scm_c_read' here, since it blocks until the whole
1132 block has been read or EOF. */
1133
1134 if (c_bport->rw_active == SCM_PORT_WRITE)
1135 scm_force_output (bport);
1136
1137 if (c_bport->read_pos >= c_bport->read_end)
1138 scm_fill_input (bport);
1139
1140 count = c_bport->read_end - c_bport->read_pos;
1141 if (count > c_port->read_buf_size)
1142 count = c_port->read_buf_size;
1143
1144 memcpy (c_port->read_buf, c_bport->read_pos, count);
1145 c_bport->read_pos += count;
1146
1147 if (c_bport->rw_random)
1148 c_bport->rw_active = SCM_PORT_READ;
1149
1150 if (count == 0)
1151 return EOF;
1152 else
1153 {
1154 c_port->read_pos = c_port->read_buf;
1155 c_port->read_end = c_port->read_buf + count;
1156 return *c_port->read_buf;
1157 }
1158}
1159
1160static void
1161tp_flush (SCM port)
1162{
1163 SCM binary_port = SCM_TP_BINARY_PORT (port);
1164 scm_t_port *c_port = SCM_PTAB_ENTRY (port);
1165 size_t count = c_port->write_pos - c_port->write_buf;
1166
1167 scm_c_write (binary_port, c_port->write_buf, count);
1168
1169 c_port->write_pos = c_port->write_buf;
1170 c_port->rw_active = SCM_PORT_NEITHER;
1171
1172 scm_force_output (binary_port);
1173}
1174
1175static int
1176tp_close (SCM port)
1177{
1178 if (SCM_OUTPUT_PORT_P (port))
1179 tp_flush (port);
1180 return scm_is_true (scm_close_port (SCM_TP_BINARY_PORT (port))) ? 0 : -1;
1181}
1182
1183static inline void
1184initialize_transcoded_ports (void)
1185{
1186 transcoded_port_type =
1187 scm_make_port_type ("r6rs-transcoded-port", tp_fill_input, tp_write);
1188
1189 scm_set_port_flush (transcoded_port_type, tp_flush);
1190 scm_set_port_close (transcoded_port_type, tp_close);
1191}
1192
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
1ee2c72e
LC
1221/* Initialization. */
1222
c0062328
LC
1223void
1224scm_register_r6rs_ports (void)
1225{
1226 scm_c_register_extension ("libguile-" SCM_EFFECTIVE_VERSION,
1227 "scm_init_r6rs_ports",
1228 (scm_t_extension_init_func) scm_init_r6rs_ports,
1229 NULL);
1230}
1231
1ee2c72e
LC
1232void
1233scm_init_r6rs_ports (void)
1234{
62e9a9b7 1235#include "libguile/r6rs-ports.x"
1ee2c72e
LC
1236
1237 initialize_bytevector_input_ports ();
1238 initialize_custom_binary_input_ports ();
1239 initialize_bytevector_output_ports ();
1240 initialize_custom_binary_output_ports ();
1044537d 1241 initialize_transcoded_ports ();
1ee2c72e 1242}