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