* *.c: Pervasive software-engineering-motivated rewrite of
[bpt/guile.git] / libguile / fports.c
1 /* Copyright (C) 1995,1996,1997,1998,1999 Free Software Foundation, Inc.
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
15 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
16 * Boston, MA 02111-1307 USA
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.
40 * If you do not wish that, delete this exception notice. */
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
45 \f
46
47 #include <stdio.h>
48 #include <fcntl.h>
49 #include "_scm.h"
50
51 #include "scm_validate.h"
52 #include "fports.h"
53
54 #ifdef HAVE_STRING_H
55 #include <string.h>
56 #endif
57 #ifdef HAVE_UNISTD_H
58 #include <unistd.h>
59 #else
60 scm_sizet fwrite ();
61 #endif
62 #ifdef HAVE_ST_BLKSIZE
63 #include <sys/stat.h>
64 #endif
65
66 #include <errno.h>
67
68 #include "iselect.h"
69
70 /* create FPORT buffer with specified sizes (or -1 to use default size or
71 0 for no buffer. */
72 static void
73 scm_fport_buffer_add (SCM port, int read_size, int write_size)
74 {
75 struct scm_fport *fp = SCM_FSTREAM (port);
76 scm_port *pt = SCM_PTAB_ENTRY (port);
77 char *s_scm_fport_buffer_add = "scm_fport_buffer_add";
78
79 if (read_size == -1 || write_size == -1)
80 {
81 int default_size;
82 #ifdef HAVE_ST_BLKSIZE
83 struct stat st;
84
85 if (fstat (fp->fdes, &st) == -1)
86 scm_syserror (s_scm_fport_buffer_add);
87 default_size = st.st_blksize;
88 #else
89 default_size = 1024;
90 #endif
91 if (read_size == -1)
92 read_size = default_size;
93 if (write_size == -1)
94 write_size = default_size;
95 }
96
97 if (SCM_INPORTP (port) && read_size > 0)
98 {
99 pt->read_buf = malloc (read_size);
100 if (pt->read_buf == NULL)
101 scm_memory_error (s_scm_fport_buffer_add);
102 pt->read_pos = pt->read_end = pt->read_buf;
103 pt->read_buf_size = read_size;
104 }
105 else
106 {
107 pt->read_pos = pt->read_buf = pt->read_end = &pt->shortbuf;
108 pt->read_buf_size = 1;
109 }
110
111 if (SCM_OUTPORTP (port) && write_size > 0)
112 {
113 pt->write_buf = malloc (write_size);
114 if (pt->write_buf == NULL)
115 scm_memory_error (s_scm_fport_buffer_add);
116 pt->write_pos = pt->write_buf;
117 pt->write_buf_size = write_size;
118 }
119 else
120 {
121 pt->write_buf = pt->write_pos = &pt->shortbuf;
122 pt->write_buf_size = 1;
123 }
124
125 pt->write_end = pt->write_buf + pt->write_buf_size;
126 if (read_size > 0 || write_size > 0)
127 SCM_SETCAR (port, SCM_CAR (port) & ~SCM_BUF0);
128 else
129 SCM_SETCAR (port, (SCM_CAR (port) | SCM_BUF0));
130 }
131
132 GUILE_PROC (scm_setvbuf, "setvbuf", 2, 1, 0,
133 (SCM port, SCM mode, SCM size),
134 "")
135 #define FUNC_NAME s_scm_setvbuf
136 {
137 int cmode, csize;
138 scm_port *pt;
139
140 port = SCM_COERCE_OUTPORT (port);
141
142 SCM_VALIDATE_OPFPORT(1,port);
143 SCM_VALIDATE_INT_COPY(2,mode,cmode);
144 if (cmode != _IONBF && cmode != _IOFBF && cmode != _IOLBF)
145 scm_out_of_range (FUNC_NAME, mode);
146
147 if (cmode == _IOLBF)
148 {
149 SCM_SETCAR (port, SCM_CAR (port) | SCM_BUFLINE);
150 cmode = _IOFBF;
151 }
152 else
153 {
154 SCM_SETCAR (port, SCM_CAR (port) ^ SCM_BUFLINE);
155 }
156
157 if (SCM_UNBNDP (size))
158 {
159 if (cmode == _IOFBF)
160 csize = -1;
161 else
162 csize = 0;
163 }
164 else
165 {
166 SCM_VALIDATE_INT_COPY(3,size,csize);
167 if (csize < 0 || (cmode == _IONBF && csize > 0))
168 scm_out_of_range (FUNC_NAME, size);
169 }
170
171 pt = SCM_PTAB_ENTRY (port);
172
173 /* silently discards buffered chars. */
174 if (pt->read_buf != &pt->shortbuf)
175 scm_must_free (pt->read_buf);
176 if (pt->write_buf != &pt->shortbuf)
177 scm_must_free (pt->write_buf);
178
179 scm_fport_buffer_add (port, csize, csize);
180 return SCM_UNSPECIFIED;
181 }
182 #undef FUNC_NAME
183
184 /* Move ports with the specified file descriptor to new descriptors,
185 * reseting the revealed count to 0.
186 */
187
188 void
189 scm_evict_ports (fd)
190 int fd;
191 {
192 int i;
193
194 for (i = 0; i < scm_port_table_size; i++)
195 {
196 SCM port = scm_port_table[i]->port;
197
198 if (SCM_FPORTP (port))
199 {
200 struct scm_fport *fp = SCM_FSTREAM (port);
201
202 if (fp->fdes == fd)
203 {
204 fp->fdes = dup (fd);
205 if (fp->fdes == -1)
206 scm_syserror ("scm_evict_ports");
207 scm_set_port_revealed_x (port, SCM_MAKINUM (0));
208 }
209 }
210 }
211 }
212
213 /* scm_open_file
214 * Return a new port open on a given file.
215 *
216 * The mode string must match the pattern: [rwa+]** which
217 * is interpreted in the usual unix way.
218 *
219 * Return the new port.
220 */
221 GUILE_PROC(scm_open_file, "open-file", 2, 0, 0,
222 (SCM filename, SCM modes),
223 "")
224 #define FUNC_NAME s_scm_open_file
225 {
226 SCM port;
227 int fdes;
228 int flags = 0;
229 char *file;
230 char *mode;
231 char *ptr;
232
233 SCM_VALIDATE_ROSTRING(1,filename);
234 SCM_VALIDATE_ROSTRING(2,modes);
235 if (SCM_SUBSTRP (filename))
236 filename = scm_makfromstr (SCM_ROCHARS (filename), SCM_ROLENGTH (filename), 0);
237 if (SCM_SUBSTRP (modes))
238 modes = scm_makfromstr (SCM_ROCHARS (modes), SCM_ROLENGTH (modes), 0);
239
240 file = SCM_ROCHARS (filename);
241 mode = SCM_ROCHARS (modes);
242
243 switch (*mode)
244 {
245 case 'r':
246 flags |= O_RDONLY;
247 break;
248 case 'w':
249 flags |= O_WRONLY | O_CREAT | O_TRUNC;
250 break;
251 case 'a':
252 flags |= O_WRONLY | O_CREAT | O_APPEND;
253 break;
254 default:
255 scm_out_of_range (FUNC_NAME, modes);
256 }
257 ptr = mode + 1;
258 while (*ptr != '\0')
259 {
260 switch (*ptr)
261 {
262 case '+':
263 flags = (flags & ~(O_RDONLY | O_WRONLY)) | O_RDWR;
264 break;
265 case '0': /* unbuffered: handled later. */
266 case 'b': /* 'binary' mode: ignored. */
267 case 'l': /* line buffered: handled during output. */
268 break;
269 default:
270 scm_out_of_range (FUNC_NAME, modes);
271 }
272 ptr++;
273 }
274 SCM_SYSCALL (fdes = open (file, flags, 0666));
275 if (fdes == -1)
276 {
277 int en = errno;
278
279 scm_syserror_msg (FUNC_NAME, "%s: %S",
280 scm_cons (scm_makfrom0str (strerror (en)),
281 scm_cons (filename, SCM_EOL)),
282 en);
283 }
284 port = scm_fdes_to_port (fdes, mode, filename);
285 return port;
286 }
287 #undef FUNC_NAME
288
289 \f
290 /* Building Guile ports from a file descriptor. */
291
292 /* Build a Scheme port from an open file descriptor `fdes'.
293 MODE indicates whether FILE is open for reading or writing; it uses
294 the same notation as open-file's second argument.
295 Use NAME as the port's filename. */
296
297 SCM
298 scm_fdes_to_port (int fdes, char *mode, SCM name)
299 {
300 long mode_bits = scm_mode_bits (mode);
301 SCM port;
302 scm_port *pt;
303
304 SCM_NEWCELL (port);
305 SCM_DEFER_INTS;
306 pt = scm_add_to_port_table (port);
307 SCM_SETPTAB_ENTRY (port, pt);
308 SCM_SETCAR (port, (scm_tc16_fport | mode_bits));
309
310 {
311 struct scm_fport *fp
312 = (struct scm_fport *) malloc (sizeof (struct scm_fport));
313 if (fp == NULL)
314 scm_memory_error ("scm_fdes_to_port");
315 fp->fdes = fdes;
316 pt->rw_random = SCM_FDES_RANDOM_P (fdes);
317 SCM_SETSTREAM (port, fp);
318 if (mode_bits & SCM_BUF0)
319 scm_fport_buffer_add (port, 0, 0);
320 else
321 scm_fport_buffer_add (port, -1, -1);
322 }
323 SCM_PTAB_ENTRY (port)->file_name = name;
324 SCM_ALLOW_INTS;
325 return port;
326 }
327
328
329 /* Return a lower bound on the number of bytes available for input. */
330 static int
331 fport_input_waiting (SCM port)
332 {
333 int fdes = SCM_FSTREAM (port)->fdes;
334
335 #ifdef HAVE_SELECT
336 struct timeval timeout;
337 SELECT_TYPE read_set;
338 SELECT_TYPE write_set;
339 SELECT_TYPE except_set;
340
341 FD_ZERO (&read_set);
342 FD_ZERO (&write_set);
343 FD_ZERO (&except_set);
344
345 FD_SET (fdes, &read_set);
346
347 timeout.tv_sec = 0;
348 timeout.tv_usec = 0;
349
350 if (select (SELECT_SET_SIZE,
351 &read_set, &write_set, &except_set, &timeout)
352 < 0)
353 scm_syserror ("fport_input_waiting");
354 return FD_ISSET (fdes, &read_set) ? 1 : 0;
355 #elif defined (FIONREAD)
356 int remir;
357 ioctl(fdes, FIONREAD, &remir);
358 return remir;
359 #else
360 scm_misc_error ("fport_input_waiting",
361 "Not fully implemented on this platform",
362 SCM_EOL);
363 #endif
364 }
365
366 \f
367 static int
368 prinfport (SCM exp,SCM port,scm_print_state *pstate)
369 {
370 scm_puts ("#<", port);
371 scm_print_port_mode (exp, port);
372 if (SCM_OPFPORTP (exp))
373 {
374 int fdes;
375 SCM name = SCM_PTAB_ENTRY (exp)->file_name;
376 scm_puts (SCM_NIMP (name) && SCM_ROSTRINGP (name)
377 ? SCM_ROCHARS (name)
378 : SCM_PTOBNAME (SCM_PTOBNUM (exp)),
379 port);
380 scm_putc (' ', port);
381 fdes = (SCM_FSTREAM (exp))->fdes;
382
383 if (isatty (fdes))
384 scm_puts (ttyname (fdes), port);
385 else
386 scm_intprint (fdes, 10, port);
387 }
388 else
389 {
390 scm_puts (SCM_PTOBNAME (SCM_PTOBNUM (exp)), port);
391 scm_putc (' ', port);
392 scm_intprint (SCM_CDR (exp), 16, port);
393 }
394 scm_putc ('>', port);
395 return 1;
396 }
397
398 #ifdef GUILE_ISELECT
399 /* thread-local block for input on fport's fdes. */
400 static void
401 fport_wait_for_input (SCM port)
402 {
403 int fdes = SCM_FSTREAM (port)->fdes;
404
405 if (!fport_input_waiting (port))
406 {
407 int n;
408 SELECT_TYPE readfds;
409 int flags = fcntl (fdes, F_GETFL);
410
411 if (flags == -1)
412 scm_syserror ("scm_fdes_wait_for_input");
413 if (!(flags & O_NONBLOCK))
414 do
415 {
416 FD_ZERO (&readfds);
417 FD_SET (fdes, &readfds);
418 n = scm_internal_select (fdes + 1, &readfds, NULL, NULL, NULL);
419 }
420 while (n == -1 && errno == EINTR);
421 }
422 }
423 #endif
424
425 static void fport_flush (SCM port);
426
427 /* fill a port's read-buffer with a single read.
428 returns the first char and moves the read_pos pointer past it.
429 or returns EOF if end of file. */
430 static int
431 fport_fill_input (SCM port)
432 {
433 int count;
434 scm_port *pt = SCM_PTAB_ENTRY (port);
435 struct scm_fport *fp = SCM_FSTREAM (port);
436
437 #ifdef GUILE_ISELECT
438 fport_wait_for_input (port);
439 #endif
440 SCM_SYSCALL (count = read (fp->fdes, pt->read_buf, pt->read_buf_size));
441 if (count == -1)
442 scm_syserror ("fport_fill_input");
443 if (count == 0)
444 return EOF;
445 else
446 {
447 pt->read_pos = pt->read_buf;
448 pt->read_end = pt->read_buf + count;
449 return *pt->read_buf;
450 }
451 }
452
453 static off_t
454 fport_seek (SCM port, off_t offset, int whence)
455 {
456 scm_port *pt = SCM_PTAB_ENTRY (port);
457 struct scm_fport *fp = SCM_FSTREAM (port);
458 off_t rv;
459 off_t result;
460
461 if (pt->rw_active == SCM_PORT_WRITE)
462 {
463 if (offset != 0 || whence != SEEK_CUR)
464 {
465 fport_flush (port);
466 result = rv = lseek (fp->fdes, offset, whence);
467 }
468 else
469 {
470 /* read current position without disturbing the buffer. */
471 rv = lseek (fp->fdes, offset, whence);
472 result = rv + (pt->write_pos - pt->write_buf);
473 }
474 }
475 else if (pt->rw_active == SCM_PORT_READ)
476 {
477 if (offset != 0 || whence != SEEK_CUR)
478 {
479 /* could expand to avoid a second seek. */
480 scm_end_input (port);
481 result = rv = lseek (fp->fdes, offset, whence);
482 }
483 else
484 {
485 /* read current position without disturbing the buffer
486 (particularly the unread-char buffer). */
487 rv = lseek (fp->fdes, offset, whence);
488 result = rv - (pt->read_end - pt->read_pos);
489
490 if (pt->read_buf == pt->putback_buf)
491 result -= pt->saved_read_end - pt->saved_read_pos;
492 }
493 }
494 else /* SCM_PORT_NEITHER */
495 {
496 result = rv = lseek (fp->fdes, offset, whence);
497 }
498
499 if (rv == -1)
500 scm_syserror ("fport_seek");
501
502 return result;
503 }
504
505 static void
506 fport_truncate (SCM port, off_t length)
507 {
508 struct scm_fport *fp = SCM_FSTREAM (port);
509
510 if (ftruncate (fp->fdes, length) == -1)
511 scm_syserror ("ftruncate");
512 }
513
514 static void
515 fport_write (SCM port, void *data, size_t size)
516 {
517 scm_port *pt = SCM_PTAB_ENTRY (port);
518
519 if (pt->write_buf == &pt->shortbuf)
520 {
521 /* "unbuffered" port. */
522 int fdes = SCM_FSTREAM (port)->fdes;
523
524 if (write (fdes, data, size) == -1)
525 scm_syserror ("fport_write");
526 }
527 else
528 {
529 const char *input = (char *) data;
530 size_t remaining = size;
531
532 while (remaining > 0)
533 {
534 int space = pt->write_end - pt->write_pos;
535 int write_len = (remaining > space) ? space : remaining;
536
537 memcpy (pt->write_pos, input, write_len);
538 pt->write_pos += write_len;
539 remaining -= write_len;
540 input += write_len;
541 if (write_len == space)
542 fport_flush (port);
543 }
544
545 /* handle line buffering. */
546 if ((SCM_CAR (port) & SCM_BUFLINE) && memchr (data, '\n', size))
547 fport_flush (port);
548 }
549 }
550
551 /* becomes 1 when process is exiting: normal exception handling won't
552 work by this time. */
553 extern int terminating;
554
555 static void
556 fport_flush (SCM port)
557 {
558 scm_port *pt = SCM_PTAB_ENTRY (port);
559 struct scm_fport *fp = SCM_FSTREAM (port);
560 char *ptr = pt->write_buf;
561 int init_size = pt->write_pos - pt->write_buf;
562 int remaining = init_size;
563
564 while (remaining > 0)
565 {
566 int count;
567
568 SCM_SYSCALL (count = write (fp->fdes, ptr, remaining));
569 if (count < 0)
570 {
571 /* error. assume nothing was written this call, but
572 fix up the buffer for any previous successful writes. */
573 int done = init_size - remaining;
574
575 if (done > 0)
576 {
577 int i;
578
579 for (i = 0; i < remaining; i++)
580 {
581 *(pt->write_buf + i) = *(pt->write_buf + done + i);
582 }
583 pt->write_pos = pt->write_buf + remaining;
584 }
585 if (!terminating)
586 scm_syserror ("fport_flush");
587 else
588 {
589 const char *msg = "Error: could not flush file-descriptor ";
590 char buf[11];
591
592 write (2, msg, strlen (msg));
593 sprintf (buf, "%d\n", fp->fdes);
594 write (2, buf, strlen (buf));
595
596 count = remaining;
597 }
598 }
599 ptr += count;
600 remaining -= count;
601 }
602 pt->write_pos = pt->write_buf;
603 pt->rw_active = SCM_PORT_NEITHER;
604 }
605
606 /* clear the read buffer and adjust the file position for unread bytes. */
607 static void
608 fport_end_input (SCM port, int offset)
609 {
610 struct scm_fport *fp = SCM_FSTREAM (port);
611 scm_port *pt = SCM_PTAB_ENTRY (port);
612
613 offset += pt->read_end - pt->read_pos;
614
615 if (offset > 0)
616 {
617 pt->read_pos = pt->read_end;
618 /* will throw error if unread-char used at beginning of file
619 then attempting to write. seems correct. */
620 if (lseek (fp->fdes, -offset, SEEK_CUR) == -1)
621 scm_syserror ("fport_end_input");
622 }
623 pt->rw_active = SCM_PORT_NEITHER;
624 }
625
626 static int
627 fport_close (SCM port)
628 {
629 struct scm_fport *fp = SCM_FSTREAM (port);
630 scm_port *pt = SCM_PTAB_ENTRY (port);
631 int rv;
632
633 fport_flush (port);
634 SCM_SYSCALL (rv = close (fp->fdes));
635 if (rv == -1 && errno != EBADF)
636 scm_syserror ("fport_close");
637 if (pt->read_buf == pt->putback_buf)
638 pt->read_buf = pt->saved_read_buf;
639 if (pt->read_buf != &pt->shortbuf)
640 free (pt->read_buf);
641 if (pt->write_buf != &pt->shortbuf)
642 free (pt->write_buf);
643 free ((char *) fp);
644 return rv;
645 }
646
647 static scm_sizet
648 fport_free (SCM port)
649 {
650 fport_close (port);
651 return 0;
652 }
653
654 void scm_make_fptob (void); /* Called from ports.c */
655
656 void
657 scm_make_fptob ()
658 {
659 long tc = scm_make_port_type ("file", fport_fill_input, fport_write);
660 scm_set_port_free (tc, fport_free);
661 scm_set_port_print (tc, prinfport);
662 scm_set_port_flush (tc, fport_flush);
663 scm_set_port_end_input (tc, fport_end_input);
664 scm_set_port_close (tc, fport_close);
665 scm_set_port_seek (tc, fport_seek);
666 scm_set_port_truncate (tc, fport_truncate);
667 scm_set_port_input_waiting (tc, fport_input_waiting);
668 }
669
670 void
671 scm_init_fports ()
672 {
673 #include "fports.x"
674 scm_sysintern ("_IOFBF", SCM_MAKINUM (_IOFBF));
675 scm_sysintern ("_IOLBF", SCM_MAKINUM (_IOLBF));
676 scm_sysintern ("_IONBF", SCM_MAKINUM (_IONBF));
677 }