1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-06-10 14:00:21 +02:00

Do magic to mix reads and writes on stdio FILE-based ports.

* fports.c (FPORT_READ_SAFE, FPORT_WRITE_SAFE, FPORT_ALL_OKAY):
New macros.
(pre_read, pre_write): New functions.
(local_fgetc, local_fgets, local_ffwrite, local_fputc,
local_fputs): Call them.
(local_fflush): Mark the port as ready for reading and writing.
(scm_stdio_to_port): Set the FPORT_READ_SAFE, FPORT_WRITE_SAFE
flags on new port objects.  This might not be accurate --- who
knows what state the FILE * is in when we get it --- but it won't
do extraneous calls to fflush or fseek, so it's no worse than the
behavior before this change.
* ports.h: Add comment.
Centralize the creation of port objects based on stdio FILE * in
fports.c; don't just throw them together anywhere.
* fports.c (scm_stdio_to_port): Make NAME a SCM value, which is
what the rest of Guile wants.  Don't set the revealed count;
that's only appropriate for stdin, stdout, stderr.
(scm_standard_stream_to_port): This function does set the revealed
count.
* init.c (scm_init_standard_ports): Use	scm_standard_stream_to_port,
not scm_stdio_to_port.
* filesys.c (scm_open): Call scm_stdio_to_port; don't write it out.
* fports.c (scm_open_file): Same.
* posix.c (scm_pipe): Same.
* socket.c (scm_sock_fd_to_port): Same.
* ioext.c (scm_fdopen): Same.
(scm_freopen): Moved from here to...
* fports.c (scm_freopen): ... here.  This is really something that
munges the internals of an fport, so it should go here.
* fports.h (scm_stdio_to_port): Adjust prototype.
(scm_standard_stream_to_port, scm_freopen): New protoypes.
* ioext.h (scm_freopen): Prototype removed.
This commit is contained in:
Jim Blandy 1998-10-09 12:45:20 +00:00
parent 5621be9997
commit e145dd0277

View file

@ -55,9 +55,85 @@
scm_sizet fwrite ();
#endif
/* {Ports - file ports}
*
*/
/* Port direction --- handling el cheapo stdio implementations.
Guile says that when you've got a port that's both readable and
writable, like a socket, why then, by gum, you can read from it and
write to it! However, most standard I/O implementations make
cheezy caveats like this:
When a file is opened for update, both input and output may
be done on the resulting stream. However, output may not be
directly followed by input without an intervening fflush(),
fseek(), fsetpos(), or rewind(), and input may not be
directly followed by output without an intervening fseek(),
fsetpos(), or rewind(), or an input operation that
encounters end-of-file.
-- the Solaris fdopen(3S) man page
I think this behavior is permitted by the ANSI C standard.
So we made the implementation more complex, so what the user sees
remains simple. When we have a Guile port based on a stdio stream
(this source file's specialty), we keep track of whether it was
last written to, read from, or whether it is in a safe state for
both operations. Each port operation function just checks the
state of the port before each operation, and does the required
magic if necessary.
We use two bits in the CAR of the port, FPORT_READ_SAFE and
FPORT_WRITE_SAFE, to indicate what operations the underlying stdio
stream could correctly perform next. You're not allowed to clear
them both at the same time, but both can be set --- for example, if
the stream has just been opened, or flushed, or had its position
changed.
It's possible for a port to have neither bit set, if we receive a
FILE * pointer in an unknown state; this code should handle that
gracefully. */
#define FPORT_READ_SAFE (1L << 24)
#define FPORT_WRITE_SAFE (2L << 24)
#define FPORT_ALL_OKAY(port) \
(SCM_SETOR_CAR (port, (FPORT_READ_SAFE | FPORT_WRITE_SAFE)))
static inline void
pre_read (SCM port)
{
if (! (SCM_CAR (port) & FPORT_READ_SAFE))
fflush ((FILE *)SCM_STREAM (port));
/* We've done the flush, so reading is safe.
Assuming that we're going to do a read next, writing will not be
safe by the time we're done. */
SCM_SETOR_CAR (port, FPORT_READ_SAFE);
SCM_SETAND_CAR (port, ~FPORT_WRITE_SAFE);
}
static inline void
pre_write (SCM port)
{
if (! (SCM_CAR (port) & FPORT_WRITE_SAFE))
/* This can fail, if we're talking to a line-buffered terminal. As
far as I can tell, there's no way to get mixed reads and writes
to work on a line-buffered terminal at all --- you get a full
line in the buffer when you read, and then you have to throw it
out to write. You have to do unbuffered input, and make the
system provide the second buffer. */
fseek ((FILE *)SCM_STREAM (port), 0, SEEK_CUR);
/* We've done the seek, so writing is safe.
Assuming that we're going to do a write next, reading will not be
safe by the time we're done. */
SCM_SETOR_CAR (port, FPORT_WRITE_SAFE);
SCM_SETAND_CAR (port, ~FPORT_READ_SAFE);
}
/* Helpful operations on stdio FILE-based ports */
/* should be called with SCM_DEFER_INTS active */
@ -178,7 +254,6 @@ scm_open_file (filename, modes)
file = SCM_ROCHARS (filename);
mode = SCM_ROCHARS (modes);
SCM_NEWCELL (port);
SCM_DEFER_INTS;
SCM_SYSCALL (f = fopen (file, mode));
if (!f)
@ -192,35 +267,64 @@ scm_open_file (filename, modes)
en);
}
else
{
struct scm_port_table * pt;
port = scm_stdio_to_port (f, mode, filename);
SCM_ALLOW_INTS;
return port;
}
pt = scm_add_to_port_table (port);
SCM_SETPTAB_ENTRY (port, pt);
SCM_SETCAR (port, scm_tc16_fport | scm_mode_bits (mode));
SCM_SETSTREAM (port, (SCM) f);
SCM_PROC (s_freopen, "freopen", 3, 0, 0, scm_freopen);
SCM
scm_freopen (filename, modes, port)
SCM filename;
SCM modes;
SCM port;
{
FILE *f;
SCM_ASSERT (SCM_NIMP (filename) && SCM_ROSTRINGP (filename), filename,
SCM_ARG1, s_freopen);
SCM_ASSERT (SCM_NIMP (modes) && SCM_ROSTRINGP (modes), modes, SCM_ARG2,
s_freopen);
SCM_COERCE_SUBSTR (filename);
SCM_COERCE_SUBSTR (modes);
port = SCM_COERCE_OUTPORT (port);
SCM_DEFER_INTS;
SCM_ASSERT (SCM_NIMP (port) && SCM_FPORTP (port), port, SCM_ARG3, s_freopen);
SCM_SYSCALL (f = freopen (SCM_ROCHARS (filename), SCM_ROCHARS (modes),
(FILE *)SCM_STREAM (port)));
if (!f)
{
SCM p;
p = port;
port = SCM_MAKINUM (errno);
SCM_SETAND_CAR (p, ~SCM_OPN);
scm_remove_from_port_table (p);
}
else
{
SCM_SETSTREAM (port, (SCM)f);
SCM_SETCAR (port, (scm_tc16_fport
| scm_mode_bits (SCM_ROCHARS (modes))
| FPORT_READ_SAFE | FPORT_WRITE_SAFE));
if (SCM_BUF0 & SCM_CAR (port))
scm_setbuf0 (port);
SCM_PTAB_ENTRY (port)->file_name = filename;
}
SCM_ALLOW_INTS;
return port;
}
/* Building Guile ports from stdio FILE pointers. */
/* Build a Scheme port from an open stdio port, FILE.
MODE indicates whether FILE is open for reading or writing; it uses
the same notation as open-file's second argument.
If NAME is non-zero, use it as the port's filename.
scm_stdio_to_port sets the revealed count for FILE's file
descriptor to 1, so that FILE won't be closed when the port object
is GC'd. */
Use NAME as the port's filename. */
SCM
scm_stdio_to_port (file, mode, name)
FILE *file;
char *mode;
char *name;
scm_stdio_to_port (FILE *file, char *mode, SCM name)
{
long mode_bits = scm_mode_bits (mode);
SCM port;
@ -231,18 +335,34 @@ scm_stdio_to_port (file, mode, name)
{
pt = scm_add_to_port_table (port);
SCM_SETPTAB_ENTRY (port, pt);
SCM_SETCAR (port, (scm_tc16_fport | mode_bits));
SCM_SETCAR (port, (scm_tc16_fport
| mode_bits
| FPORT_READ_SAFE | FPORT_WRITE_SAFE));
SCM_SETSTREAM (port, (SCM) file);
if (SCM_BUF0 & SCM_CAR (port))
scm_setbuf0 (port);
SCM_PTAB_ENTRY (port)->file_name = scm_makfrom0str (name);
SCM_PTAB_ENTRY (port)->file_name = name;
}
SCM_ALLOW_INTS;
return port;
}
/* Like scm_stdio_to_port, except that:
- NAME is a standard C string, not a Guile string
- we set the revealed count for FILE's file descriptor to 1, so
that FILE won't be closed when the port object is GC'd. */
SCM
scm_standard_stream_to_port (FILE *file, char *mode, char *name)
{
SCM port = scm_stdio_to_port (file, mode, scm_makfrom0str (name));
scm_set_port_revealed_x (port, SCM_MAKINUM (1));
return port;
}
/* The fport and pipe port scm_ptobfuns functions --- reading and writing */
static int prinfport SCM_P ((SCM exp, SCM port, scm_print_state *pstate));
@ -277,6 +397,7 @@ static int
local_fgetc (SCM port)
{
FILE *s = (FILE *) SCM_STREAM (port);
pre_read (port);
if (feof (s))
return EOF;
else
@ -293,6 +414,8 @@ local_fgets (SCM port, int *len)
char *p; /* pointer to current buffer position */
int limit = 80; /* current size of buffer */
pre_read (port);
f = (FILE *) SCM_STREAM (port);
if (feof (f))
return NULL;
@ -365,11 +488,6 @@ pwrite (ptr, size, nitems, port)
#define ffwrite fwrite
#endif
/* This otherwise pointless code helps some poor
* crippled C compilers cope with life.
*/
static int
local_fclose (SCM port)
{
@ -383,6 +501,7 @@ local_fflush (SCM port)
{
FILE *fp = (FILE *) SCM_STREAM (port);
return fflush (fp);
FPORT_ALL_OKAY (port);
}
static int
@ -390,6 +509,7 @@ local_fputc (int c, SCM port)
{
FILE *fp = (FILE *) SCM_STREAM (port);
pre_write (port);
return fputc (c, fp);
}
@ -397,6 +517,7 @@ static int
local_fputs (char *s, SCM port)
{
FILE *fp = (FILE *) SCM_STREAM (port);
pre_write (port);
return fputs (s, fp);
}
@ -407,6 +528,7 @@ local_ffwrite (char *ptr,
SCM port)
{
FILE *fp = (FILE *) SCM_STREAM (port);
pre_write (port);
return ffwrite (ptr, size, nitems, fp);
}
@ -426,6 +548,8 @@ local_pclose (SCM port)
}
/* The file and pipe port scm_ptobfuns structures themselves. */
scm_ptobfuns scm_fptob =
{
0,