Changes in doc/ref:
[bpt/guile.git] / libguile / fports.c
CommitLineData
e81d98ec 1/* Copyright (C) 1995,1996,1997,1998,1999,2000,2001 Free Software Foundation, Inc.
0f2d19dd
JB
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2, or (at your option)
6 * any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this software; see the file COPYING. If not, write to
82892bed
JB
15 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
16 * Boston, MA 02111-1307 USA
0f2d19dd
JB
17 *
18 * As a special exception, the Free Software Foundation gives permission
19 * for additional uses of the text contained in its release of GUILE.
20 *
21 * The exception is that, if you link the GUILE library with other files
22 * to produce an executable, this does not by itself cause the
23 * resulting executable to be covered by the GNU General Public License.
24 * Your use of that executable is in no way restricted on account of
25 * linking the GUILE library code into it.
26 *
27 * This exception does not however invalidate any other reasons why
28 * the executable file might be covered by the GNU General Public License.
29 *
30 * This exception applies only to the code released by the
31 * Free Software Foundation under the name GUILE. If you copy
32 * code from other Free Software Foundation releases into a copy of
33 * GUILE, as the General Public License permits, the exception does
34 * not apply to the code that you add in this way. To avoid misleading
35 * anyone as to the status of such modified files, you must delete
36 * this exception notice from them.
37 *
38 * If you write modifications of your own for GUILE, it is your choice
39 * whether to permit this exception to apply to your modifications.
82892bed 40 * If you do not wish that, delete this exception notice. */
1bbd0b84 41
1bbd0b84 42
0f2d19dd
JB
43\f
44
45#include <stdio.h>
cb63cf9e 46#include <fcntl.h>
a0599745
MD
47#include "libguile/_scm.h"
48#include "libguile/strings.h"
a0599745 49#include "libguile/validate.h"
6b72ac1d
GH
50#include "libguile/gc.h"
51
a0599745 52#include "libguile/fports.h"
95b88819
GH
53
54#ifdef HAVE_STRING_H
55#include <string.h>
56#endif
0f2d19dd
JB
57#ifdef HAVE_UNISTD_H
58#include <unistd.h>
59#else
1be6b49c 60size_t fwrite ();
0f2d19dd 61#endif
b8b17bfd
MV
62#ifdef HAVE_IO_H
63#include <io.h>
64#endif
f47a5239 65#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
cb63cf9e
JB
66#include <sys/stat.h>
67#endif
0f2d19dd 68
cb63cf9e 69#include <errno.h>
e145dd02 70
a0599745 71#include "libguile/iselect.h"
edb810bb
SJ
72
73/* Some defines for Windows (native port, not Cygwin). */
82893676
MG
74#ifdef __MINGW32__
75# include <sys/stat.h>
76# include <winsock2.h>
77# define ftruncate(fd, size) chsize (fd, size)
78#endif /* __MINGW32__ */
cb63cf9e 79
a98bddfd 80
92c2555f 81scm_t_bits scm_tc16_fport;
a98bddfd
DH
82
83
19b27fa2 84/* default buffer size, used if the O/S won't supply a value. */
1be6b49c 85static const size_t default_buffer_size = 1024;
19b27fa2 86
cb63cf9e
JB
87/* create FPORT buffer with specified sizes (or -1 to use default size or
88 0 for no buffer. */
89static void
c014a02e 90scm_fport_buffer_add (SCM port, long read_size, int write_size)
c6c79933 91#define FUNC_NAME "scm_fport_buffer_add"
e145dd02 92{
92c2555f 93 scm_t_port *pt = SCM_PTAB_ENTRY (port);
e145dd02 94
cb63cf9e
JB
95 if (read_size == -1 || write_size == -1)
96 {
1be6b49c 97 size_t default_size;
f47a5239 98#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
cb63cf9e 99 struct stat st;
b8b17bfd 100 scm_t_fport *fp = SCM_FSTREAM (port);
cb63cf9e 101
19b27fa2
GH
102 default_size = (fstat (fp->fdes, &st) == -1) ? default_buffer_size
103 : st.st_blksize;
cb63cf9e 104#else
19b27fa2 105 default_size = default_buffer_size;
cb63cf9e
JB
106#endif
107 if (read_size == -1)
108 read_size = default_size;
109 if (write_size == -1)
110 write_size = default_size;
111 }
0f2d19dd 112
f5f2dcff 113 if (SCM_INPUT_PORT_P (port) && read_size > 0)
cb63cf9e 114 {
4c9419ac 115 pt->read_buf = scm_gc_malloc (read_size, "port buffer");
cb63cf9e
JB
116 pt->read_pos = pt->read_end = pt->read_buf;
117 pt->read_buf_size = read_size;
118 }
119 else
120 {
840ae05d 121 pt->read_pos = pt->read_buf = pt->read_end = &pt->shortbuf;
cb63cf9e
JB
122 pt->read_buf_size = 1;
123 }
1717856b 124
f5f2dcff 125 if (SCM_OUTPUT_PORT_P (port) && write_size > 0)
cb63cf9e 126 {
4c9419ac 127 pt->write_buf = scm_gc_malloc (write_size, "port buffer");
cb63cf9e
JB
128 pt->write_pos = pt->write_buf;
129 pt->write_buf_size = write_size;
130 }
131 else
132 {
133 pt->write_buf = pt->write_pos = &pt->shortbuf;
134 pt->write_buf_size = 1;
135 }
136
137 pt->write_end = pt->write_buf + pt->write_buf_size;
138 if (read_size > 0 || write_size > 0)
54778cd3 139 SCM_SET_CELL_WORD_0 (port, SCM_CELL_WORD_0 (port) & ~SCM_BUF0);
cb63cf9e 140 else
54778cd3 141 SCM_SET_CELL_WORD_0 (port, SCM_CELL_WORD_0 (port) | SCM_BUF0);
7a6f1ffa 142}
c6c79933 143#undef FUNC_NAME
7a6f1ffa 144
a1ec6916 145SCM_DEFINE (scm_setvbuf, "setvbuf", 2, 1, 0,
1bbd0b84 146 (SCM port, SCM mode, SCM size),
fc0d72d4
MD
147 "Set the buffering mode for @var{port}. @var{mode} can be:\n"
148 "@table @code\n"
149 "@item _IONBF\n"
150 "non-buffered\n"
151 "@item _IOLBF\n"
152 "line buffered\n"
153 "@item _IOFBF\n"
154 "block buffered, using a newly allocated buffer of @var{size} bytes.\n"
155 "If @var{size} is omitted, a default size will be used.\n"
2c1ae20e 156 "@end table")
1bbd0b84 157#define FUNC_NAME s_scm_setvbuf
7a6f1ffa 158{
1be6b49c 159 int cmode;
c014a02e 160 long csize;
92c2555f 161 scm_t_port *pt;
7a6f1ffa 162
78446828
MV
163 port = SCM_COERCE_OUTPORT (port);
164
3b3b36dd
GB
165 SCM_VALIDATE_OPFPORT (1,port);
166 SCM_VALIDATE_INUM_COPY (2,mode,cmode);
d3639214 167 if (cmode != _IONBF && cmode != _IOFBF && cmode != _IOLBF)
1bbd0b84 168 scm_out_of_range (FUNC_NAME, mode);
d3639214
GH
169
170 if (cmode == _IOLBF)
171 {
54778cd3 172 SCM_SET_CELL_WORD_0 (port, SCM_CELL_WORD_0 (port) | SCM_BUFLINE);
d3639214
GH
173 cmode = _IOFBF;
174 }
175 else
176 {
54778cd3 177 SCM_SET_CELL_WORD_0 (port, SCM_CELL_WORD_0 (port) ^ SCM_BUFLINE);
d3639214
GH
178 }
179
7a6f1ffa 180 if (SCM_UNBNDP (size))
cb63cf9e
JB
181 {
182 if (cmode == _IOFBF)
183 csize = -1;
184 else
185 csize = 0;
186 }
7a6f1ffa
GH
187 else
188 {
3b3b36dd 189 SCM_VALIDATE_INUM_COPY (3,size,csize);
cb63cf9e 190 if (csize < 0 || (cmode == _IONBF && csize > 0))
1bbd0b84 191 scm_out_of_range (FUNC_NAME, size);
7a6f1ffa 192 }
d3639214 193
cb63cf9e 194 pt = SCM_PTAB_ENTRY (port);
7a6f1ffa 195
4c9419ac
MV
196 /* silently discards buffered and put-back chars. */
197 if (pt->read_buf == pt->putback_buf)
198 {
199 pt->read_buf = pt->saved_read_buf;
200 pt->read_pos = pt->saved_read_pos;
201 pt->read_end = pt->saved_read_end;
202 pt->read_buf_size = pt->saved_read_buf_size;
203 }
cb63cf9e 204 if (pt->read_buf != &pt->shortbuf)
4c9419ac 205 scm_gc_free (pt->read_buf, pt->read_buf_size, "port buffer");
cb63cf9e 206 if (pt->write_buf != &pt->shortbuf)
4c9419ac 207 scm_gc_free (pt->write_buf, pt->write_buf_size, "port buffer");
7a6f1ffa 208
cb63cf9e
JB
209 scm_fport_buffer_add (port, csize, csize);
210 return SCM_UNSPECIFIED;
0f2d19dd 211}
1bbd0b84 212#undef FUNC_NAME
0f2d19dd 213
eadd48de
GH
214/* Move ports with the specified file descriptor to new descriptors,
215 * reseting the revealed count to 0.
0f2d19dd 216 */
1717856b 217
eadd48de 218void
6e8d25a6 219scm_evict_ports (int fd)
0f2d19dd 220{
c014a02e 221 long i;
0f2d19dd 222
f5fd8aa2 223 for (i = 0; i < scm_port_table_size; i++)
eadd48de 224 {
f5fd8aa2 225 SCM port = scm_port_table[i]->port;
cb63cf9e
JB
226
227 if (SCM_FPORTP (port))
eadd48de 228 {
92c2555f 229 scm_t_fport *fp = SCM_FSTREAM (port);
cb63cf9e
JB
230
231 if (fp->fdes == fd)
232 {
233 fp->fdes = dup (fd);
234 if (fp->fdes == -1)
235 scm_syserror ("scm_evict_ports");
236 scm_set_port_revealed_x (port, SCM_MAKINUM (0));
237 }
eadd48de
GH
238 }
239 }
240}
0f2d19dd 241
efa40607
DH
242
243SCM_DEFINE (scm_file_port_p, "file-port?", 1, 0, 0,
244 (SCM obj),
2069af38 245 "Determine whether @var{obj} is a port that is related to a file.")
efa40607
DH
246#define FUNC_NAME s_scm_file_port_p
247{
248 return SCM_BOOL (SCM_FPORTP (obj));
249}
250#undef FUNC_NAME
251
252
0f2d19dd
JB
253/* scm_open_file
254 * Return a new port open on a given file.
255 *
256 * The mode string must match the pattern: [rwa+]** which
257 * is interpreted in the usual unix way.
258 *
259 * Return the new port.
260 */
3b3b36dd 261SCM_DEFINE (scm_open_file, "open-file", 2, 0, 0,
1e6808ea
MG
262 (SCM filename, SCM mode),
263 "Open the file whose name is @var{filename}, and return a port\n"
fc0d72d4 264 "representing that file. The attributes of the port are\n"
1e6808ea
MG
265 "determined by the @var{mode} string. The way in which this is\n"
266 "interpreted is similar to C stdio. The first character must be\n"
267 "one of the following:\n"
fc0d72d4
MD
268 "@table @samp\n"
269 "@item r\n"
270 "Open an existing file for input.\n"
271 "@item w\n"
272 "Open a file for output, creating it if it doesn't already exist\n"
273 "or removing its contents if it does.\n"
274 "@item a\n"
1e6808ea
MG
275 "Open a file for output, creating it if it doesn't already\n"
276 "exist. All writes to the port will go to the end of the file.\n"
fc0d72d4
MD
277 "The \"append mode\" can be turned off while the port is in use\n"
278 "@pxref{Ports and File Descriptors, fcntl}\n"
1e6808ea
MG
279 "@end table\n"
280 "The following additional characters can be appended:\n"
fc0d72d4
MD
281 "@table @samp\n"
282 "@item +\n"
283 "Open the port for both input and output. E.g., @code{r+}: open\n"
284 "an existing file for both input and output.\n"
285 "@item 0\n"
1e6808ea
MG
286 "Create an \"unbuffered\" port. In this case input and output\n"
287 "operations are passed directly to the underlying port\n"
288 "implementation without additional buffering. This is likely to\n"
289 "slow down I/O operations. The buffering mode can be changed\n"
290 "while a port is in use @pxref{Ports and File Descriptors,\n"
291 "setvbuf}\n"
fc0d72d4
MD
292 "@item l\n"
293 "Add line-buffering to the port. The port output buffer will be\n"
294 "automatically flushed whenever a newline character is written.\n"
1e6808ea
MG
295 "@end table\n"
296 "In theory we could create read/write ports which were buffered\n"
297 "in one direction only. However this isn't included in the\n"
298 "current interfaces. If a file cannot be opened with the access\n"
299 "requested, @code{open-file} throws an exception.")
1bbd0b84 300#define FUNC_NAME s_scm_open_file
0f2d19dd 301{
19639113 302 SCM port;
cb63cf9e
JB
303 int fdes;
304 int flags = 0;
19639113 305 char *file;
1e6808ea 306 char *md;
cb63cf9e 307 char *ptr;
19639113 308
a6d9e5ab 309 SCM_VALIDATE_STRING (1, filename);
1e6808ea 310 SCM_VALIDATE_STRING (2, mode);
19639113 311
a6d9e5ab 312 file = SCM_STRING_CHARS (filename);
1e6808ea 313 md = SCM_STRING_CHARS (mode);
19639113 314
1e6808ea 315 switch (*md)
0f2d19dd 316 {
cb63cf9e
JB
317 case 'r':
318 flags |= O_RDONLY;
319 break;
320 case 'w':
321 flags |= O_WRONLY | O_CREAT | O_TRUNC;
322 break;
323 case 'a':
324 flags |= O_WRONLY | O_CREAT | O_APPEND;
325 break;
326 default:
1e6808ea 327 scm_out_of_range (FUNC_NAME, mode);
0f2d19dd 328 }
1e6808ea 329 ptr = md + 1;
cb63cf9e 330 while (*ptr != '\0')
e145dd02 331 {
cb63cf9e
JB
332 switch (*ptr)
333 {
334 case '+':
335 flags = (flags & ~(O_RDONLY | O_WRONLY)) | O_RDWR;
336 break;
9f561420
GH
337 case 'b':
338#if defined (O_BINARY)
339 flags |= O_BINARY;
340#endif
341 break;
cb63cf9e 342 case '0': /* unbuffered: handled later. */
d3639214 343 case 'l': /* line buffered: handled during output. */
cb63cf9e
JB
344 break;
345 default:
1e6808ea 346 scm_out_of_range (FUNC_NAME, mode);
cb63cf9e
JB
347 }
348 ptr++;
e145dd02 349 }
cb63cf9e
JB
350 SCM_SYSCALL (fdes = open (file, flags, 0666));
351 if (fdes == -1)
e145dd02 352 {
cb63cf9e
JB
353 int en = errno;
354
5d2d2ffc 355 SCM_SYSERROR_MSG ("~A: ~S",
cb63cf9e 356 scm_cons (scm_makfrom0str (strerror (en)),
5d2d2ffc 357 scm_cons (filename, SCM_EOL)), en);
0f2d19dd 358 }
1e6808ea 359 port = scm_fdes_to_port (fdes, md, filename);
0f2d19dd
JB
360 return port;
361}
1bbd0b84 362#undef FUNC_NAME
0f2d19dd 363
e145dd02 364\f
82893676
MG
365#ifdef __MINGW32__
366/*
367 * Try getting the appropiate file flags for a given file descriptor
368 * under Windows. This incorporates some fancy operations because Windows
369 * differentiates between file, pipe and socket descriptors.
370 */
371#ifndef O_ACCMODE
372# define O_ACCMODE 0x0003
373#endif
374
375static int getflags (int fdes)
376{
377 int flags = 0;
378 struct stat buf;
379 int error, optlen = sizeof (int);
380
381 /* Is this a socket ? */
382 if (getsockopt (fdes, SOL_SOCKET, SO_ERROR, (void *) &error, &optlen) >= 0)
383 flags = O_RDWR;
384 /* Maybe a regular file ? */
385 else if (fstat (fdes, &buf) < 0)
386 flags = -1;
387 else
388 {
389 /* Or an anonymous pipe handle ? */
b8b17bfd 390 if (buf.st_mode & _S_IFIFO)
8f99e3f3
SJ
391 flags = PeekNamedPipe ((HANDLE) _get_osfhandle (fdes), NULL, 0,
392 NULL, NULL, NULL) ? O_RDONLY : O_WRONLY;
82893676 393 /* stdin ? */
b8b17bfd 394 else if (fdes == fileno (stdin) && isatty (fdes))
82893676
MG
395 flags = O_RDONLY;
396 /* stdout / stderr ? */
b8b17bfd
MV
397 else if ((fdes == fileno (stdout) || fdes == fileno (stderr)) &&
398 isatty (fdes))
82893676
MG
399 flags = O_WRONLY;
400 else
401 flags = buf.st_mode;
402 }
403 return flags;
404}
405#endif /* __MINGW32__ */
406
cb63cf9e 407/* Building Guile ports from a file descriptor. */
e145dd02 408
cb63cf9e 409/* Build a Scheme port from an open file descriptor `fdes'.
a089567e
JB
410 MODE indicates whether FILE is open for reading or writing; it uses
411 the same notation as open-file's second argument.
19b27fa2
GH
412 NAME is a string to be used as the port's filename.
413*/
a089567e 414SCM
cb63cf9e 415scm_fdes_to_port (int fdes, char *mode, SCM name)
19b27fa2 416#define FUNC_NAME "scm_fdes_to_port"
a089567e
JB
417{
418 long mode_bits = scm_mode_bits (mode);
419 SCM port;
92c2555f 420 scm_t_port *pt;
19b27fa2
GH
421 int flags;
422
423 /* test that fdes is valid. */
82893676
MG
424#ifdef __MINGW32__
425 flags = getflags (fdes);
426#else
19b27fa2 427 flags = fcntl (fdes, F_GETFL, 0);
82893676 428#endif
19b27fa2
GH
429 if (flags == -1)
430 SCM_SYSERROR;
431 flags &= O_ACCMODE;
432 if (flags != O_RDWR
433 && ((flags != O_WRONLY && (mode_bits & SCM_WRTNG))
434 || (flags != O_RDONLY && (mode_bits & SCM_RDNG))))
435 {
436 SCM_MISC_ERROR ("requested file mode not available on fdes", SCM_EOL);
437 }
a089567e 438
228a24ef 439 port = scm_cell (scm_tc16_fport, 0);
a089567e 440 SCM_DEFER_INTS;
cb63cf9e
JB
441 pt = scm_add_to_port_table (port);
442 SCM_SETPTAB_ENTRY (port, pt);
54778cd3 443 SCM_SET_CELL_TYPE (port, (scm_tc16_fport | mode_bits));
cb63cf9e 444
a089567e 445 {
92c2555f 446 scm_t_fport *fp
4c9419ac 447 = (scm_t_fport *) scm_gc_malloc (sizeof (scm_t_fport), "file port");
c6c79933 448
cb63cf9e 449 fp->fdes = fdes;
0de97b83 450 pt->rw_random = SCM_FDES_RANDOM_P (fdes);
cb63cf9e
JB
451 SCM_SETSTREAM (port, fp);
452 if (mode_bits & SCM_BUF0)
453 scm_fport_buffer_add (port, 0, 0);
454 else
455 scm_fport_buffer_add (port, -1, -1);
a089567e 456 }
b24b5e13 457 SCM_SET_FILENAME (port, name);
a089567e 458 SCM_ALLOW_INTS;
e145dd02
JB
459 return port;
460}
19b27fa2 461#undef FUNC_NAME
e145dd02 462
affc96b5 463/* Return a lower bound on the number of bytes available for input. */
cb63cf9e 464static int
affc96b5 465fport_input_waiting (SCM port)
e145dd02 466{
cb63cf9e
JB
467 int fdes = SCM_FSTREAM (port)->fdes;
468
469#ifdef HAVE_SELECT
470 struct timeval timeout;
471 SELECT_TYPE read_set;
472 SELECT_TYPE write_set;
473 SELECT_TYPE except_set;
474
475 FD_ZERO (&read_set);
476 FD_ZERO (&write_set);
477 FD_ZERO (&except_set);
478
479 FD_SET (fdes, &read_set);
480
481 timeout.tv_sec = 0;
482 timeout.tv_usec = 0;
483
484 if (select (SELECT_SET_SIZE,
485 &read_set, &write_set, &except_set, &timeout)
486 < 0)
affc96b5
GH
487 scm_syserror ("fport_input_waiting");
488 return FD_ISSET (fdes, &read_set) ? 1 : 0;
cb63cf9e
JB
489#elif defined (FIONREAD)
490 int remir;
491 ioctl(fdes, FIONREAD, &remir);
492 return remir;
493#else
affc96b5 494 scm_misc_error ("fport_input_waiting",
cb63cf9e
JB
495 "Not fully implemented on this platform",
496 SCM_EOL);
497#endif
a089567e
JB
498}
499
cb63cf9e 500\f
0f2d19dd 501static int
e81d98ec 502fport_print (SCM exp, SCM port, scm_print_state *pstate SCM_UNUSED)
0f2d19dd 503{
b3ec3c64
MD
504 scm_puts ("#<", port);
505 scm_print_port_mode (exp, port);
506 if (SCM_OPFPORTP (exp))
0f2d19dd 507 {
b3ec3c64 508 int fdes;
b24b5e13
DH
509 SCM name = SCM_FILENAME (exp);
510 if (SCM_STRINGP (name) || SCM_SYMBOLP (name))
511 scm_display (name, port);
512 else
513 scm_puts (SCM_PTOBNAME (SCM_PTOBNUM (exp)), port);
b3ec3c64
MD
514 scm_putc (' ', port);
515 fdes = (SCM_FSTREAM (exp))->fdes;
516
82893676 517#ifdef HAVE_TTYNAME
b3ec3c64
MD
518 if (isatty (fdes))
519 scm_puts (ttyname (fdes), port);
520 else
82893676 521#endif /* HAVE_TTYNAME */
b3ec3c64 522 scm_intprint (fdes, 10, port);
0f2d19dd
JB
523 }
524 else
525 {
b3ec3c64
MD
526 scm_puts (SCM_PTOBNAME (SCM_PTOBNUM (exp)), port);
527 scm_putc (' ', port);
63bcad19 528 scm_intprint ((scm_t_bits) SCM_PTAB_ENTRY (exp), 16, port);
0f2d19dd 529 }
b3ec3c64
MD
530 scm_putc ('>', port);
531 return 1;
0f2d19dd
JB
532}
533
cb63cf9e
JB
534#ifdef GUILE_ISELECT
535/* thread-local block for input on fport's fdes. */
536static void
537fport_wait_for_input (SCM port)
3cb988bd 538{
cb63cf9e 539 int fdes = SCM_FSTREAM (port)->fdes;
3cb988bd 540
affc96b5 541 if (!fport_input_waiting (port))
8122b543 542 {
cb63cf9e
JB
543 int n;
544 SELECT_TYPE readfds;
545 int flags = fcntl (fdes, F_GETFL);
546
547 if (flags == -1)
548 scm_syserror ("scm_fdes_wait_for_input");
549 if (!(flags & O_NONBLOCK))
550 do
551 {
552 FD_ZERO (&readfds);
553 FD_SET (fdes, &readfds);
554 n = scm_internal_select (fdes + 1, &readfds, NULL, NULL, NULL);
555 }
556 while (n == -1 && errno == EINTR);
8122b543 557 }
3cb988bd 558}
0f2d19dd
JB
559#endif
560
affc96b5 561static void fport_flush (SCM port);
0f2d19dd 562
c2da2648
GH
563/* fill a port's read-buffer with a single read. returns the first
564 char or EOF if end of file. */
0f2d19dd 565static int
affc96b5 566fport_fill_input (SCM port)
0f2d19dd 567{
c014a02e 568 long count;
92c2555f
MV
569 scm_t_port *pt = SCM_PTAB_ENTRY (port);
570 scm_t_fport *fp = SCM_FSTREAM (port);
cb63cf9e 571
cb63cf9e
JB
572#ifdef GUILE_ISELECT
573 fport_wait_for_input (port);
574#endif
575 SCM_SYSCALL (count = read (fp->fdes, pt->read_buf, pt->read_buf_size));
576 if (count == -1)
affc96b5 577 scm_syserror ("fport_fill_input");
cb63cf9e
JB
578 if (count == 0)
579 return EOF;
580 else
581 {
5c070ca7 582 pt->read_pos = pt->read_buf;
cb63cf9e 583 pt->read_end = pt->read_buf + count;
5c070ca7 584 return *pt->read_buf;
cb63cf9e 585 }
0f2d19dd
JB
586}
587
cb63cf9e 588static off_t
affc96b5 589fport_seek (SCM port, off_t offset, int whence)
0f2d19dd 590{
92c2555f
MV
591 scm_t_port *pt = SCM_PTAB_ENTRY (port);
592 scm_t_fport *fp = SCM_FSTREAM (port);
7dcb364d
GH
593 off_t rv;
594 off_t result;
595
596 if (pt->rw_active == SCM_PORT_WRITE)
597 {
598 if (offset != 0 || whence != SEEK_CUR)
599 {
600 fport_flush (port);
601 result = rv = lseek (fp->fdes, offset, whence);
602 }
603 else
604 {
605 /* read current position without disturbing the buffer. */
606 rv = lseek (fp->fdes, offset, whence);
607 result = rv + (pt->write_pos - pt->write_buf);
608 }
609 }
610 else if (pt->rw_active == SCM_PORT_READ)
611 {
612 if (offset != 0 || whence != SEEK_CUR)
613 {
614 /* could expand to avoid a second seek. */
615 scm_end_input (port);
616 result = rv = lseek (fp->fdes, offset, whence);
617 }
618 else
619 {
620 /* read current position without disturbing the buffer
621 (particularly the unread-char buffer). */
622 rv = lseek (fp->fdes, offset, whence);
623 result = rv - (pt->read_end - pt->read_pos);
624
625 if (pt->read_buf == pt->putback_buf)
626 result -= pt->saved_read_end - pt->saved_read_pos;
627 }
628 }
629 else /* SCM_PORT_NEITHER */
630 {
631 result = rv = lseek (fp->fdes, offset, whence);
632 }
cb8dfa3f 633
7dcb364d 634 if (rv == -1)
affc96b5 635 scm_syserror ("fport_seek");
7dcb364d 636
cb8dfa3f 637 return result;
0f2d19dd
JB
638}
639
840ae05d 640static void
affc96b5 641fport_truncate (SCM port, off_t length)
840ae05d 642{
92c2555f 643 scm_t_fport *fp = SCM_FSTREAM (port);
840ae05d
JB
644
645 if (ftruncate (fp->fdes, length) == -1)
646 scm_syserror ("ftruncate");
647}
648
0c6d2191
GH
649/* helper for fport_write: try to write data, using multiple system
650 calls if required. */
651#define FUNC_NAME "write_all"
652static void write_all (SCM port, const void *data, size_t remaining)
653{
654 int fdes = SCM_FSTREAM (port)->fdes;
655
656 while (remaining > 0)
657 {
82893676 658 size_t done;
0c6d2191
GH
659
660 SCM_SYSCALL (done = write (fdes, data, remaining));
661
662 if (done == -1)
663 SCM_SYSERROR;
664 remaining -= done;
665 data = ((const char *) data) + done;
666 }
667}
668#undef FUNC_NAME
669
31703ab8 670static void
8aa011a1 671fport_write (SCM port, const void *data, size_t size)
31703ab8 672{
0c6d2191 673 /* this procedure tries to minimize the number of writes/flushes. */
92c2555f 674 scm_t_port *pt = SCM_PTAB_ENTRY (port);
31703ab8 675
0c6d2191
GH
676 if (pt->write_buf == &pt->shortbuf
677 || (pt->write_pos == pt->write_buf && size >= pt->write_buf_size))
31703ab8 678 {
0c6d2191
GH
679 /* "unbuffered" port, or
680 port with empty buffer and data won't fit in buffer. */
681 write_all (port, data, size);
682 return;
31703ab8 683 }
d3639214 684
0c6d2191
GH
685 {
686 off_t space = pt->write_end - pt->write_pos;
687
688 if (size <= space)
689 {
690 /* data fits in buffer. */
691 memcpy (pt->write_pos, data, size);
692 pt->write_pos += size;
693 if (pt->write_pos == pt->write_end)
694 {
affc96b5 695 fport_flush (port);
0c6d2191
GH
696 /* we can skip the line-buffering check if nothing's buffered. */
697 return;
698 }
699 }
700 else
701 {
702 memcpy (pt->write_pos, data, space);
703 pt->write_pos = pt->write_end;
704 fport_flush (port);
705 {
706 const void *ptr = ((const char *) data) + space;
707 size_t remaining = size - space;
708
709 if (size >= pt->write_buf_size)
710 {
711 write_all (port, ptr, remaining);
712 return;
713 }
714 else
715 {
716 memcpy (pt->write_pos, ptr, remaining);
717 pt->write_pos += remaining;
718 }
31703ab8 719 }
0c6d2191 720 }
31703ab8 721
0c6d2191
GH
722 /* handle line buffering. */
723 if ((SCM_CELL_WORD_0 (port) & SCM_BUFLINE) && memchr (data, '\n', size))
724 fport_flush (port);
725 }
31703ab8
GH
726}
727
728/* becomes 1 when process is exiting: normal exception handling won't
729 work by this time. */
cb63cf9e 730extern int terminating;
0f2d19dd 731
cb63cf9e 732static void
affc96b5 733fport_flush (SCM port)
0f2d19dd 734{
92c2555f
MV
735 scm_t_port *pt = SCM_PTAB_ENTRY (port);
736 scm_t_fport *fp = SCM_FSTREAM (port);
6f760c1d 737 unsigned char *ptr = pt->write_buf;
c014a02e
ML
738 long init_size = pt->write_pos - pt->write_buf;
739 long remaining = init_size;
0f2d19dd 740
cb63cf9e
JB
741 while (remaining > 0)
742 {
c014a02e 743 long count;
cb63cf9e
JB
744
745 SCM_SYSCALL (count = write (fp->fdes, ptr, remaining));
746 if (count < 0)
747 {
748 /* error. assume nothing was written this call, but
749 fix up the buffer for any previous successful writes. */
c014a02e 750 long done = init_size - remaining;
cb63cf9e
JB
751
752 if (done > 0)
753 {
754 int i;
755
756 for (i = 0; i < remaining; i++)
757 {
758 *(pt->write_buf + i) = *(pt->write_buf + done + i);
759 }
760 pt->write_pos = pt->write_buf + remaining;
761 }
6b72ac1d 762 if (terminating)
cb63cf9e
JB
763 {
764 const char *msg = "Error: could not flush file-descriptor ";
765 char buf[11];
766
767 write (2, msg, strlen (msg));
768 sprintf (buf, "%d\n", fp->fdes);
769 write (2, buf, strlen (buf));
770
771 count = remaining;
772 }
6b72ac1d
GH
773 else if (scm_gc_running_p)
774 {
775 /* silently ignore the error. scm_error would abort if we
776 called it now. */
777 count = remaining;
778 }
779 else
780 scm_syserror ("fport_flush");
cb63cf9e
JB
781 }
782 ptr += count;
783 remaining -= count;
784 }
785 pt->write_pos = pt->write_buf;
61e452ba 786 pt->rw_active = SCM_PORT_NEITHER;
840ae05d
JB
787}
788
283a1a0e 789/* clear the read buffer and adjust the file position for unread bytes. */
840ae05d 790static void
affc96b5 791fport_end_input (SCM port, int offset)
840ae05d 792{
92c2555f
MV
793 scm_t_fport *fp = SCM_FSTREAM (port);
794 scm_t_port *pt = SCM_PTAB_ENTRY (port);
283a1a0e
GH
795
796 offset += pt->read_end - pt->read_pos;
840ae05d 797
840ae05d
JB
798 if (offset > 0)
799 {
800 pt->read_pos = pt->read_end;
801 /* will throw error if unread-char used at beginning of file
802 then attempting to write. seems correct. */
803 if (lseek (fp->fdes, -offset, SEEK_CUR) == -1)
affc96b5 804 scm_syserror ("fport_end_input");
840ae05d 805 }
61e452ba 806 pt->rw_active = SCM_PORT_NEITHER;
8f29fbd0
JB
807}
808
6a2c4c81 809static int
affc96b5 810fport_close (SCM port)
6a2c4c81 811{
92c2555f
MV
812 scm_t_fport *fp = SCM_FSTREAM (port);
813 scm_t_port *pt = SCM_PTAB_ENTRY (port);
cb63cf9e 814 int rv;
840ae05d 815
affc96b5 816 fport_flush (port);
cb63cf9e
JB
817 SCM_SYSCALL (rv = close (fp->fdes));
818 if (rv == -1 && errno != EBADF)
6b72ac1d
GH
819 {
820 if (scm_gc_running_p)
821 /* silently ignore the error. scm_error would abort if we
822 called it now. */
823 ;
824 else
825 scm_syserror ("fport_close");
826 }
6c951427
GH
827 if (pt->read_buf == pt->putback_buf)
828 pt->read_buf = pt->saved_read_buf;
cb63cf9e 829 if (pt->read_buf != &pt->shortbuf)
4c9419ac 830 scm_gc_free (pt->read_buf, pt->read_buf_size, "port buffer");
cb63cf9e 831 if (pt->write_buf != &pt->shortbuf)
4c9419ac
MV
832 scm_gc_free (pt->write_buf, pt->write_buf_size, "port buffer");
833 scm_gc_free (fp, sizeof (*fp), "file port");
cb63cf9e 834 return rv;
6a2c4c81
JB
835}
836
1be6b49c 837static size_t
affc96b5 838fport_free (SCM port)
b3ec3c64 839{
affc96b5 840 fport_close (port);
b3ec3c64
MD
841 return 0;
842}
843
92c2555f 844static scm_t_bits
b3ec3c64
MD
845scm_make_fptob ()
846{
92c2555f 847 scm_t_bits tc = scm_make_port_type ("file", fport_fill_input, fport_write);
a98bddfd 848
affc96b5 849 scm_set_port_free (tc, fport_free);
e841c3e0 850 scm_set_port_print (tc, fport_print);
affc96b5
GH
851 scm_set_port_flush (tc, fport_flush);
852 scm_set_port_end_input (tc, fport_end_input);
853 scm_set_port_close (tc, fport_close);
854 scm_set_port_seek (tc, fport_seek);
855 scm_set_port_truncate (tc, fport_truncate);
856 scm_set_port_input_waiting (tc, fport_input_waiting);
a98bddfd
DH
857
858 return tc;
b3ec3c64 859}
0f2d19dd 860
0f2d19dd
JB
861void
862scm_init_fports ()
0f2d19dd 863{
a98bddfd
DH
864 scm_tc16_fport = scm_make_fptob ();
865
86d31dfe
MV
866 scm_c_define ("_IOFBF", SCM_MAKINUM (_IOFBF));
867 scm_c_define ("_IOLBF", SCM_MAKINUM (_IOLBF));
868 scm_c_define ("_IONBF", SCM_MAKINUM (_IONBF));
a98bddfd
DH
869
870#ifndef SCM_MAGIC_SNARFER
871#include "libguile/fports.x"
872#endif
0f2d19dd 873}
89e00824
ML
874
875/*
876 Local Variables:
877 c-file-style: "gnu"
878 End:
879*/