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