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