removed bogus files
This commit is contained in:
parent
aa8647061d
commit
56234c5ae2
|
@ -1,533 +0,0 @@
|
||||||
/* This file contains the code for doing the scsh file ports stuff.
|
|
||||||
** Copyright (c) 1993, 1994 by Olin Shivers.
|
|
||||||
**
|
|
||||||
** Note that this code mutates Scheme records -- it has the layout
|
|
||||||
** of fdports and their data records wired in. This is somewhat fragile.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* A note on the clearerr() calls herein: SunOS stdio input routines,
|
|
||||||
** contrary to POSIX, will return EOF if the stream's EOF flag is set,
|
|
||||||
** without trying to read the stream. This is a lose for tty's, which
|
|
||||||
** can frequently still be read from after the first EOF (e.g., if you
|
|
||||||
** type a ^D to bag out of a breakpoint, you would like the terminal
|
|
||||||
** input port to not shut down forever.)
|
|
||||||
**
|
|
||||||
** To fix this lossage, we are careful to call clearerr() before every
|
|
||||||
** input stream op.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* We maintain the following invariant: every open port has a FILE*
|
|
||||||
** associated with it.
|
|
||||||
*/
|
|
||||||
#include "sysdep.h"
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include "cstuff.h"
|
|
||||||
#define NUM_FDPORTS 256
|
|
||||||
#include "fdports.h"
|
|
||||||
#include "machine/stdio_dep.h"
|
|
||||||
|
|
||||||
/* Make sure our exports match up w/the implementation: */
|
|
||||||
#include "fdports1.h"
|
|
||||||
|
|
||||||
extern int errno;
|
|
||||||
|
|
||||||
/* Maps fd's to FILE*'s. */
|
|
||||||
static FILE *fstar_cache[NUM_FDPORTS] = {NULL};
|
|
||||||
|
|
||||||
/* Maps fd's to ports. */
|
|
||||||
static s48_value fdports[NUM_FDPORTS] = {SCHFALSE};
|
|
||||||
|
|
||||||
void init_fdports(void)
|
|
||||||
{
|
|
||||||
int i = NUM_FDPORTS;
|
|
||||||
|
|
||||||
#ifdef linux
|
|
||||||
/* Gross hack to cover for Linux lossage. -Olin */
|
|
||||||
extern void remove_bone_from_head_of_linux_libc (void);
|
|
||||||
remove_bone_from_head_of_linux_libc();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
while( i-- ) fdports[i] = S48_FALSE;
|
|
||||||
|
|
||||||
/* Specially hack stdio. */
|
|
||||||
fstar_cache[fileno(stdin)] = stdin;
|
|
||||||
fstar_cache[fileno(stdout)] = stdout;
|
|
||||||
fstar_cache[fileno(stderr)] = stderr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* (maybe_fdes2port fd)
|
|
||||||
** Return: the port if there is one allocated; otherwise #f.
|
|
||||||
** If a port is returned, the revealed count is NOT incremented.
|
|
||||||
*/
|
|
||||||
s48_value maybe_fdes2port(int fd)
|
|
||||||
{
|
|
||||||
if( fd < 0 || fd >= NUM_FDPORTS )
|
|
||||||
return S48_FALSE;
|
|
||||||
|
|
||||||
return fdports[fd];
|
|
||||||
}
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
/* Bogus old code. We now compute the mode string from the actual fd. */
|
|
||||||
static char const *mode2string(int mode)
|
|
||||||
{
|
|
||||||
if( mode == 0 ) return "r";
|
|
||||||
else if( mode == 1 ) return "w";
|
|
||||||
else if( mode == 2 ) return "r+";
|
|
||||||
else return "x"; /* What??? */
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static char const *fdes_modestr(int fd)
|
|
||||||
{
|
|
||||||
int flags = fcntl(fd,F_GETFL);
|
|
||||||
|
|
||||||
if( flags == -1 ) return NULL;
|
|
||||||
flags &= O_ACCMODE;
|
|
||||||
|
|
||||||
if( flags == O_WRONLY ) return "w";
|
|
||||||
else if( flags == O_RDONLY ) return "r";
|
|
||||||
else if( flags == O_RDWR ) return "r+";
|
|
||||||
|
|
||||||
fputs("That's impossible.\n", stderr);
|
|
||||||
abort();
|
|
||||||
_exit(-1);
|
|
||||||
/*NOTREACHED*/
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns a char, #f for EOF, or errno. */
|
|
||||||
s48_value fdport_getchar(s48_value data)
|
|
||||||
{
|
|
||||||
int fd = s48_extract_fixnum(*PortData_Fd(data));
|
|
||||||
FILE *f = fstar_cache[fd];
|
|
||||||
int c;
|
|
||||||
|
|
||||||
clearerr(f);
|
|
||||||
c = getc(f);
|
|
||||||
|
|
||||||
if( EOF == c )
|
|
||||||
return ferror(f) ? s48_enter_fixnum(errno) : S48_FALSE;
|
|
||||||
else
|
|
||||||
return s48_enter_char(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
int fdport_putchar(s48_value data, char c)
|
|
||||||
{
|
|
||||||
int fd = s48_extract_fixnum(*PortData_Fd(data));
|
|
||||||
FILE *f = fstar_cache[fd];
|
|
||||||
int retval = putc(c,f);
|
|
||||||
return (retval == EOF) ? errno : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Not POSIX, so we punt to an OS-specific routine. */
|
|
||||||
s48_value fdport_char_readyp(s48_value data)
|
|
||||||
{
|
|
||||||
extern s48_value stream_char_readyp(FILE *);
|
|
||||||
return stream_char_readyp(fstar_cache[s48_extract_fixnum(*PortData_Fd(data))]);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int flush_fdport(s48_value data)
|
|
||||||
{
|
|
||||||
FILE *f = fstar_cache[s48_extract_fixnum(*PortData_Fd(data))];
|
|
||||||
return fflush(f) ? errno : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* This is actually just fflush(NULL), but apparently the pinheads
|
|
||||||
** at Sun can't be bothered to implement ANSI C or POSIX, so this op
|
|
||||||
** cleverly dumps core. Hence we do this incomplete approximation.
|
|
||||||
*/
|
|
||||||
|
|
||||||
int flush_all_ports(void)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
for(i=0; i<NUM_FDPORTS; i++)
|
|
||||||
if(fstar_cache[i]) fflush(fstar_cache[i]);
|
|
||||||
return 0;
|
|
||||||
/* return fflush(NULL) ? errno : 0; THE REAL SOLUTION.*/
|
|
||||||
}
|
|
||||||
|
|
||||||
int seek_fdport(s48_value data, off_t offset, int whence, int *newpos)
|
|
||||||
{
|
|
||||||
FILE *f = fstar_cache[s48_extract_fixnum(*PortData_Fd(data))];
|
|
||||||
*PortData_Peek(data) = S48_FALSE; /* Flush buffered data. */
|
|
||||||
|
|
||||||
if( fseek(f, offset, whence) ) /* seek */
|
|
||||||
{ *newpos = 0; return errno; }
|
|
||||||
|
|
||||||
*newpos = ftell(f); /* tell */
|
|
||||||
return (*newpos < 0) ? errno : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int tell_fdport( s48_value data, int *newpos )
|
|
||||||
{
|
|
||||||
FILE *f = fstar_cache[s48_extract_fixnum(*PortData_Fd(data))];
|
|
||||||
*newpos = ftell(f);
|
|
||||||
return (*newpos < 0) ? errno : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int set_fdbuf( s48_value data, int policy, int bufsize )
|
|
||||||
{
|
|
||||||
FILE *f = fstar_cache[s48_extract_fixnum(*PortData_Fd(data))];
|
|
||||||
int size = (bufsize < 0) ? BUFSIZ : bufsize;
|
|
||||||
return setvbuf(f, NULL, policy, size) ? errno : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int close_fdport(s48_value port_data)
|
|
||||||
{
|
|
||||||
if( *PortData_Closed(port_data) == S48_FALSE ) {
|
|
||||||
int fd = s48_extract_fixnum(*PortData_Fd(port_data));
|
|
||||||
FILE *f = fstar_cache[fd];
|
|
||||||
|
|
||||||
if( fclose(f) ) return errno;
|
|
||||||
|
|
||||||
*PortData_Fd(port_data) = S48_FALSE;
|
|
||||||
fdports[fd] = S48_FALSE;
|
|
||||||
*PortData_Closed(port_data) = S48_TRUE;
|
|
||||||
*PortData_Peek(port_data) = S48_FALSE;
|
|
||||||
fstar_cache[fd] = NULL;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
else return EBADF; /* Already closed. */
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int cloexec_fdport(s48_value port_data)
|
|
||||||
{
|
|
||||||
int fd = s48_extract_fixnum(*PortData_Fd(port_data));
|
|
||||||
|
|
||||||
return fcntl(fd, F_SETFD, FD_CLOEXEC) ? errno : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int install_port(int fd, s48_value port, int revealed)
|
|
||||||
{
|
|
||||||
FILE *stream;
|
|
||||||
const char *modestr;
|
|
||||||
|
|
||||||
if( fd < 0 || fd >= NUM_FDPORTS ) return -1;
|
|
||||||
if( fdports[fd] != S48_FALSE ) return -2;
|
|
||||||
if( !(modestr = fdes_modestr(fd)) )
|
|
||||||
return -3;
|
|
||||||
|
|
||||||
fdports[fd] = port;
|
|
||||||
|
|
||||||
if( !revealed )
|
|
||||||
if( set_cloexec(fd, 1) ) return errno;
|
|
||||||
|
|
||||||
if( fstar_cache[fd] ) return 0; /* A hack mainly for stdio. */
|
|
||||||
|
|
||||||
fstar_cache[fd] = stream = fdopen(fd, modestr);
|
|
||||||
return stream ? 0 : errno;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
FILE *fdes2fstar(int fd)
|
|
||||||
{
|
|
||||||
if( fstar_cache[fd] ) return fstar_cache[fd];
|
|
||||||
else {
|
|
||||||
const char *modestr = fdes_modestr(fd);
|
|
||||||
return modestr ? fdopen(fd, modestr) : NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* fd_from's FILE* structure is changed to be fd_to's FILE* structure.
|
|
||||||
** So buffered data isn't lost. Return 0 on failure.
|
|
||||||
** Rather non-portable.
|
|
||||||
*/
|
|
||||||
static int move_fstar(int fd_from, int fd_to)
|
|
||||||
{
|
|
||||||
FILE *f1 = fdes2fstar(fd_from);
|
|
||||||
if( !f1 ) return 0;
|
|
||||||
setfileno(f1, fd_to);
|
|
||||||
fstar_cache[fd_from] = NULL;
|
|
||||||
fstar_cache[fd_to] = f1;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Move port so that it's underlying file descriptor is fd.
|
|
||||||
** The port's underlying FILE* is also shifted over, so that
|
|
||||||
** buffered data isn't lost on a shift. Return 0 on success.
|
|
||||||
*/
|
|
||||||
int move_fdport(int fd, s48_value port, int new_revealed)
|
|
||||||
{
|
|
||||||
s48_value port_data = *Port_PortData(port);
|
|
||||||
int old_fd = s48_extract_fixnum(*PortData_Fd(port_data));
|
|
||||||
|
|
||||||
if( fd < 0 || fd >= NUM_FDPORTS ) return 1;
|
|
||||||
|
|
||||||
/* You are allowed to "move" a port to its current fd.
|
|
||||||
Otherwise, the fd must be unallocated. Kluge. */
|
|
||||||
|
|
||||||
if( fdports[fd] != port ) {
|
|
||||||
if( fdports[fd] != S48_FALSE ) return 1; /* Target already allocated. */
|
|
||||||
|
|
||||||
if( !move_fstar(old_fd, fd) ) return 1;
|
|
||||||
|
|
||||||
fdports[fd] = port;
|
|
||||||
fdports[old_fd] = S48_FALSE;
|
|
||||||
*PortData_Fd(port_data) = s48_enter_fixnum(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Unreveal the port by shifting the revealed count
|
|
||||||
over to the old-revealed count. */
|
|
||||||
*PortData_OldRev(port_data) = s48_enter_fixnum(s48_extract_fixnum(*PortData_OldRev(port_data))+
|
|
||||||
s48_extract_fixnum(*PortData_Rev(port_data)));
|
|
||||||
*PortData_Rev(port_data) = s48_enter_fixnum(new_revealed);
|
|
||||||
if( !new_revealed ) return set_cloexec(fd, 1);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Scan the fdports vector after a gc. Uncopied unrevealed ports
|
|
||||||
** have their fds closed. The fdports vec is updated with the copy.
|
|
||||||
*/
|
|
||||||
#ifdef TEST_GC
|
|
||||||
void post_gc_fdports(void) {}
|
|
||||||
#else
|
|
||||||
void post_gc_fdports(void)
|
|
||||||
{
|
|
||||||
int fd;
|
|
||||||
|
|
||||||
#ifdef NOISY_FDGC
|
|
||||||
fputs("{GC", stderr); fflush(stderr);
|
|
||||||
#endif
|
|
||||||
for(fd=0; fd<NUM_FDPORTS; fd++) {
|
|
||||||
|
|
||||||
s48_value port = fdports[fd];
|
|
||||||
if(STOBP(port)) {
|
|
||||||
long header = STOB_HEADER(port);
|
|
||||||
if(STOBP(header)) {
|
|
||||||
#ifdef NOISY_FDGC
|
|
||||||
fprintf(stderr, "Copying port[fd] %d[%d] header %d\n",
|
|
||||||
port, fd, header);
|
|
||||||
fflush(stderr);
|
|
||||||
#endif
|
|
||||||
/* Port was copied, so update fdports entry. */
|
|
||||||
fdports[fd] = header;
|
|
||||||
}
|
|
||||||
|
|
||||||
else {
|
|
||||||
/* Port wasn't copied -- is garbage.
|
|
||||||
If fd unrevealed, close it. */
|
|
||||||
int rev = s48_extract_fixnum(*PortRev(port));
|
|
||||||
#ifdef NOISY_FDGC
|
|
||||||
fprintf(stderr, "GC'ing %srevealed port[fd] %d[%d]\n",
|
|
||||||
rev == 0 ? "un" : "",
|
|
||||||
port, fd);
|
|
||||||
fflush(stderr);
|
|
||||||
#endif
|
|
||||||
if( rev == 0 )
|
|
||||||
/* Close, even if interrupted -- GC's must be atomic. */
|
|
||||||
while( EINTR == close_fdport(*Port_PortData(port)) );
|
|
||||||
|
|
||||||
fdports[fd] = S48_FALSE; /* Drop the port. */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#ifdef NOISY_FDGC
|
|
||||||
fputs("}", stderr); fflush(stderr);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define Min(a,b) (((a) < (b)) ? (a) : (b)) /* Not a function. */
|
|
||||||
|
|
||||||
/* Note the clearerr() call. This is so a ^D on a tty input stream
|
|
||||||
** doesn't shut the stream down forever. SunOS doesn't handle this according
|
|
||||||
** to POSIX spec, so we have to explicitly hack this case.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int read_stream_substring(s48_value buf, int start, int end, FILE *f)
|
|
||||||
{
|
|
||||||
char *p = StrByte(buf,start);
|
|
||||||
int len = end-start;
|
|
||||||
|
|
||||||
clearerr(f);
|
|
||||||
|
|
||||||
/* If there's data in the buffer, use it. */
|
|
||||||
|
|
||||||
if (fbufcount(f) > 0)
|
|
||||||
return fread(p, 1, Min(len, fbufcount(f)), f);
|
|
||||||
|
|
||||||
/* Otherwise, do a read. */
|
|
||||||
return read(fileno(f), p, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define MIN(a,b) (((a) < (b)) ? (a) : (b)) /* Not a function. */
|
|
||||||
|
|
||||||
int read_fdport_substring(s48_value buf, int start, int end, s48_value data)
|
|
||||||
{
|
|
||||||
s48_value peek = *PortData_Peek(data);
|
|
||||||
FILE *f = fstar_cache[s48_extract_fixnum(*PortData_Fd(data))];
|
|
||||||
|
|
||||||
clearerr(f); /* SunOS sux. */
|
|
||||||
|
|
||||||
/* If there's a peek char, then we'll use it and
|
|
||||||
whatever is buffered in the FILE*. */
|
|
||||||
|
|
||||||
if( IsChar(peek) ) {
|
|
||||||
int len = end-start;
|
|
||||||
if( len > 0 ) {
|
|
||||||
char *p = StrByte(buf,start);
|
|
||||||
*p++ = s48_extract_char(peek);
|
|
||||||
*PortData_Peek(data) = S48_FALSE;
|
|
||||||
return 1 + fread(p, 1, MIN(len-1, fbufcount(f)), f);
|
|
||||||
}
|
|
||||||
else return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Otherwise, just do a read_stream_substring. */
|
|
||||||
return read_stream_substring(buf, start, end, f);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* We assume either fileno(f) does blocking i/o or f is unbuffered. */
|
|
||||||
|
|
||||||
int write_fdport_substring(s48_value buf, int start, int end, s48_value data)
|
|
||||||
{
|
|
||||||
FILE *f = fstar_cache[s48_extract_fixnum(*PortData_Fd(data))];
|
|
||||||
int nbytes = end - start;
|
|
||||||
int retval = fwrite(StrByte(buf,start), 1, nbytes, f);
|
|
||||||
return (retval < nbytes) ? -1 : retval; /* -1: error, otw numwritten */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 1st return value says why we terminated the read:
|
|
||||||
** - integer errno if error.
|
|
||||||
** - char c if string terminated by char c (which is consumed).
|
|
||||||
** - eof object if string terminated by EOF
|
|
||||||
** - #f if buf overflow.
|
|
||||||
**
|
|
||||||
** 2nd return value is num chars read into BUF.
|
|
||||||
**
|
|
||||||
** GOBBLE boolean says whether or not to read a terminating delimiter char
|
|
||||||
** or just leave it in the input stream.
|
|
||||||
**
|
|
||||||
** N.B.:
|
|
||||||
** - This code is dependent on the representation of scsh's char-sets.
|
|
||||||
** We assume they are 256-elt strings. If this changes, we'd have to
|
|
||||||
** rewrite the code.
|
|
||||||
** - This procedure is made more complex by the fact that we may have to take
|
|
||||||
** the first char read from the *port's* peek-char slot.
|
|
||||||
** - buffer-full/terminated-char ties are broken in favor of successful
|
|
||||||
** termination.
|
|
||||||
*/
|
|
||||||
|
|
||||||
s48_value read_delim(const char *delims, char *buf, int gobble,
|
|
||||||
s48_value port, int start, int end,
|
|
||||||
int *nread)
|
|
||||||
{
|
|
||||||
|
|
||||||
s48_value data = *Port_PortData(port);
|
|
||||||
s48_value peekc = *PortData_Peek(data);
|
|
||||||
int fd = s48_extract_fixnum(*PortData_Fd(data));
|
|
||||||
FILE *f = fstar_cache[fd];
|
|
||||||
char *cptr = buf+start-1, /* Location of last char deposited. */
|
|
||||||
*bufend = buf+end-1; /* Last writeable position. */
|
|
||||||
|
|
||||||
/* This chunk of code is necessary because we have to check
|
|
||||||
** the port's one-char pushback slot before going to the port's
|
|
||||||
** stdio FILE*. Yech.
|
|
||||||
*/
|
|
||||||
if( IsChar(peekc) ) {
|
|
||||||
char c = s48_extract_char(peekc);
|
|
||||||
if( delims[c] ) { /* Is c in cset? */
|
|
||||||
if( gobble ) *PortData_Peek(data) = S48_FALSE;
|
|
||||||
*nread = 0;
|
|
||||||
return peekc;
|
|
||||||
}
|
|
||||||
else if( start >= end ) {
|
|
||||||
*nread = 0; /* Overflow. */
|
|
||||||
return S48_FALSE;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
*++cptr = c;
|
|
||||||
*PortData_Peek(data) = S48_FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
clearerr(f);
|
|
||||||
|
|
||||||
while( 1 ) {
|
|
||||||
int c = getc(f);
|
|
||||||
|
|
||||||
if( EOF == c ) { /* Terminal case: EOF or error. */
|
|
||||||
*nread = 1 + cptr - buf - start;
|
|
||||||
return ferror(f) ? s48_enter_fixnum(errno) : S48_EOF;
|
|
||||||
}
|
|
||||||
|
|
||||||
else if( delims[c] ) { /* Terminal case: delimiter char. */
|
|
||||||
s48_value ch = s48_enter_char(c);
|
|
||||||
*nread = 1 + cptr - buf - start;
|
|
||||||
if( !gobble ) *PortData_Peek(data) = ch;
|
|
||||||
return ch;
|
|
||||||
}
|
|
||||||
|
|
||||||
else if( cptr >= bufend ) { /* Terminal case: buffer overflow. */
|
|
||||||
*PortData_Peek(data) = s48_enter_char(c); /* Put C back. */
|
|
||||||
*nread = end-start;
|
|
||||||
return S48_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
else *++cptr = c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
s48_value skip_chars(const char *skipchars, s48_value port, int *nread)
|
|
||||||
{
|
|
||||||
|
|
||||||
s48_value data = *Port_PortData(port);
|
|
||||||
s48_value peekc = *PortData_Peek(data);
|
|
||||||
int fd = s48_extract_fixnum(*PortData_Fd(data));
|
|
||||||
FILE *f = fstar_cache[fd];
|
|
||||||
int nr = 0; /* Number of skip chars read. */
|
|
||||||
|
|
||||||
/* This chunk of code is necessary because we have to check
|
|
||||||
** the port's one-char pushback slot before going to the port's
|
|
||||||
** stdio FILE*. Yech.
|
|
||||||
*/
|
|
||||||
if( IsChar(peekc) ) {
|
|
||||||
int c = s48_extract_char(peekc);
|
|
||||||
if( skipchars[c] ) { /* Is c in cset? */
|
|
||||||
*PortData_Peek(data) = S48_FALSE;
|
|
||||||
nr = 1;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
*nread = 0;
|
|
||||||
return S48_FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
clearerr(f);
|
|
||||||
|
|
||||||
while( 1 ) {
|
|
||||||
int c = getc(f);
|
|
||||||
|
|
||||||
if( EOF == c ) { /* Terminal case: EOF or error. */
|
|
||||||
*nread = nr;
|
|
||||||
return ferror(f) ? s48_enter_fixnum(errno) : S48_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
else if( !skipchars[c] ) { /* Terminal case: non-skip char. */
|
|
||||||
*PortData_Peek(data) = s48_enter_char(c);
|
|
||||||
*nread = nr;
|
|
||||||
return S48_FALSE;
|
|
||||||
}
|
|
||||||
nr++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Why, you might ask, do I push back the final char using the Scheme port
|
|
||||||
** instead of using ungetc()? Because ungetc() isn't guaranteed to work for
|
|
||||||
** unbuffered ports. This is regrettable, because it causes C stdio clients
|
|
||||||
** that are ignorant of the Scheme port machinery to miss this char. No
|
|
||||||
** real help for it.
|
|
||||||
*/
|
|
|
@ -1,572 +0,0 @@
|
||||||
;;; A Unix file port system to completely replace S48 file ports.
|
|
||||||
;;; We use S48 extensible ports.
|
|
||||||
;;; Copyright (c) 1993 by Olin Shivers.
|
|
||||||
|
|
||||||
;(define-record fdport-data
|
|
||||||
; fd ; Unix file descriptor - integer.
|
|
||||||
; (closed? #f) ; Is port closed.
|
|
||||||
; (peek-char #f)
|
|
||||||
; revealed ; REVEALED & OLD-REVEALED are for keeping
|
|
||||||
; (old-revealed 0)) ; track of whether the FD value has escaped.
|
|
||||||
|
|
||||||
(define-record fdport-data
|
|
||||||
channel
|
|
||||||
revealed)
|
|
||||||
|
|
||||||
;;; We could flush the PEEK-CHAR field and use stdio ungetc(), but it
|
|
||||||
;;; is only guaranteed for buffered streams. Too bad...
|
|
||||||
|
|
||||||
;Arbitrary, for now.
|
|
||||||
(define buffer-size 255)
|
|
||||||
|
|
||||||
;(define (alloc-input-fdport fd revealed)
|
|
||||||
; (make-port input-fdport-handler open-input-port-status (make-lock) #f
|
|
||||||
; (make-fdport-data fd revealed)
|
|
||||||
; (make-code-vector buffer-size (char->ascii #\!)) 0
|
|
||||||
; 0 #f))
|
|
||||||
|
|
||||||
(define (alloc-input-fdport fd revealed)
|
|
||||||
(make-input-port input-fdport-handler
|
|
||||||
(make-fdport-data (make-input-fdchannel fd) revealed)
|
|
||||||
(make-code-vector buffer-size 0) 0 0))
|
|
||||||
|
|
||||||
;Have to use make-port, make-output-port wants to use code-vectors
|
|
||||||
;(define (alloc-output-fdport fd revealed)
|
|
||||||
; (make-port output-fdport-handler open-output-port-status (make-lock) #f
|
|
||||||
; (make-fdport-data fd revealed) (make-string buffer-size) 0
|
|
||||||
; buffer-size #f))
|
|
||||||
|
|
||||||
(define (alloc-output-fdport fd revealed)
|
|
||||||
(make-output-port output-fdport-handler
|
|
||||||
(make-fdport-data (make-output-fdchannel fd) revealed)
|
|
||||||
(make-code-vector buffer-size 0) 0 buffer-size))
|
|
||||||
|
|
||||||
;(define (make-input-fdport fd revealed)
|
|
||||||
; (let ((p (alloc-input-fdport fd revealed)))
|
|
||||||
; (%install-port fd p revealed)
|
|
||||||
; (add-finalizer! p (lambda (x) (close-fdport* (fdport-data x))))
|
|
||||||
; p))
|
|
||||||
|
|
||||||
(define make-input-fdport alloc-input-fdport)
|
|
||||||
|
|
||||||
;(define (make-output-fdport fd revealed)
|
|
||||||
; (let ((p (alloc-output-fdport fd revealed)))
|
|
||||||
; (%install-port fd p revealed)
|
|
||||||
; (periodically-force-output! p)
|
|
||||||
; (add-finalizer! p (lambda (x) (close-fdport* (fdport-data x))))
|
|
||||||
; p))
|
|
||||||
|
|
||||||
(define (make-output-fdport fd revealed)
|
|
||||||
(let ((p (alloc-output-fdport fd revealed)))
|
|
||||||
(periodically-force-output! p)
|
|
||||||
p))
|
|
||||||
|
|
||||||
;(define (fdport? x)
|
|
||||||
; (cond ((or (and (extensible-input-port? x)
|
|
||||||
; (extensible-input-port-local-data x))
|
|
||||||
; (and (extensible-output-port? x)
|
|
||||||
; (extensible-output-port-local-data x)))
|
|
||||||
; => (lambda (d) (fdport-data? d)))
|
|
||||||
; (else #f)))
|
|
||||||
|
|
||||||
(define (fdport? x)
|
|
||||||
(cond ((or (and (input-port? x) (port-data x))
|
|
||||||
(and (output-port? x) (port-data x)))
|
|
||||||
=> (lambda (d) (fdport-data? d)))
|
|
||||||
(else #f)))
|
|
||||||
|
|
||||||
|
|
||||||
;;; Basic methods
|
|
||||||
;;; -------------
|
|
||||||
|
|
||||||
(define fdport-null-method (lambda (x) x #f))
|
|
||||||
|
|
||||||
;;; CLOSE-FDPORT*, FLUSH-FDPORT* defined in syscalls.scm.
|
|
||||||
;;; (So you must load that file before loading this file.)
|
|
||||||
|
|
||||||
;(define (fdport*-read-char data)
|
|
||||||
; (check-arg open-fdport-data? data fdport*-read-char)
|
|
||||||
; (cond ((fdport-data:peek-char data) =>
|
|
||||||
; (lambda (char)
|
|
||||||
; (set-fdport-data:peek-char data #f)
|
|
||||||
; char))
|
|
||||||
; (else
|
|
||||||
; (or (%fdport*-read-char data) eof-object))))
|
|
||||||
|
|
||||||
(define (fdport*-read-char data)
|
|
||||||
(check-arg open-fdport-data? data fdport*-read-char)
|
|
||||||
(or (%fdport*-read-char data) (eof-object)))
|
|
||||||
|
|
||||||
;(define (fdport*-peek-char data)
|
|
||||||
; (check-arg open-fdport-data? data fdport*-peek-char)
|
|
||||||
; (or (fdport-data:peek-char data)
|
|
||||||
; (cond ((%fdport*-read-char data) =>
|
|
||||||
; (lambda (char)
|
|
||||||
; (set-fdport-data:peek-char data char)
|
|
||||||
; char))
|
|
||||||
; (else eof-object))))
|
|
||||||
|
|
||||||
;(define (fdport*-char-ready? data)
|
|
||||||
; (check-arg open-fdport-data? data fdport*-char-ready?)
|
|
||||||
; (or (fdport-data:peek-char data)
|
|
||||||
; (%fdport*-char-ready? data)))
|
|
||||||
|
|
||||||
(define (fdport*-char-ready? data)
|
|
||||||
(check-arg open-fdport-data? data fdport*-char-ready?)
|
|
||||||
(%fdport*-char-ready? data))
|
|
||||||
|
|
||||||
(define (fdport*-write-char data char)
|
|
||||||
(check-arg open-fdport-data? data fdport*-write-char)
|
|
||||||
(if (not (fdport-data:closed? data))
|
|
||||||
(%fdport*-write-char data char))
|
|
||||||
#f) ; Bogus fix -- otherwise %fdport*-...'s 0-value return blows up S48.
|
|
||||||
|
|
||||||
(define (fdport*-write-string data string)
|
|
||||||
(check-arg open-fdport-data? data fdport*-write-string)
|
|
||||||
(generic-write-string string 0 (string-length string) ; from rw.scm
|
|
||||||
write-fdport*-substring/errno data)
|
|
||||||
#f)
|
|
||||||
|
|
||||||
;(define input-fdport-methods
|
|
||||||
; (make-input-port-methods close-fdport*
|
|
||||||
; fdport*-read-char
|
|
||||||
; fdport*-peek-char
|
|
||||||
; fdport*-char-ready?
|
|
||||||
; fdport-null-method ; current-column
|
|
||||||
; fdport-null-method)) ; current-row
|
|
||||||
|
|
||||||
(define input-fdport-reader
|
|
||||||
(lambda (fdport buffer start-index needed-bytes)
|
|
||||||
(let* ((buffer-length (if (string? buffer) string-length
|
|
||||||
code-vector-length))
|
|
||||||
(buffer-set! (if (string? buffer) string-set! code-vector-set!))
|
|
||||||
(max-size (buffer-length buffer)))
|
|
||||||
(if (number? needed-bytes)
|
|
||||||
(let ((max-read (modulo (+ start-index needed-bytes) max-size)))
|
|
||||||
(let fill-buffer ((current-index start-index))
|
|
||||||
(if (eq? current-index max-read)
|
|
||||||
needed-bytes
|
|
||||||
(if (fdport*-char-ready? fdport)
|
|
||||||
(let ((found-char (fdport*-read-char fdport)))
|
|
||||||
(if (eof-object? found-char)
|
|
||||||
found-char
|
|
||||||
(begin
|
|
||||||
(buffer-set! buffer current-index
|
|
||||||
(char->ascii found-char))
|
|
||||||
(fill-buffer (modulo (+ current-index 1)
|
|
||||||
max-size)))))
|
|
||||||
(begin
|
|
||||||
(relinquish-timeslice)
|
|
||||||
(fill-buffer current-index))))))
|
|
||||||
(let ((immediate-get
|
|
||||||
(lambda ()
|
|
||||||
(let fill-buffer ((current-index start-index))
|
|
||||||
(if (and (< current-index max-size)
|
|
||||||
(fdport*-char-ready? fdport))
|
|
||||||
(begin
|
|
||||||
(let ((found-char (fdport*-read-char fdport)))
|
|
||||||
(if (not (eof-object? found-char))
|
|
||||||
(begin
|
|
||||||
(buffer-set! buffer current-index
|
|
||||||
(char->ascii found-char))
|
|
||||||
(fill-buffer (+ current-index 1)))
|
|
||||||
found-char)))
|
|
||||||
(- current-index start-index))))))
|
|
||||||
(cond
|
|
||||||
((eq? needed-bytes 'immediate) (immediate-get))
|
|
||||||
((eq? needed-bytes 'any)
|
|
||||||
(let fill-buffer ()
|
|
||||||
(if (fdport*-char-ready? fdport)
|
|
||||||
(immediate-get)
|
|
||||||
(begin
|
|
||||||
(relinquish-timeslice)
|
|
||||||
(fill-buffer)))))))))))
|
|
||||||
|
|
||||||
(define null-func (lambda args #t))
|
|
||||||
|
|
||||||
(define input-fdport-handler
|
|
||||||
(make-port-handler
|
|
||||||
(lambda (fdport)
|
|
||||||
(list 'fdport (fdport-data:fd fdport)))
|
|
||||||
close-fdport*
|
|
||||||
input-fdport-reader))
|
|
||||||
|
|
||||||
;(define output-fdport-methods
|
|
||||||
; (make-output-port-methods close-fdport*
|
|
||||||
; fdport*-write-char
|
|
||||||
; fdport*-write-string
|
|
||||||
; (lambda (d) ; force output
|
|
||||||
; (flush-fdport* d)
|
|
||||||
; #f) ; bogus workaround.
|
|
||||||
; fdport-null-method ; fresh-line
|
|
||||||
; fdport-null-method ; current-column
|
|
||||||
; fdport-null-method)) ; current-row
|
|
||||||
|
|
||||||
(define output-fdport-writer
|
|
||||||
(lambda (fdport buffer start-index needed-bytes)
|
|
||||||
(let* ((buffer-length (string-length buffer))
|
|
||||||
(to-print
|
|
||||||
(if (> (+ start-index needed-bytes) buffer-length)
|
|
||||||
(string-append (substring buffer start-index buffer-length)
|
|
||||||
(substring buffer 0
|
|
||||||
(- (+ start-index needed-bytes)
|
|
||||||
buffer-length)))
|
|
||||||
(substring buffer start-index (+ start-index needed-bytes)))))
|
|
||||||
(fdport*-write-string fdport to-print))))
|
|
||||||
|
|
||||||
(define output-fdport-handler
|
|
||||||
(make-port-handler
|
|
||||||
(lambda (fdport)
|
|
||||||
(list 'fdport (fdport-data:fd fdport)))
|
|
||||||
close-fdport*
|
|
||||||
output-fdport-writer))
|
|
||||||
|
|
||||||
(define unbuffered-output-fdport-handler
|
|
||||||
(make-port-handler
|
|
||||||
(lambda (fdport)
|
|
||||||
(list 'fdport (fdport-data:fd fdport)))
|
|
||||||
close-fdport*
|
|
||||||
fdport*-write-char))
|
|
||||||
|
|
||||||
;(define (fdport-data port)
|
|
||||||
; (let ((d ((cond ((extensible-input-port? port)
|
|
||||||
; extensible-input-port-local-data)
|
|
||||||
; ((extensible-output-port? port)
|
|
||||||
; extensible-output-port-local-data)
|
|
||||||
; (else (error "Illegal value" port)))
|
|
||||||
; port)))
|
|
||||||
; (if (and d (fdport-data? d)) d
|
|
||||||
; (error "fport closed" port))))
|
|
||||||
|
|
||||||
(define fdport-data port-data)
|
|
||||||
; That was easy.
|
|
||||||
|
|
||||||
(define (%fdport-seek/errno port offset whence)
|
|
||||||
(%fdport*-seek/errno (fdport-data port) offset whence))
|
|
||||||
|
|
||||||
(define (%fdport-tell/errno port)
|
|
||||||
(%fdport*-tell/errno (fdport-data port)))
|
|
||||||
|
|
||||||
(define (%fdport-set-buffering/errno port policy size)
|
|
||||||
(%fdport*-set-buffering/errno (fdport-data port) policy size))
|
|
||||||
|
|
||||||
(define (set-port-buffering port policy . maybe-size)
|
|
||||||
(let* ((size (if (pair? maybe-size)
|
|
||||||
(if (pair? (cdr maybe-size))
|
|
||||||
(error "Too many arguments." set-port-buffering)
|
|
||||||
(check-arg (lambda (s) (and (integer? s)
|
|
||||||
(<= 0 s)))
|
|
||||||
(car maybe-size)
|
|
||||||
set-port-buffering))
|
|
||||||
-1))
|
|
||||||
(policy (if (zero? size) bufpol/none policy))
|
|
||||||
(err (%fdport-set-buffering/errno port policy size)))
|
|
||||||
(if err (errno-error err set-port-buffering port policy size))))
|
|
||||||
|
|
||||||
|
|
||||||
;;; Open & Close
|
|
||||||
;;; ------------
|
|
||||||
|
|
||||||
(define (open-file fname flags . maybe-mode)
|
|
||||||
(let ((fd (apply open-fdes fname flags maybe-mode))
|
|
||||||
(access (bitwise-and flags open/access-mask)))
|
|
||||||
((if (or (= access open/read) (= access open/read+write))
|
|
||||||
make-input-fdport
|
|
||||||
make-output-fdport)
|
|
||||||
fd 0)))
|
|
||||||
|
|
||||||
(define (open-input-file fname . maybe-flags)
|
|
||||||
(let ((flags (:optional maybe-flags 0)))
|
|
||||||
(open-file fname (deposit-bit-field flags open/access-mask open/read))))
|
|
||||||
|
|
||||||
(define (open-output-file fname . rest)
|
|
||||||
(let* ((flags (if (pair? rest) (car rest)
|
|
||||||
(bitwise-ior open/create open/truncate))) ; default
|
|
||||||
(maybe-mode (if (null? rest) '() (cdr rest)))
|
|
||||||
(flags (deposit-bit-field flags open/access-mask open/write)))
|
|
||||||
(apply open-file fname flags maybe-mode)))
|
|
||||||
|
|
||||||
|
|
||||||
;;; All these revealed-count-hacking procs have atomicity problems.
|
|
||||||
;;; They need to run uninterrupted.
|
|
||||||
|
|
||||||
(define (increment-revealed-count port delta)
|
|
||||||
(let* ((data (fdport-data port))
|
|
||||||
(count (fdport-data:revealed data))
|
|
||||||
(newcount (+ count delta)))
|
|
||||||
(set-fdport-data:revealed data newcount)
|
|
||||||
(if (and (zero? count) (> newcount 0)) ; We just became revealed,
|
|
||||||
(%set-cloexec (fdport-data:fd data) #f)))) ; so don't close on exec().
|
|
||||||
|
|
||||||
(define (release-port-handle port)
|
|
||||||
(check-arg fdport? port port->fdes)
|
|
||||||
(let* ((data (fdport-data port))
|
|
||||||
(rev (fdport-data:revealed data)))
|
|
||||||
(if (zero? rev)
|
|
||||||
(set-fdport-data:old-revealed data
|
|
||||||
(- (fdport-data:old-revealed data) 1))
|
|
||||||
(let ((new-rev (- rev 1)))
|
|
||||||
(set-fdport-data:revealed data new-rev)
|
|
||||||
(if (zero? new-rev) ; We just became unrevealed, so
|
|
||||||
(%set-cloexec (fdport-data:fd data) #t)))))); the fd can be closed on exec.
|
|
||||||
|
|
||||||
(define (port-revealed port)
|
|
||||||
(let ((count (fdport-data:revealed
|
|
||||||
(fdport-data
|
|
||||||
(check-arg fdport? port port-revealed)))))
|
|
||||||
(and (not (zero? count)) count)))
|
|
||||||
|
|
||||||
(define (fdes->port fd port-maker) ; local proc.
|
|
||||||
(cond ((%maybe-fdes->port fd) =>
|
|
||||||
(lambda (p)
|
|
||||||
(increment-revealed-count p 1)
|
|
||||||
p))
|
|
||||||
(else (port-maker fd 1))))
|
|
||||||
|
|
||||||
(define (fdes->inport fd) (fdes->port fd make-input-fdport))
|
|
||||||
(define (fdes->outport fd) (fdes->port fd make-output-fdport))
|
|
||||||
|
|
||||||
(define (port->fdes port)
|
|
||||||
(check-arg open-fdport? port port->fdes)
|
|
||||||
(let ((data (fdport-data port)))
|
|
||||||
(increment-revealed-count port 1)
|
|
||||||
(fdport-data:fd data)))
|
|
||||||
|
|
||||||
(define (call/fdes fd/port proc)
|
|
||||||
(cond ((integer? fd/port)
|
|
||||||
(proc fd/port))
|
|
||||||
|
|
||||||
((fdport? fd/port)
|
|
||||||
(let ((port fd/port))
|
|
||||||
(dynamic-wind
|
|
||||||
(lambda ()
|
|
||||||
(if (not port) (error "Can't throw back into call/fdes.")))
|
|
||||||
(lambda () (proc (port->fdes port)))
|
|
||||||
(lambda ()
|
|
||||||
(release-port-handle port)
|
|
||||||
(set! port #f)))))
|
|
||||||
|
|
||||||
(else (error "Not a file descriptor or fdport." fd/port))))
|
|
||||||
|
|
||||||
;;; Don't mess with the revealed count in the port case
|
|
||||||
;;; -- just sneakily grab the fdes and run.
|
|
||||||
|
|
||||||
(define (sleazy-call/fdes fd/port proc)
|
|
||||||
(proc (cond ((integer? fd/port) fd/port)
|
|
||||||
((fdport? fd/port) (fdport-data:fd (fdport-data fd/port)))
|
|
||||||
(else (error "Not a file descriptor or fdport." fd/port)))))
|
|
||||||
|
|
||||||
|
|
||||||
;;; Random predicates and arg checkers
|
|
||||||
;;; ----------------------------------
|
|
||||||
|
|
||||||
(define (open-fdport-data? x)
|
|
||||||
(and (fdport-data? x)
|
|
||||||
(not (fdport-data:closed? x))))
|
|
||||||
|
|
||||||
;(define (open-fdport? x)
|
|
||||||
; (cond ((or (and (extensible-input-port? x)
|
|
||||||
; (extensible-input-port-local-data x))
|
|
||||||
; (and (extensible-output-port? x)
|
|
||||||
; (extensible-output-port-local-data x)))
|
|
||||||
; => (lambda (d) (and (fdport-data? d) (not (fdport-data:closed? d)))))
|
|
||||||
; (else #f)))
|
|
||||||
|
|
||||||
(define (open-fdport? x)
|
|
||||||
(and (fdport? x) (or (open-output-port? x) (open-input-port? x))))
|
|
||||||
|
|
||||||
;(define (extensible-port-local-data xport)
|
|
||||||
; ((if (extensible-input-port? xport)
|
|
||||||
; extensible-input-port-local-data
|
|
||||||
; extensible-output-port-local-data)
|
|
||||||
; xport))
|
|
||||||
|
|
||||||
(define (fdport-open? port)
|
|
||||||
(check-arg fdport? port fdport-open?)
|
|
||||||
(not (fdport-data:closed? (port-data port))))
|
|
||||||
|
|
||||||
|
|
||||||
;;; Initialise the system
|
|
||||||
;;; ---------------------
|
|
||||||
|
|
||||||
(define old-inport #f) ; Just because.
|
|
||||||
(define old-outport #f)
|
|
||||||
(define old-errport #f)
|
|
||||||
|
|
||||||
(define (init-fdports!)
|
|
||||||
(%init-fdports!)
|
|
||||||
(if (not (fdport? (current-input-port)))
|
|
||||||
(set! old-inport (current-input-port)))
|
|
||||||
(if (not (fdport? (current-output-port)))
|
|
||||||
(set! old-outport (current-output-port)))
|
|
||||||
(if (not (fdport? (current-error-port)))
|
|
||||||
(set! old-errport (current-error-port)))
|
|
||||||
(let ((iport (fdes->inport 0))
|
|
||||||
(oport (fdes->outport 1)))
|
|
||||||
(set-port-buffering iport bufpol/none) ; Stdin is unbuffered.
|
|
||||||
(set-port-buffering oport bufpol/none)
|
|
||||||
(set-fluid! $current-input-port iport)
|
|
||||||
(set-fluid! $current-output-port oport)
|
|
||||||
(set-fluid! $current-error-port (fdes->outport 2))))
|
|
||||||
|
|
||||||
|
|
||||||
;;; Generic port operations
|
|
||||||
;;; -----------------------
|
|
||||||
|
|
||||||
;;; (close-after port f)
|
|
||||||
;;; Apply F to PORT. When F returns, close PORT, then return F's result.
|
|
||||||
;;; Does nothing special if you throw out or throw in.
|
|
||||||
|
|
||||||
(define (close-after port f)
|
|
||||||
(receive vals (f port)
|
|
||||||
(close port)
|
|
||||||
(apply values vals)))
|
|
||||||
|
|
||||||
(define (close port/fd)
|
|
||||||
((cond ((integer? port/fd) close-fdes)
|
|
||||||
((output-port? port/fd) close-output-port)
|
|
||||||
((input-port? port/fd) close-input-port)
|
|
||||||
(else (error "Not file-descriptor or port" port/fd))) port/fd))
|
|
||||||
|
|
||||||
;;; If this fd has an associated input or output port,
|
|
||||||
;;; move it to a new fd, freeing this one up.
|
|
||||||
|
|
||||||
(define (evict-ports fd)
|
|
||||||
(cond ((%maybe-fdes->port fd) => ; Shouldn't bump the revealed count.
|
|
||||||
(lambda (port)
|
|
||||||
(%move-fdport (%dup fd) port 0)))))
|
|
||||||
|
|
||||||
(define (close-fdes fd)
|
|
||||||
(evict-ports fd)
|
|
||||||
(%close-fdes fd))
|
|
||||||
|
|
||||||
|
|
||||||
;;; Extend R4RS i/o ops to handle file descriptors.
|
|
||||||
;;; -----------------------------------------------
|
|
||||||
|
|
||||||
(define s48-char-ready? (structure-ref scheme char-ready?))
|
|
||||||
(define s48-read-char (structure-ref scheme read-char))
|
|
||||||
|
|
||||||
(define-simple-syntax
|
|
||||||
(define-r4rs-input (name arg ...) stream s48name body ...)
|
|
||||||
(define (name arg ... . maybe-i/o)
|
|
||||||
(let ((stream (:optional maybe-i/o (current-input-port))))
|
|
||||||
(cond ((input-port? stream) (s48name arg ... stream))
|
|
||||||
((integer? stream) body ...)
|
|
||||||
(else (error "Not a port or file descriptor" stream))))))
|
|
||||||
|
|
||||||
(define-r4rs-input (char-ready?) input s48-char-ready?
|
|
||||||
(%char-ready-fdes? input))
|
|
||||||
|
|
||||||
(define-r4rs-input (read-char) input s48-read-char
|
|
||||||
(read-fdes-char input))
|
|
||||||
|
|
||||||
;structure refs changed to get reference from scheme -dalbertz
|
|
||||||
(define s48-display (structure-ref scheme display))
|
|
||||||
(define s48-newline (structure-ref scheme newline))
|
|
||||||
(define s48-write (structure-ref scheme write))
|
|
||||||
(define s48-write-char (structure-ref scheme write-char))
|
|
||||||
(define s48-format (structure-ref formats format))
|
|
||||||
(define s48-force-output (structure-ref i/o force-output))
|
|
||||||
|
|
||||||
(define-simple-syntax
|
|
||||||
(define-r4rs-output (name arg ...) stream s48name body ...)
|
|
||||||
(define (name arg ... . maybe-i/o)
|
|
||||||
(let ((stream (:optional maybe-i/o (current-output-port))))
|
|
||||||
(cond ((output-port? stream) (s48name arg ... stream))
|
|
||||||
((integer? stream) body ...)
|
|
||||||
(else (error "Not a port or file descriptor" stream))))))
|
|
||||||
|
|
||||||
;;; This one depends upon S48's string ports.
|
|
||||||
(define-r4rs-output (display object) output s48-display
|
|
||||||
(let ((sp (make-string-output-port)))
|
|
||||||
(display object sp)
|
|
||||||
(write-string (string-output-port-output sp) output)))
|
|
||||||
|
|
||||||
(define-r4rs-output (newline) output s48-newline
|
|
||||||
(write-fdes-char #\newline output))
|
|
||||||
|
|
||||||
(define-r4rs-output (write object) output s48-write
|
|
||||||
(let ((sp (make-string-output-port)))
|
|
||||||
(write object sp)
|
|
||||||
(write-string (string-output-port-output sp) output)))
|
|
||||||
|
|
||||||
(define-r4rs-output (write-char char) output s48-write-char
|
|
||||||
(write-fdes-char char output))
|
|
||||||
|
|
||||||
;;; S48's force-output doesn't default to forcing (current-output-port).
|
|
||||||
(define-r4rs-output (force-output) output s48-force-output
|
|
||||||
(values)) ; Do nothing if applied to a file descriptor.
|
|
||||||
|
|
||||||
|
|
||||||
(define (format dest cstring . args)
|
|
||||||
(if (integer? dest)
|
|
||||||
(write-string (apply s48-format #f cstring args) dest)
|
|
||||||
(apply s48-format dest cstring args)))
|
|
||||||
|
|
||||||
;;; with-current-foo-port procs
|
|
||||||
;;; ---------------------------
|
|
||||||
|
|
||||||
(define (with-current-input-port* port thunk)
|
|
||||||
(let-fluid $current-input-port port thunk))
|
|
||||||
|
|
||||||
(define (with-current-output-port* port thunk)
|
|
||||||
(let-fluid $current-output-port port thunk))
|
|
||||||
|
|
||||||
(define (with-current-error-port* port thunk)
|
|
||||||
(let-fluid $current-error-port port thunk))
|
|
||||||
|
|
||||||
(define-simple-syntax (with-current-input-port port body ...)
|
|
||||||
(with-current-input-port* port (lambda () body ...)))
|
|
||||||
|
|
||||||
(define-simple-syntax (with-current-output-port port body ...)
|
|
||||||
(with-current-output-port* port (lambda () body ...)))
|
|
||||||
|
|
||||||
(define-simple-syntax (with-current-error-port port body ...)
|
|
||||||
(with-current-error-port* port (lambda () body ...)))
|
|
||||||
|
|
||||||
|
|
||||||
;;; set-foo-port! procs
|
|
||||||
;;; -------------------
|
|
||||||
;;; Side-effecting variants of with-current-input-port* and friends.
|
|
||||||
|
|
||||||
(define (set-current-input-port! port) (set-fluid! $current-input-port port))
|
|
||||||
(define (set-current-output-port! port) (set-fluid! $current-output-port port))
|
|
||||||
(define (set-current-error-port! port) (set-fluid! $current-error-port port))
|
|
||||||
|
|
||||||
|
|
||||||
;;; call-with-foo-file with-foo-to-file
|
|
||||||
;;; -----------------------------------
|
|
||||||
|
|
||||||
;;; Copied straight from rts/port.scm, but re-defined in this module,
|
|
||||||
;;; closed over my versions of open-input-file and open-output-file.
|
|
||||||
|
|
||||||
(define (call-with-mumble-file open close)
|
|
||||||
(lambda (string proc)
|
|
||||||
(let ((port #f))
|
|
||||||
(dynamic-wind (lambda ()
|
|
||||||
(if port
|
|
||||||
(warn "throwing back into a call-with-...put-file"
|
|
||||||
string)
|
|
||||||
(set! port (open string))))
|
|
||||||
(lambda () (proc port))
|
|
||||||
(lambda ()
|
|
||||||
(if port
|
|
||||||
(close port)))))))
|
|
||||||
|
|
||||||
(define call-with-input-file
|
|
||||||
(call-with-mumble-file open-input-file close-input-port))
|
|
||||||
|
|
||||||
(define call-with-output-file
|
|
||||||
(call-with-mumble-file open-output-file close-output-port))
|
|
||||||
|
|
||||||
(define (with-input-from-file string thunk)
|
|
||||||
(call-with-input-file string
|
|
||||||
(lambda (port)
|
|
||||||
(let-fluid $current-input-port port thunk))))
|
|
||||||
|
|
||||||
(define (with-output-to-file string thunk)
|
|
||||||
(call-with-output-file string
|
|
||||||
(lambda (port)
|
|
||||||
(let-fluid $current-output-port port thunk))))
|
|
||||||
|
|
|
@ -1,437 +0,0 @@
|
||||||
;;; A Unix file port system to completely replace S48 file ports.
|
|
||||||
;;; We use S48 extensible ports.
|
|
||||||
;;; Copyright (c) 1993 by Olin Shivers.
|
|
||||||
|
|
||||||
(define-record fdport-data
|
|
||||||
fd ; Unix file descriptor - integer.
|
|
||||||
(closed? #f) ; Is port closed.
|
|
||||||
(peek-char #f)
|
|
||||||
revealed ; REVEALED & OLD-REVEALED are for keeping
|
|
||||||
(old-revealed 0)) ; track of whether the FD value has escaped.
|
|
||||||
|
|
||||||
;;; We could flush the PEEK-CHAR field and use stdio ungetc(), but it
|
|
||||||
;;; is only guaranteed for buffered streams. Too bad...
|
|
||||||
|
|
||||||
(define (alloc-input-fdport fd revealed)
|
|
||||||
(make-extensible-input-port (make-fdport-data fd revealed)
|
|
||||||
input-fdport-methods))
|
|
||||||
|
|
||||||
(define (alloc-output-fdport fd revealed)
|
|
||||||
(make-extensible-output-port (make-fdport-data fd revealed)
|
|
||||||
output-fdport-methods))
|
|
||||||
|
|
||||||
(define (make-input-fdport fd revealed)
|
|
||||||
(let ((p (alloc-input-fdport fd revealed)))
|
|
||||||
(%install-port fd p revealed)
|
|
||||||
p))
|
|
||||||
|
|
||||||
(define (make-output-fdport fd revealed)
|
|
||||||
(let ((p (alloc-output-fdport fd revealed)))
|
|
||||||
(%install-port fd p revealed)
|
|
||||||
p))
|
|
||||||
|
|
||||||
|
|
||||||
(define (fdport? x)
|
|
||||||
(cond ((or (and (extensible-input-port? x)
|
|
||||||
(extensible-input-port-local-data x))
|
|
||||||
(and (extensible-output-port? x)
|
|
||||||
(extensible-output-port-local-data x)))
|
|
||||||
=> (lambda (d) (fdport-data? d)))
|
|
||||||
(else #f)))
|
|
||||||
|
|
||||||
;;; Basic methods
|
|
||||||
;;; -------------
|
|
||||||
|
|
||||||
(define fdport-null-method (lambda (x) x #f))
|
|
||||||
|
|
||||||
;;; CLOSE-FDPORT*, FLUSH-FDPORT* defined in syscalls.scm.
|
|
||||||
;;; (So you must load that file before loading this file.)
|
|
||||||
|
|
||||||
(define (fdport*-read-char data)
|
|
||||||
(check-arg open-fdport-data? data fdport*-read-char)
|
|
||||||
(cond ((fdport-data:peek-char data) =>
|
|
||||||
(lambda (char)
|
|
||||||
(set-fdport-data:peek-char data #f)
|
|
||||||
char))
|
|
||||||
(else
|
|
||||||
(or (%fdport*-read-char data) eof-object))))
|
|
||||||
|
|
||||||
(define (fdport*-peek-char data)
|
|
||||||
(check-arg open-fdport-data? data fdport*-peek-char)
|
|
||||||
(or (fdport-data:peek-char data)
|
|
||||||
(cond ((%fdport*-read-char data) =>
|
|
||||||
(lambda (char)
|
|
||||||
(set-fdport-data:peek-char data char)
|
|
||||||
char))
|
|
||||||
(else eof-object))))
|
|
||||||
|
|
||||||
(define (fdport*-char-ready? data)
|
|
||||||
(check-arg open-fdport-data? data fdport*-char-ready?)
|
|
||||||
(or (fdport-data:peek-char data)
|
|
||||||
(%fdport*-char-ready? data)))
|
|
||||||
|
|
||||||
(define (fdport*-write-char data char)
|
|
||||||
(check-arg open-fdport-data? data fdport*-write-char)
|
|
||||||
(if (not (fdport-data:closed? data))
|
|
||||||
(%fdport*-write-char data char))
|
|
||||||
#f) ; Bogus fix -- otherwise %fdport*-...'s 0-value return blows up S48.
|
|
||||||
|
|
||||||
(define (fdport*-write-string data string)
|
|
||||||
(check-arg open-fdport-data? data fdport*-write-string)
|
|
||||||
(generic-write-string string 0 (string-length string) ; from rw.scm
|
|
||||||
write-fdport*-substring/errno data)
|
|
||||||
#f)
|
|
||||||
|
|
||||||
(define input-fdport-methods
|
|
||||||
(make-input-port-methods close-fdport*
|
|
||||||
fdport*-read-char
|
|
||||||
fdport*-peek-char
|
|
||||||
fdport*-char-ready?
|
|
||||||
fdport-null-method ; current-column
|
|
||||||
fdport-null-method)) ; current-row
|
|
||||||
|
|
||||||
|
|
||||||
(define output-fdport-methods
|
|
||||||
(make-output-port-methods close-fdport*
|
|
||||||
fdport*-write-char
|
|
||||||
fdport*-write-string
|
|
||||||
(lambda (d) ; force output
|
|
||||||
(flush-fdport* d)
|
|
||||||
#f) ; bogus workaround.
|
|
||||||
fdport-null-method ; fresh-line
|
|
||||||
fdport-null-method ; current-column
|
|
||||||
fdport-null-method)) ; current-row
|
|
||||||
|
|
||||||
(define (fdport-data port)
|
|
||||||
(let ((d ((cond ((extensible-input-port? port)
|
|
||||||
extensible-input-port-local-data)
|
|
||||||
((extensible-output-port? port)
|
|
||||||
extensible-output-port-local-data)
|
|
||||||
(else (error "Illegal value" port)))
|
|
||||||
port)))
|
|
||||||
(if (and d (fdport-data? d)) d
|
|
||||||
(error "fport closed" port))))
|
|
||||||
|
|
||||||
(define (%fdport-seek/errno port offset whence)
|
|
||||||
(%fdport*-seek/errno (fdport-data port) offset whence))
|
|
||||||
|
|
||||||
(define (%fdport-tell/errno port)
|
|
||||||
(%fdport*-tell/errno (fdport-data port)))
|
|
||||||
|
|
||||||
(define (%fdport-set-buffering/errno port policy size)
|
|
||||||
(%fdport*-set-buffering/errno (fdport-data port) policy size))
|
|
||||||
|
|
||||||
(define (set-port-buffering port policy . maybe-size)
|
|
||||||
(let* ((size (if (pair? maybe-size)
|
|
||||||
(if (pair? (cdr maybe-size))
|
|
||||||
(error "Too many arguments." set-port-buffering)
|
|
||||||
(check-arg (lambda (s) (and (integer? s)
|
|
||||||
(<= 0 s)))
|
|
||||||
(car maybe-size)
|
|
||||||
set-port-buffering))
|
|
||||||
-1))
|
|
||||||
(policy (if (zero? size) bufpol/none policy))
|
|
||||||
(err (%fdport-set-buffering/errno port policy size)))
|
|
||||||
(if err (errno-error err set-port-buffering port policy size))))
|
|
||||||
|
|
||||||
|
|
||||||
;;; Open & Close
|
|
||||||
;;; ------------
|
|
||||||
|
|
||||||
(define (open-file fname flags . maybe-mode)
|
|
||||||
(let ((fd (apply open-fdes fname flags maybe-mode))
|
|
||||||
(access (bitwise-and flags open/access-mask)))
|
|
||||||
((if (or (= access open/read) (= access open/read+write))
|
|
||||||
make-input-fdport
|
|
||||||
make-output-fdport)
|
|
||||||
fd 0)))
|
|
||||||
|
|
||||||
(define (open-input-file fname . maybe-flags)
|
|
||||||
(let ((flags (:optional maybe-flags 0)))
|
|
||||||
(open-file fname (deposit-bit-field flags open/access-mask open/read))))
|
|
||||||
|
|
||||||
(define (open-output-file fname . rest)
|
|
||||||
(let* ((flags (if (pair? rest) (car rest)
|
|
||||||
(bitwise-ior open/create open/truncate))) ; default
|
|
||||||
(maybe-mode (if (null? rest) '() (cdr rest)))
|
|
||||||
(flags (deposit-bit-field flags open/access-mask open/write)))
|
|
||||||
(apply open-file fname flags maybe-mode)))
|
|
||||||
|
|
||||||
|
|
||||||
;;; All these revealed-count-hacking procs have atomicity problems.
|
|
||||||
;;; They need to run uninterrupted.
|
|
||||||
|
|
||||||
(define (increment-revealed-count port delta)
|
|
||||||
(let* ((data (extensible-port-local-data port))
|
|
||||||
(count (fdport-data:revealed data))
|
|
||||||
(newcount (+ count delta)))
|
|
||||||
(set-fdport-data:revealed data newcount)
|
|
||||||
(if (and (zero? count) (> newcount 0)) ; We just became revealed,
|
|
||||||
(%set-cloexec (fdport-data:fd data) #f)))) ; so don't close on exec().
|
|
||||||
|
|
||||||
(define (release-port-handle port)
|
|
||||||
(check-arg fdport? port port->fdes)
|
|
||||||
(let* ((data (extensible-port-local-data port))
|
|
||||||
(rev (fdport-data:revealed data)))
|
|
||||||
(if (zero? rev)
|
|
||||||
(set-fdport-data:old-revealed data
|
|
||||||
(- (fdport-data:old-revealed data) 1))
|
|
||||||
(let ((new-rev (- rev 1)))
|
|
||||||
(set-fdport-data:revealed data new-rev)
|
|
||||||
(if (zero? new-rev) ; We just became unrevealed, so
|
|
||||||
(%set-cloexec (fdport-data:fd data) #t)))))); the fd can be closed on exec.
|
|
||||||
|
|
||||||
(define (port-revealed port)
|
|
||||||
(let ((count (fdport-data:revealed
|
|
||||||
(extensible-port-local-data
|
|
||||||
(check-arg fdport? port port-revealed)))))
|
|
||||||
(and (not (zero? count)) count)))
|
|
||||||
|
|
||||||
(define (fdes->port fd port-maker) ; local proc.
|
|
||||||
(cond ((%maybe-fdes->port fd) =>
|
|
||||||
(lambda (p)
|
|
||||||
(increment-revealed-count p 1)
|
|
||||||
p))
|
|
||||||
(else (port-maker fd 1))))
|
|
||||||
|
|
||||||
(define (fdes->inport fd) (fdes->port fd make-input-fdport))
|
|
||||||
(define (fdes->outport fd) (fdes->port fd make-output-fdport))
|
|
||||||
|
|
||||||
(define (port->fdes port)
|
|
||||||
(check-arg open-fdport? port port->fdes)
|
|
||||||
(let ((data (extensible-port-local-data port)))
|
|
||||||
(increment-revealed-count port 1)
|
|
||||||
(fdport-data:fd data)))
|
|
||||||
|
|
||||||
(define (call/fdes fd/port proc)
|
|
||||||
(cond ((integer? fd/port)
|
|
||||||
(proc fd/port))
|
|
||||||
|
|
||||||
((fdport? fd/port)
|
|
||||||
(let ((port fd/port))
|
|
||||||
(dynamic-wind
|
|
||||||
(lambda ()
|
|
||||||
(if (not port) (error "Can't throw back into call/fdes.")))
|
|
||||||
(lambda () (proc (port->fdes port)))
|
|
||||||
(lambda ()
|
|
||||||
(release-port-handle port)
|
|
||||||
(set! port #f)))))
|
|
||||||
|
|
||||||
(else (error "Not a file descriptor or fdport." fd/port))))
|
|
||||||
|
|
||||||
;;; Don't mess with the revealed count in the port case
|
|
||||||
;;; -- just sneakily grab the fdes and run.
|
|
||||||
|
|
||||||
(define (sleazy-call/fdes fd/port proc)
|
|
||||||
(proc (cond ((integer? fd/port) fd/port)
|
|
||||||
((fdport? fd/port) (fdport-data:fd (fdport-data fd/port)))
|
|
||||||
(else (error "Not a file descriptor or fdport." fd/port)))))
|
|
||||||
|
|
||||||
|
|
||||||
;;; Random predicates and arg checkers
|
|
||||||
;;; ----------------------------------
|
|
||||||
|
|
||||||
(define (open-fdport-data? x)
|
|
||||||
(and (fdport-data? x)
|
|
||||||
(not (fdport-data:closed? x))))
|
|
||||||
|
|
||||||
(define (open-fdport? x)
|
|
||||||
(cond ((or (and (extensible-input-port? x)
|
|
||||||
(extensible-input-port-local-data x))
|
|
||||||
(and (extensible-output-port? x)
|
|
||||||
(extensible-output-port-local-data x)))
|
|
||||||
=> (lambda (d) (and (fdport-data? d) (not (fdport-data:closed? d)))))
|
|
||||||
(else #f)))
|
|
||||||
|
|
||||||
(define (extensible-port-local-data xport)
|
|
||||||
((if (extensible-input-port? xport)
|
|
||||||
extensible-input-port-local-data
|
|
||||||
extensible-output-port-local-data)
|
|
||||||
xport))
|
|
||||||
|
|
||||||
(define (fdport-open? port)
|
|
||||||
(check-arg fdport? port fdport-open?)
|
|
||||||
(not (fdport-data:closed? (extensible-port-local-data port))))
|
|
||||||
|
|
||||||
|
|
||||||
;;; Initialise the system
|
|
||||||
;;; ---------------------
|
|
||||||
|
|
||||||
(define old-inport #f) ; Just because.
|
|
||||||
(define old-outport #f)
|
|
||||||
(define old-errport #f)
|
|
||||||
|
|
||||||
(define (init-fdports!)
|
|
||||||
(%init-fdports!)
|
|
||||||
(if (not (fdport? (current-input-port)))
|
|
||||||
(set! old-inport (current-input-port)))
|
|
||||||
(if (not (fdport? (current-output-port)))
|
|
||||||
(set! old-outport (current-output-port)))
|
|
||||||
(if (not (fdport? (error-output-port)))
|
|
||||||
(set! old-errport (error-output-port)))
|
|
||||||
(let ((iport (fdes->inport 0)))
|
|
||||||
(set-port-buffering iport bufpol/none) ; Stdin is unbuffered.
|
|
||||||
(set-fluid! $current-input-port iport)
|
|
||||||
(set-fluid! $current-output-port (fdes->outport 1))
|
|
||||||
(set-fluid! $error-output-port (fdes->outport 2))))
|
|
||||||
|
|
||||||
|
|
||||||
;;; Generic port operations
|
|
||||||
;;; -----------------------
|
|
||||||
|
|
||||||
;;; (close-after port f)
|
|
||||||
;;; Apply F to PORT. When F returns, close PORT, then return F's result.
|
|
||||||
;;; Does nothing special if you throw out or throw in.
|
|
||||||
|
|
||||||
(define (close-after port f)
|
|
||||||
(receive vals (f port)
|
|
||||||
(close port)
|
|
||||||
(apply values vals)))
|
|
||||||
|
|
||||||
(define (close port/fd)
|
|
||||||
((cond ((integer? port/fd) close-fdes)
|
|
||||||
((output-port? port/fd) close-output-port)
|
|
||||||
((input-port? port/fd) close-input-port)
|
|
||||||
(else (error "Not file-descriptor or port" port/fd))) port/fd))
|
|
||||||
|
|
||||||
;;; If this fd has an associated input or output port,
|
|
||||||
;;; move it to a new fd, freeing this one up.
|
|
||||||
|
|
||||||
(define (evict-ports fd)
|
|
||||||
(cond ((%maybe-fdes->port fd) => ; Shouldn't bump the revealed count.
|
|
||||||
(lambda (port)
|
|
||||||
(%move-fdport (%dup fd) port 0)))))
|
|
||||||
|
|
||||||
(define (close-fdes fd)
|
|
||||||
(evict-ports fd)
|
|
||||||
(%close-fdes fd))
|
|
||||||
|
|
||||||
|
|
||||||
;;; Extend R4RS i/o ops to handle file descriptors.
|
|
||||||
;;; -----------------------------------------------
|
|
||||||
|
|
||||||
(define s48-char-ready? (structure-ref scheme char-ready?))
|
|
||||||
(define s48-read-char (structure-ref scheme read-char))
|
|
||||||
|
|
||||||
(define-simple-syntax
|
|
||||||
(define-r4rs-input (name arg ...) stream s48name body ...)
|
|
||||||
(define (name arg ... . maybe-i/o)
|
|
||||||
(let ((stream (:optional maybe-i/o (current-input-port))))
|
|
||||||
(cond ((input-port? stream) (s48name arg ... stream))
|
|
||||||
((integer? stream) body ...)
|
|
||||||
(else (error "Not a port or file descriptor" stream))))))
|
|
||||||
|
|
||||||
(define-r4rs-input (char-ready?) input s48-char-ready?
|
|
||||||
(%char-ready-fdes? input))
|
|
||||||
|
|
||||||
(define-r4rs-input (read-char) input s48-read-char
|
|
||||||
(read-fdes-char input))
|
|
||||||
|
|
||||||
;structure refs changed to get reference from scheme -dalbertz
|
|
||||||
(define s48-display (structure-ref scheme display))
|
|
||||||
(define s48-newline (structure-ref scheme newline))
|
|
||||||
(define s48-write (structure-ref scheme write))
|
|
||||||
(define s48-write-char (structure-ref scheme write-char))
|
|
||||||
(define s48-format (structure-ref formats format))
|
|
||||||
(define s48-force-output (structure-ref ports force-output))
|
|
||||||
|
|
||||||
(define-simple-syntax
|
|
||||||
(define-r4rs-output (name arg ...) stream s48name body ...)
|
|
||||||
(define (name arg ... . maybe-i/o)
|
|
||||||
(let ((stream (:optional maybe-i/o (current-output-port))))
|
|
||||||
(cond ((output-port? stream) (s48name arg ... stream))
|
|
||||||
((integer? stream) body ...)
|
|
||||||
(else (error "Not a port or file descriptor" stream))))))
|
|
||||||
|
|
||||||
;;; This one depends upon S48's string ports.
|
|
||||||
(define-r4rs-output (display object) output s48-display
|
|
||||||
(let ((sp (make-string-output-port)))
|
|
||||||
(display object sp)
|
|
||||||
(write-string (string-output-port-output sp) output)))
|
|
||||||
|
|
||||||
(define-r4rs-output (newline) output s48-newline
|
|
||||||
(write-fdes-char #\newline output))
|
|
||||||
|
|
||||||
(define-r4rs-output (write object) output s48-write
|
|
||||||
(let ((sp (make-string-output-port)))
|
|
||||||
(write object sp)
|
|
||||||
(write-string (string-output-port-output sp) output)))
|
|
||||||
|
|
||||||
(define-r4rs-output (write-char char) output s48-write-char
|
|
||||||
(write-fdes-char char output))
|
|
||||||
|
|
||||||
;;; S48's force-output doesn't default to forcing (current-output-port).
|
|
||||||
(define-r4rs-output (force-output) output s48-force-output
|
|
||||||
(values)) ; Do nothing if applied to a file descriptor.
|
|
||||||
|
|
||||||
|
|
||||||
(define (format dest cstring . args)
|
|
||||||
(if (integer? dest)
|
|
||||||
(write-string (apply s48-format #f cstring args) dest)
|
|
||||||
(apply s48-format dest cstring args)))
|
|
||||||
|
|
||||||
;;; with-current-foo-port procs
|
|
||||||
;;; ---------------------------
|
|
||||||
|
|
||||||
(define (with-current-input-port* port thunk)
|
|
||||||
(let-fluid $current-input-port port thunk))
|
|
||||||
|
|
||||||
(define (with-current-output-port* port thunk)
|
|
||||||
(let-fluid $current-output-port port thunk))
|
|
||||||
|
|
||||||
(define (with-error-output-port* port thunk)
|
|
||||||
(let-fluid $error-output-port port thunk))
|
|
||||||
|
|
||||||
(define-simple-syntax (with-current-input-port port body ...)
|
|
||||||
(with-current-input-port* port (lambda () body ...)))
|
|
||||||
|
|
||||||
(define-simple-syntax (with-current-output-port port body ...)
|
|
||||||
(with-current-output-port* port (lambda () body ...)))
|
|
||||||
|
|
||||||
(define-simple-syntax (with-error-output-port port body ...)
|
|
||||||
(with-error-output-port* port (lambda () body ...)))
|
|
||||||
|
|
||||||
|
|
||||||
;;; set-foo-port! procs
|
|
||||||
;;; -------------------
|
|
||||||
;;; Side-effecting variants of with-current-input-port* and friends.
|
|
||||||
|
|
||||||
(define (set-current-input-port! port) (set-fluid! $current-input-port port))
|
|
||||||
(define (set-current-output-port! port) (set-fluid! $current-output-port port))
|
|
||||||
(define (set-error-output-port! port) (set-fluid! $error-output-port port))
|
|
||||||
|
|
||||||
|
|
||||||
;;; call-with-foo-file with-foo-to-file
|
|
||||||
;;; -----------------------------------
|
|
||||||
|
|
||||||
;;; Copied straight from rts/port.scm, but re-defined in this module,
|
|
||||||
;;; closed over my versions of open-input-file and open-output-file.
|
|
||||||
|
|
||||||
(define (call-with-mumble-file open close)
|
|
||||||
(lambda (string proc)
|
|
||||||
(let ((port #f))
|
|
||||||
(dynamic-wind (lambda ()
|
|
||||||
(if port
|
|
||||||
(warn "throwing back into a call-with-...put-file"
|
|
||||||
string)
|
|
||||||
(set! port (open string))))
|
|
||||||
(lambda () (proc port))
|
|
||||||
(lambda ()
|
|
||||||
(if port
|
|
||||||
(close port)))))))
|
|
||||||
|
|
||||||
(define call-with-input-file
|
|
||||||
(call-with-mumble-file open-input-file close-input-port))
|
|
||||||
|
|
||||||
(define call-with-output-file
|
|
||||||
(call-with-mumble-file open-output-file close-output-port))
|
|
||||||
|
|
||||||
(define (with-input-from-file string thunk)
|
|
||||||
(call-with-input-file string
|
|
||||||
(lambda (port)
|
|
||||||
(let-fluid $current-input-port port thunk))))
|
|
||||||
|
|
||||||
(define (with-output-to-file string thunk)
|
|
||||||
(call-with-output-file string
|
|
||||||
(lambda (port)
|
|
||||||
(let-fluid $current-output-port port thunk))))
|
|
|
@ -1,340 +0,0 @@
|
||||||
;;; Unix wait & process objects for scsh
|
|
||||||
;;; Copyright (c) 1993, 1994, 1995 by Olin Shivers.
|
|
||||||
|
|
||||||
;;; This is a GC'd abstraction for Unix process id's.
|
|
||||||
;;; The problem with Unix pids is (a) they clutter up the kernel
|
|
||||||
;;; process table until you wait(2) them, and (b) you can only
|
|
||||||
;;; wait(2) them once. Scsh's process objects are similar, but
|
|
||||||
;;; allow the storage to be allocated in the scsh address space,
|
|
||||||
;;; and out of the kernel process table, and they can be waited on
|
|
||||||
;;; multiple times.
|
|
||||||
|
|
||||||
;;; Process objects
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
|
|
||||||
(define-record proc ; A process object
|
|
||||||
pid ; Proc's pid.
|
|
||||||
(%status #f) ; The cached exit status of the process;
|
|
||||||
; #f if we haven't wait(2)'d the process yet.
|
|
||||||
|
|
||||||
;; Make proc objects print like #{proc 2318}.
|
|
||||||
((disclose p) (list "proc" (proc:pid p))))
|
|
||||||
|
|
||||||
|
|
||||||
;;; Indexing this table by pid requires a linear scan.
|
|
||||||
;;; Probably not an important op, tho.
|
|
||||||
|
|
||||||
(define process-table (make-population))
|
|
||||||
|
|
||||||
(define (maybe-pid->proc pid)
|
|
||||||
(call/cc (lambda (quit)
|
|
||||||
;; Search the table.
|
|
||||||
(walk-population (lambda (p)
|
|
||||||
(if (= (proc:pid p) pid) (quit p)))
|
|
||||||
process-table)
|
|
||||||
#f)))
|
|
||||||
|
|
||||||
(define (pid->proc pid . maybe-probe?)
|
|
||||||
(let ((probe? (:optional maybe-probe? #f)))
|
|
||||||
(or (maybe-pid->proc pid)
|
|
||||||
(case probe?
|
|
||||||
((#f) (error "Pid has no corresponding process object" pid))
|
|
||||||
((create) (let ((p (make-proc pid))) ; Install a new one.
|
|
||||||
(add-to-population! p process-table)
|
|
||||||
p))
|
|
||||||
(else #f)))))
|
|
||||||
|
|
||||||
;;; Coerce pids and procs to procs.
|
|
||||||
|
|
||||||
(define (->proc proc/pid)
|
|
||||||
(cond ((proc? proc/pid) proc/pid)
|
|
||||||
((and (integer? proc/pid) (>= proc/pid 0))
|
|
||||||
(pid->proc proc/pid))
|
|
||||||
(else (error "Illegal parameter" ->proc proc/pid))))
|
|
||||||
|
|
||||||
|
|
||||||
;;; Is X a pid or a proc?
|
|
||||||
|
|
||||||
(define (pid/proc? x) (or (proc? x) (and (integer? x) (>= pid 0))))
|
|
||||||
|
|
||||||
|
|
||||||
;;; Process reaping
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
;;; "Reaping" a process means using wait(2) to move its exit status from the
|
|
||||||
;;; kernel's process table into scsh, thus cleaning up the kernel's process
|
|
||||||
;;; table and saving the value in a gc'd data structure, where it can be
|
|
||||||
;;; referenced multiple times.
|
|
||||||
;;;
|
|
||||||
;;; - Stopped processes are never reaped, only dead ones.
|
|
||||||
;;;
|
|
||||||
;;; - Stopped process status codes are never cached in proc objects,
|
|
||||||
;;; only status codes for dead processes. So you can wait for a
|
|
||||||
;;; dead process multiple times, but only once per process-stop.
|
|
||||||
;;;
|
|
||||||
;;; - Unfortunately, reaping a process loses the information specifying its
|
|
||||||
;;; process group, so if a process is reaped into scsh, it cannot be
|
|
||||||
;;; waited for by WAIT-PROCESS-GROUP. Notice that only dead processes are
|
|
||||||
;;; reaped, not suspended ones. Programs almost never use WAIT-PROCESS-GROUP
|
|
||||||
;;; to wait for dead processes, so this is not likely to be a problem. If
|
|
||||||
;;; it is, turn autoreaping off with (autoreap-policy #f).
|
|
||||||
;;;
|
|
||||||
;;; - Reaping can be encouraged by calling (REAP-ZOMBIES).
|
|
||||||
|
|
||||||
;;; (autoreap-policy [new-policy])
|
|
||||||
|
|
||||||
(define *autoreap-policy* #f) ; Not exported from this module.
|
|
||||||
|
|
||||||
(define (autoreap-policy . maybe-policy)
|
|
||||||
(let ((old-policy *autoreap-policy*))
|
|
||||||
(if (pair? maybe-policy)
|
|
||||||
(let ((new-policy (car maybe-policy)))
|
|
||||||
(cond ((pair? (cdr maybe-policy))
|
|
||||||
(error "Too many args to autoreap-policy" maybe-policy))
|
|
||||||
((not (memq new-policy '(early #f)))
|
|
||||||
(error "Illegal autoreap policy." new-policy))
|
|
||||||
(else (set! *autoreap-policy* new-policy)
|
|
||||||
(if (eq? new-policy 'early)
|
|
||||||
(set-interrupt-handler interrupt/chld
|
|
||||||
(lambda (enabled-ints) (reap-zombies))))))))
|
|
||||||
old-policy))
|
|
||||||
|
|
||||||
;;; (reap-zombies) => bool
|
|
||||||
;;; Move any zombies from the kernel process table into scsh.
|
|
||||||
;;; Return true if no more outstanding children; #f if some still live.
|
|
||||||
|
|
||||||
(define (reap-zombies)
|
|
||||||
(let lp ()
|
|
||||||
(receive (pid status) (%wait-any wait/poll)
|
|
||||||
(if pid
|
|
||||||
(begin (add-reaped-proc! pid status)
|
|
||||||
; (format (current-error-port)
|
|
||||||
; "Reaping ~d[~d]~%" pid status)
|
|
||||||
(lp))
|
|
||||||
status))))
|
|
||||||
|
|
||||||
;;; This list contains procs that haven't exited yet. FORK adds new
|
|
||||||
;;; procs to the list. When a proc exits, it is removed from the list.
|
|
||||||
;;; Being on this list keeps live children's proc objects from being gc'd.
|
|
||||||
|
|
||||||
(define unexited-procs '())
|
|
||||||
|
|
||||||
(define (new-child-proc pid)
|
|
||||||
(let ((proc (make-proc pid)))
|
|
||||||
(add-to-population! proc process-table)
|
|
||||||
(set! unexited-procs (cons proc unexited-procs))
|
|
||||||
proc))
|
|
||||||
|
|
||||||
(define (mark-proc-exited proc)
|
|
||||||
(set! unexited-procs (del proc unexited-procs)))
|
|
||||||
|
|
||||||
|
|
||||||
;;; (WAIT proc/pid [flags])
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
|
|
||||||
;;; (wait proc/pid [flags]) => status or #f
|
|
||||||
;;;
|
|
||||||
;;; FLAGS (default 0) is the exclusive or of the following:
|
|
||||||
;;; wait/poll
|
|
||||||
;;; Return #f immediately if there are no
|
|
||||||
;;; unwaited children available.
|
|
||||||
;;; wait/stopped-children
|
|
||||||
;;; Report on suspended children as well.
|
|
||||||
;;;
|
|
||||||
;;; If the process hasn't terminated (or suspended, if wait/stopped
|
|
||||||
;;; is set) and wait/poll is set, return #f.
|
|
||||||
|
|
||||||
;;; WAIT waits for a specific process. Before performing a waitpid(2)
|
|
||||||
;;; systcall, wait first consults the proc object to see if the process has
|
|
||||||
;;; been reaped already. If so, its saved status is returned immediately.
|
|
||||||
;;;
|
|
||||||
|
|
||||||
(define (wait pid/proc . maybe-flags)
|
|
||||||
(let* ((flags (:optional maybe-flags 0))
|
|
||||||
(proc (->proc pid/proc))
|
|
||||||
(win (lambda (status)
|
|
||||||
(mark-proc-waited! proc) ; Not eligible for a WAIT-ANY
|
|
||||||
status)))
|
|
||||||
(let lp ()
|
|
||||||
;; First, see if we've already waited or reaped the process.
|
|
||||||
(cond ((proc:%status proc) => win)
|
|
||||||
|
|
||||||
(else ; Really wait.
|
|
||||||
(receive (err pid status) (%wait-pid/errno (proc:pid proc) flags)
|
|
||||||
(cond ((not err)
|
|
||||||
(and (not (zero? pid)) ; pid=0 => none ready.
|
|
||||||
(win (cache-wait-status proc status))))
|
|
||||||
|
|
||||||
((= err errno/intr) (lp))
|
|
||||||
|
|
||||||
;; We got an error -- before reporting it, check
|
|
||||||
;; the proc record one last time.
|
|
||||||
((proc:%status proc) => win)
|
|
||||||
|
|
||||||
(else (errno-error err %wait-pid pid flags)))))))))
|
|
||||||
|
|
||||||
;;; Another way to do it:
|
|
||||||
;;; Every time we reap a process, we pop out of our SIGCHLD
|
|
||||||
;;; block so that we can service an interrupt if the system
|
|
||||||
;;; so wishes.
|
|
||||||
;(define (wait/pid pid)
|
|
||||||
; ((let lp ()
|
|
||||||
; (blocking signal/chld
|
|
||||||
; (or (waited pid) ; Previously waited or reaped
|
|
||||||
; (receive (next-dead status) (reap-a-pid)
|
|
||||||
; (if (= pid next-dead) (lambda () status)
|
|
||||||
; lp)))))))
|
|
||||||
|
|
||||||
|
|
||||||
(define (cache-wait-status proc status)
|
|
||||||
(cond ((and (integer? status)
|
|
||||||
(not (status:stop-sig status))) ; He's dead, Jim.
|
|
||||||
(set-proc:%status proc status) ; Cache exit status.
|
|
||||||
(mark-proc-exited proc))) ; We're now gc'able.
|
|
||||||
status)
|
|
||||||
|
|
||||||
|
|
||||||
;;; (wait-any [flags]) => [proc status]
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
;;; [#f #f] => non-blocking, none ready.
|
|
||||||
;;; [#f #t] => no more.
|
|
||||||
|
|
||||||
(define (wait-any . maybe-flags)
|
|
||||||
(let ((table-hit (lambda (proc) (values proc (proc:%status proc)))) ; Hit.
|
|
||||||
(flags (:optional maybe-flags 0)))
|
|
||||||
(cond ((get-reaped-proc!) => table-hit) ; Check internal table.
|
|
||||||
|
|
||||||
(else ; Really wait.
|
|
||||||
(let lp ()
|
|
||||||
(receive (err pid status)
|
|
||||||
(%wait-pid/errno -1 flags)
|
|
||||||
|
|
||||||
;; We got an error of some sort. Check the reaped table
|
|
||||||
;; one last time before really deciding there was an error.
|
|
||||||
(cond (err (cond ((get-reaped-proc!) => table-hit)
|
|
||||||
((= err errno/child) (values #f #t)) ; No more.
|
|
||||||
((= err errno/intr) (lp))
|
|
||||||
(else (errno-error err %wait-any flags))))
|
|
||||||
|
|
||||||
;; None ready. Check the reaped-proc table once more
|
|
||||||
;; before reporting this.
|
|
||||||
((zero? pid)
|
|
||||||
(cond ((get-reaped-proc!) => table-hit)
|
|
||||||
(else (values #f #f)))) ; None ready.
|
|
||||||
|
|
||||||
;; Win.
|
|
||||||
(else (let ((proc (pid->proc pid)))
|
|
||||||
(cache-wait-status proc status)
|
|
||||||
(values proc status))))))))))
|
|
||||||
|
|
||||||
|
|
||||||
;;; (wait-process-group [proc-group flags]) => [proc status]
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
;;; [#f #f] => non-blocking, none ready.
|
|
||||||
;;; [#f #t] => no more.
|
|
||||||
;;;
|
|
||||||
;;;
|
|
||||||
;;; If you are doing process-group waits, you do *not* want to use
|
|
||||||
;;; early autoreaping, since the reaper loses process-group information.
|
|
||||||
|
|
||||||
(define (wait-process-group . args)
|
|
||||||
(let-optionals args ((proc-group 0) (flags 0))
|
|
||||||
(let ((proc-group (cond ((integer? proc-group) proc-group)
|
|
||||||
((proc? proc-group) (proc:pid proc-group))
|
|
||||||
(else (error "Illegal argument" wait-process-group
|
|
||||||
proc-group)))))
|
|
||||||
(receive (pid status) (%wait-process-group proc-group flags)
|
|
||||||
(if pid
|
|
||||||
(let ((proc (pid->proc pid)))
|
|
||||||
(cache-wait-status proc status)
|
|
||||||
(values proc status))
|
|
||||||
(values pid status)))))) ; pid = #f -- Empty poll.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
;;; (%wait-any flags) (%wait-pid pid flags) (%wait-process-group pgrp flags)
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
;;; Direct interfaces to waitpid(2) call.
|
|
||||||
;;; [#f #f] means no processes ready on a non-blocking wait.
|
|
||||||
;;; [#f #t] means no waitable process on wait-any.
|
|
||||||
|
|
||||||
(define (%wait-pid pid flags)
|
|
||||||
(let lp ()
|
|
||||||
(receive (err pid status) (%wait-pid/errno pid flags)
|
|
||||||
(cond ((not err) (and (not (zero? pid)) status)) ; pid=0 => none ready.
|
|
||||||
((= err errno/intr) (lp))
|
|
||||||
(else (errno-error err %wait-pid pid flags))))))
|
|
||||||
|
|
||||||
(define (%wait-any flags)
|
|
||||||
(let lp ()
|
|
||||||
(receive (err pid status) (%wait-pid/errno -1 flags)
|
|
||||||
(cond (err (cond ((= err errno/child) (values #f #t)) ; No more.
|
|
||||||
((= err errno/intr) (lp))
|
|
||||||
(else (errno-error err %wait-any flags))))
|
|
||||||
((zero? pid) (values #f #f)) ; None ready.
|
|
||||||
(else (values pid status))))))
|
|
||||||
|
|
||||||
(define (%wait-process-group pgrp flags)
|
|
||||||
(let lp ()
|
|
||||||
(receive (err pid status) (%wait-pid/errno (- pgrp) flags)
|
|
||||||
(cond (err (cond ((= err errno/child) (values #f #t)) ; No more.
|
|
||||||
((= err errno/intr) (lp))
|
|
||||||
(else (errno-error err %wait-process-group pgrp flags))))
|
|
||||||
((zero? pid) (values #f #f)) ; None ready.
|
|
||||||
(else (values pid status))))))
|
|
||||||
|
|
||||||
|
|
||||||
;;; Reaped process table
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
;;; We keep track of procs that have been reaped but not yet waited on by
|
|
||||||
;;; the user's code. These proces are eligible for return by WAIT-ANY.
|
|
||||||
;;; We keep track of these so that WAIT-ANY will hand them out exactly once.
|
|
||||||
;;; Whenever WAIT, WAIT-ANY, WAIT-PROCESS-GROUP waits on a process to exit,
|
|
||||||
;;; it removes the process from this table if it's in it.
|
|
||||||
;;; This code is bogus -- we use weak pointers. We need populations that
|
|
||||||
;;; support deletion or filtering.
|
|
||||||
|
|
||||||
(define reaped-procs '()) ; Reaped, but not yet waited.
|
|
||||||
|
|
||||||
(define (filter-weak-ptr-list pred lis)
|
|
||||||
(reverse (reduce (lambda (result wptr)
|
|
||||||
(let ((val (weak-pointer-ref wptr)))
|
|
||||||
(if (and val (pred val))
|
|
||||||
(cons wptr result)
|
|
||||||
result)))
|
|
||||||
'()
|
|
||||||
lis)))
|
|
||||||
|
|
||||||
;;; Add a newly-reaped proc to the list.
|
|
||||||
(define (add-reaped-proc! pid status)
|
|
||||||
((with-enabled-interrupts 0
|
|
||||||
(cond ((maybe-pid->proc pid) =>
|
|
||||||
(lambda (proc)
|
|
||||||
(set-proc:%status proc status)
|
|
||||||
(set! reaped-procs (cons (make-weak-pointer proc)
|
|
||||||
reaped-procs))
|
|
||||||
(lambda () #f)))
|
|
||||||
(else (lambda () ; Do this w/interrupts enabled.
|
|
||||||
(warn "Exiting child pid has no proc object." pid status)))))))
|
|
||||||
|
|
||||||
;;; Pop one off the list.
|
|
||||||
(define (get-reaped-proc!)
|
|
||||||
(with-enabled-interrupts 0
|
|
||||||
(let grp! ()
|
|
||||||
(and (pair? reaped-procs)
|
|
||||||
(let ((proc (weak-pointer-ref (car reaped-procs))))
|
|
||||||
(set! reaped-procs (cdr reaped-procs))
|
|
||||||
(or proc (grp!)))))))
|
|
||||||
|
|
||||||
;;; PROC no longer eligible to be in the list. Delete it.
|
|
||||||
(define (mark-proc-waited! proc)
|
|
||||||
(with-enabled-interrupts 0
|
|
||||||
(set! reaped-procs
|
|
||||||
(filter-weak-ptr-list (lambda (elt) (not (eq? proc elt)))
|
|
||||||
reaped-procs))))
|
|
||||||
|
|
||||||
;;; The mark-proc-waited! machinery above is a crock. It is inefficient --
|
|
||||||
;;; we should have a flag in the proc saying if it's eligible for a WAIT-ANY.
|
|
||||||
;;; Starts off #t, changes to #f after a wait. On a #t->#f transition, we
|
|
||||||
;;; delete it from the WAIT-ANY population. Right now, every time the user
|
|
||||||
;;; waits on the proc, we re-delete it from the population -- which is
|
|
||||||
;;; a no-op after the first time.
|
|
181
scsh/rw.old.scm
181
scsh/rw.old.scm
|
@ -1,181 +0,0 @@
|
||||||
;;; Basic read and write
|
|
||||||
;;; Copyright (c) 1993 by Olin Shivers.
|
|
||||||
|
|
||||||
;;; Note: read ops should check to see if their string args are mutable.
|
|
||||||
|
|
||||||
(define (bogus-substring-spec? s start end)
|
|
||||||
(or (< start 0)
|
|
||||||
(< (string-length s) end)
|
|
||||||
(< end start)))
|
|
||||||
|
|
||||||
|
|
||||||
;;; Best-effort/forward-progress reading
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
|
|
||||||
(define (generic-read-string!/partial s start end reader source)
|
|
||||||
(if (bogus-substring-spec? s start end)
|
|
||||||
(error "Bad substring indices" reader source s start end))
|
|
||||||
|
|
||||||
(if (= start end) 0 ; Vacuous request.
|
|
||||||
(let loop ()
|
|
||||||
(receive (err nread) (reader s start end source)
|
|
||||||
(cond ((not err) (and (not (zero? nread)) nread))
|
|
||||||
((= err errno/intr) (loop))
|
|
||||||
((or (= err errno/wouldblock) ; No forward-progess here.
|
|
||||||
(= err errno/again))
|
|
||||||
0)
|
|
||||||
(else (errno-error err reader s start start end source)))))))
|
|
||||||
|
|
||||||
(define (read-string!/partial s . args)
|
|
||||||
(let-optionals args ((fd/port (current-input-port))
|
|
||||||
(start 0)
|
|
||||||
(end (string-length s)))
|
|
||||||
(cond ((integer? fd/port)
|
|
||||||
(generic-read-string!/partial s start end
|
|
||||||
read-fdes-substring!/errno fd/port))
|
|
||||||
((fdport? fd/port)
|
|
||||||
(generic-read-string!/partial s start end
|
|
||||||
read-fdport*-substring!/errno
|
|
||||||
(port-data fd/port)))
|
|
||||||
|
|
||||||
(else ; Hack it for base S48 ports
|
|
||||||
;; This case is a little gross in order to get
|
|
||||||
;; the forward-progress guarantee and handle non-blocking i/o.
|
|
||||||
;; Unix sux. So do low-level Scheme looping constructs.
|
|
||||||
(if (>= start end) 0
|
|
||||||
(let lp ((i start))
|
|
||||||
(let ((c (with-errno-handler
|
|
||||||
((err data) ((errno/wouldblock errno/again) #f))
|
|
||||||
(read-char fd/port))))
|
|
||||||
(cond ((not c) (- i start)) ; non-blocking i/o bailout
|
|
||||||
((eof-object? c)
|
|
||||||
(let ((nread (- i start)))
|
|
||||||
(and (not (zero? nread)) nread)))
|
|
||||||
(else
|
|
||||||
(string-set! s i c)
|
|
||||||
(let ((i (+ i 1)))
|
|
||||||
(if (or (= i end) (not (char-ready? fd/port)))
|
|
||||||
(- i start)
|
|
||||||
(lp i))))))))))))
|
|
||||||
|
|
||||||
(define (read-string/partial len . maybe-fd/port)
|
|
||||||
(let* ((s (make-string len))
|
|
||||||
(fd/port (:optional maybe-fd/port (current-input-port)))
|
|
||||||
(nread (read-string!/partial s fd/port 0 len)))
|
|
||||||
(cond ((not nread) #f) ; EOF
|
|
||||||
((= nread len) s)
|
|
||||||
(else (substring s 0 nread)))))
|
|
||||||
|
|
||||||
|
|
||||||
;;; Persistent reading
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
|
|
||||||
(define (generic-read-string! s start end reader source)
|
|
||||||
(if (bogus-substring-spec? s start end)
|
|
||||||
(error "Bad substring indices" reader source s start end))
|
|
||||||
|
|
||||||
(let loop ((i start))
|
|
||||||
(if (>= i end) (- i start)
|
|
||||||
(receive (err nread) (reader s i end source)
|
|
||||||
(cond (err (if (= err errno/intr) (loop i)
|
|
||||||
;; Give info on partially-read data in error packet.
|
|
||||||
(errno-error err reader
|
|
||||||
s start i end source)))
|
|
||||||
|
|
||||||
((zero? nread) ; EOF
|
|
||||||
(let ((result (- i start)))
|
|
||||||
(and (not (zero? result)) result)))
|
|
||||||
|
|
||||||
(else (loop (+ i nread))))))))
|
|
||||||
|
|
||||||
(define (read-string! s . args)
|
|
||||||
(let-optionals args ((fd/port (current-input-port))
|
|
||||||
(start 0)
|
|
||||||
(end (string-length s)))
|
|
||||||
(cond ((integer? fd/port)
|
|
||||||
(generic-read-string! s start end
|
|
||||||
read-fdes-substring!/errno fd/port))
|
|
||||||
|
|
||||||
((fdport? fd/port)
|
|
||||||
(generic-read-string! s start end
|
|
||||||
read-fdport*-substring!/errno
|
|
||||||
(port-data fd/port)))
|
|
||||||
|
|
||||||
;; Hack it
|
|
||||||
(else (let lp ((i start))
|
|
||||||
(if (= i end) (- end start)
|
|
||||||
(let ((c (read-char fd/port)))
|
|
||||||
(if (eof-object? c)
|
|
||||||
(let ((nread (- i start)))
|
|
||||||
(and (not (zero? nread)) nread))
|
|
||||||
(begin (string-set! s i c)
|
|
||||||
(lp (+ i 1)))))))))))
|
|
||||||
|
|
||||||
(define (read-string len . maybe-fd/port)
|
|
||||||
(let* ((s (make-string len))
|
|
||||||
(fd/port (:optional maybe-fd/port (current-input-port)))
|
|
||||||
(nread (read-string! s fd/port 0 len)))
|
|
||||||
(cond ((not nread) #f) ; EOF
|
|
||||||
((= nread len) s)
|
|
||||||
(else (substring s 0 nread)))))
|
|
||||||
|
|
||||||
|
|
||||||
;;; Best-effort/forward-progress writing
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
;;; Non-blocking output to a buffered port is not defined.
|
|
||||||
|
|
||||||
(define (generic-write-string/partial s start end writer target)
|
|
||||||
(if (bogus-substring-spec? s start end)
|
|
||||||
(error "Bad substring indices" writer s start end target))
|
|
||||||
|
|
||||||
(if (= start end) 0 ; Vacuous request.
|
|
||||||
(let loop ()
|
|
||||||
(receive (err nwritten) (writer s start end target)
|
|
||||||
(cond ((not err) nwritten)
|
|
||||||
((= err errno/intr) (loop))
|
|
||||||
((or (= err errno/again) (= err errno/wouldblock)) 0)
|
|
||||||
(else (errno-error err writer
|
|
||||||
s start start end target)))))))
|
|
||||||
|
|
||||||
(define (write-string/partial s . args)
|
|
||||||
(let-optionals args ((fd/port (current-output-port))
|
|
||||||
(start 0)
|
|
||||||
(end (string-length s)))
|
|
||||||
(cond ((integer? fd/port)
|
|
||||||
(generic-write-string/partial s start end
|
|
||||||
write-fdes-substring/errno fd/port))
|
|
||||||
((fdport? fd/port)
|
|
||||||
(generic-write-string/partial s start end
|
|
||||||
write-fdport*-substring/errno
|
|
||||||
(port-data fd/port)))
|
|
||||||
(else (display (substring s start end) fd/port))))) ; hack
|
|
||||||
|
|
||||||
|
|
||||||
;;; Persistent writing
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
|
|
||||||
(define (generic-write-string s start end writer target)
|
|
||||||
(if (bogus-substring-spec? s start end)
|
|
||||||
(error "Bad substring indices" writer s start end target))
|
|
||||||
|
|
||||||
(let loop ((i start))
|
|
||||||
(if (< i end)
|
|
||||||
(receive (err nwritten) (writer s i end target)
|
|
||||||
(cond ((not err) (loop (+ i nwritten)))
|
|
||||||
((= err errno/intr) (loop i))
|
|
||||||
(else (errno-error err writer
|
|
||||||
s start i end target)))))))
|
|
||||||
|
|
||||||
(define (write-string s . args)
|
|
||||||
(let-optionals args ((fd/port (current-output-port))
|
|
||||||
(start 0)
|
|
||||||
(end (string-length s)))
|
|
||||||
(cond ((integer? fd/port)
|
|
||||||
(generic-write-string s start end
|
|
||||||
write-fdes-substring/errno fd/port))
|
|
||||||
((fdport? fd/port)
|
|
||||||
(generic-write-string s start end
|
|
||||||
write-fdport*-substring/errno
|
|
||||||
(port-data fd/port)))
|
|
||||||
|
|
||||||
(else (display (substring s start end) fd/port))))) ; hack
|
|
|
@ -1,225 +0,0 @@
|
||||||
;;; Copyright (c) 1993 by Olin Shivers.
|
|
||||||
;;; Signal handler system
|
|
||||||
|
|
||||||
;;; The principal trickiness here is that we have to interface to Unix signals
|
|
||||||
;;; *through* an intermediate interface, the S48 vm's idea of interrupts.
|
|
||||||
;;; So there is a difference between delivering a signal to the underlying
|
|
||||||
;;; Unix process and delivering it to the program that runs on the VM.
|
|
||||||
;;;
|
|
||||||
;;; One effect is that we have two separate codes for the same thing -- the
|
|
||||||
;;; Unix signal code, and the S48 interrupt value. E.g., SIGNAL/TSTP and
|
|
||||||
;;; INTERRUPT/TSTP.
|
|
||||||
|
|
||||||
;;; These system calls can return EINTR or restart. In order for the S48 vm's
|
|
||||||
;;; interrupt system to detect a signal and invoke the handler, they *must*
|
|
||||||
;;; return EINTR, and this must cause a return from C to Scheme.
|
|
||||||
;;;
|
|
||||||
;;; open close dup2 accept connect
|
|
||||||
;;; read recv recvfrom recvmsg
|
|
||||||
;;; write send sendto sendmsg
|
|
||||||
;;; select
|
|
||||||
;;; wait
|
|
||||||
;;; fcntl* ioctl
|
|
||||||
;;; sigsuspend
|
|
||||||
;;; HP-UX, but I don't use: poll lockf msem_lock msgsnd msgrcv semop
|
|
||||||
;;;
|
|
||||||
;;; * Only during a F_SETLKW
|
|
||||||
;;;
|
|
||||||
;;; From rts/interrupt.scm (package interrupts, interface interrupts-interface)
|
|
||||||
;;; WITH-INTERRUPTS INTERRUPT-HANDLERS SET-ENABLED-INTERRUPTS !
|
|
||||||
;;; ENABLED-INTERRUPTS
|
|
||||||
;;; Must define WITH-INTERRUPTS* and WITH-INTERRUPTS.
|
|
||||||
|
|
||||||
(foreign-source
|
|
||||||
"extern int errno;"
|
|
||||||
""
|
|
||||||
"/* Make sure foreign-function stubs interface to the C funs correctly: */"
|
|
||||||
"#include \"sighandlers1.h\""
|
|
||||||
"" "")
|
|
||||||
|
|
||||||
;;; Map a Unix async signal to its S48 interrupt value.
|
|
||||||
;;; -1 => Not defined.
|
|
||||||
(define-foreign %signal->interrupt (sig2interrupt (integer sig))
|
|
||||||
integer)
|
|
||||||
|
|
||||||
(define (signal->interrupt sig)
|
|
||||||
(let ((int (%signal->interrupt sig)))
|
|
||||||
(if (>= int 0) int
|
|
||||||
(error "Unix signal has no Scheme 48 interrupt." sig))))
|
|
||||||
|
|
||||||
|
|
||||||
(define (interrupt-set . interrupts)
|
|
||||||
(let lp ((ints interrupts) (ans 0))
|
|
||||||
(if (pair? ints)
|
|
||||||
(lp (cdr ints) (bitwise-ior ans (arithmetic-shift 1 (- (car ints) 1))))
|
|
||||||
ans)))
|
|
||||||
|
|
||||||
;;; I'm trying to be consistent about the ! suffix -- I don't use it
|
|
||||||
;;; when frobbing process state. This is not a great rule; perhaps I
|
|
||||||
;;; should change it.
|
|
||||||
(define set-enabled-interrupts set-enabled-interrupts!)
|
|
||||||
|
|
||||||
(define-simple-syntax (with-enabled-interrupts mask body ...)
|
|
||||||
(with-interrupts mask (lambda () body ...)))
|
|
||||||
|
|
||||||
(define with-enabled-interrupts* with-interrupts)
|
|
||||||
|
|
||||||
(define interrupt/alarm (enum interrupt alarm))
|
|
||||||
(define interrupt/keyboard (enum interrupt keyboard))
|
|
||||||
;(define interrupt/memory-shortage (enum interrupt memory-shortage))
|
|
||||||
(define interrupt/chld (enum interrupt chld))
|
|
||||||
(define interrupt/cont (enum interrupt cont))
|
|
||||||
(define interrupt/hup (enum interrupt hup))
|
|
||||||
(define interrupt/quit (enum interrupt quit))
|
|
||||||
(define interrupt/term (enum interrupt term))
|
|
||||||
(define interrupt/tstp (enum interrupt tstp))
|
|
||||||
(define interrupt/usr1 (enum interrupt usr1))
|
|
||||||
(define interrupt/usr2 (enum interrupt usr2))
|
|
||||||
(define interrupt/info (enum interrupt info))
|
|
||||||
(define interrupt/io (enum interrupt io))
|
|
||||||
(define interrupt/poll (enum interrupt poll))
|
|
||||||
(define interrupt/prof (enum interrupt prof))
|
|
||||||
(define interrupt/pwr (enum interrupt pwr))
|
|
||||||
(define interrupt/urg (enum interrupt urg))
|
|
||||||
(define interrupt/vtalrm (enum interrupt vtalrm))
|
|
||||||
(define interrupt/winch (enum interrupt winch))
|
|
||||||
(define interrupt/xcpu (enum interrupt xcpu))
|
|
||||||
(define interrupt/xfsz (enum interrupt xfsz))
|
|
||||||
|
|
||||||
(define interrupt/int interrupt/keyboard)
|
|
||||||
(define interrupt/alrm interrupt/alarm)
|
|
||||||
|
|
||||||
|
|
||||||
;;; Get/Set signal handlers
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
|
|
||||||
;;; When you set a signal's handler to "default," if the default for that
|
|
||||||
;;; signal is something other than "ignore," we actually install this guy.
|
|
||||||
;;; When he is called by the S48 interrupt system, he'll magically make
|
|
||||||
;;; the default action happen (by calling C code that *really* sets the
|
|
||||||
;;; handler to SIGDFL, and then re-sending the signal). This basically
|
|
||||||
;;; terminates the process, since if the default isn't "ignore," it's always
|
|
||||||
;;; "terminate" of some kind. Doing it this way means the exit code given
|
|
||||||
;;; to our waiting parent proc correctly reflects how we died, and also
|
|
||||||
;;; makes the core dump happen if it should. Details, details.
|
|
||||||
|
|
||||||
(define-foreign %do-default-sigaction (do_default_sigaction (fixnum signal))
|
|
||||||
ignore)
|
|
||||||
|
|
||||||
(define default-int-handler-vec
|
|
||||||
;; Non-Unix-signal interrupts just get their default values from
|
|
||||||
;; the current value of I-H.
|
|
||||||
(let ((v (copy-vector interrupt-handlers)))
|
|
||||||
(do ((sig 31 (- sig 1))) ; For each Unix signal
|
|
||||||
((< sig 0)) ; make & install a default
|
|
||||||
(let ((i (%signal->interrupt sig))) ; signal handler.
|
|
||||||
(if (>= i 0) ; Don't mess with non-signal interrupts.
|
|
||||||
(vector-set! v i (if (memv sig signals-ignored-by-default)
|
|
||||||
(lambda (enabled-interrupts) #f)
|
|
||||||
(lambda (enabled-interrupts)
|
|
||||||
(%do-default-sigaction sig)))))))
|
|
||||||
v))
|
|
||||||
|
|
||||||
;;; HANDLER is #f (ignore), #t (default), or a procedure taking an integer
|
|
||||||
;;; argument. The interrupt is delivered to a procedure by (1) setting the
|
|
||||||
;;; ENABLED-INTERRUPTS register to 0 (i.e., blocking all interrupts), and (2)
|
|
||||||
;;; applying the procedure to the previous value of the ENABLED-INTERRUPTS
|
|
||||||
;;; register. If the procedure returns normally, the ENABLED-INTERRUPTS
|
|
||||||
;;; register will be restored to its previous value.
|
|
||||||
|
|
||||||
;;; This handler does nothing -- used when the handler is #f.
|
|
||||||
(define (noop-sig-handler enabled-interrupts) #f)
|
|
||||||
|
|
||||||
(define (set-interrupt-handler int handler)
|
|
||||||
(let ((ohandler (interrupt-handler int)))
|
|
||||||
(vector-set! interrupt-handlers int
|
|
||||||
(case handler
|
|
||||||
((#t) (vector-ref default-int-handler-vec int))
|
|
||||||
((#f) noop-sig-handler)
|
|
||||||
(else handler)))
|
|
||||||
|
|
||||||
(cond ((and (not handler) ohandler ; Toggling from something
|
|
||||||
(int->signal int)) => ; to ignored.
|
|
||||||
(lambda (sig)
|
|
||||||
(%set-unix-signal-handler sig 0)))
|
|
||||||
((and handler (not ohandler) ; Toggling from ignored
|
|
||||||
(int->signal int)) => ; to something.
|
|
||||||
(lambda (sig)
|
|
||||||
(%set-unix-signal-handler sig 2))))
|
|
||||||
|
|
||||||
ohandler))
|
|
||||||
|
|
||||||
(define (interrupt-handler int)
|
|
||||||
(let ((handler (vector-ref interrupt-handlers int)))
|
|
||||||
(cond ((eq? handler (vector-ref default-int-handler-vec int)) #t)
|
|
||||||
((eq? handler noop-sig-handler) #f)
|
|
||||||
(else handler))))
|
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
;;; Set the Unix signal handler. One doesn't usually use this; one usually
|
|
||||||
;;; uses the S48 VM's interrupt system.
|
|
||||||
;;; HANDLER-CODE: 0 => ignore, 1 => default, 2 => S48 VM
|
|
||||||
;;; Returns equivalent code, additionally 3 => other handler.
|
|
||||||
;;; Raises an error exception if there's a problem.
|
|
||||||
|
|
||||||
(define (%set-unix-signal-handler sig handler-code)
|
|
||||||
(check-arg (lambda (x) (and (integer? sig) (< 0 sig 32)))
|
|
||||||
sig
|
|
||||||
%set-unix-signal-handler)
|
|
||||||
(check-arg (lambda (x) (and (integer? handler-code) (<= 0 handler-code 2)))
|
|
||||||
handler-code
|
|
||||||
%set-unix-signal-handler)
|
|
||||||
(let retry ()
|
|
||||||
(receive (err old-hc old-flags)
|
|
||||||
(%%set-unix-signal-handler sig handler-code 0)
|
|
||||||
(cond ((not err) old-hc)
|
|
||||||
((= err errno/intr) (retry))
|
|
||||||
(else (errno-error err %set-unix-signal-handler sig handler-code))))))
|
|
||||||
|
|
||||||
(define-foreign %%set-unix-signal-handler
|
|
||||||
(scsh_set_sig (fixnum sig) (fixnum hc) (fixnum flags))
|
|
||||||
desc ; #f or errno
|
|
||||||
integer ; previous handler-code
|
|
||||||
integer) ; previous handler flags
|
|
||||||
|
|
||||||
(define (%unix-signal-handler sig)
|
|
||||||
(check-arg (lambda (x) (and (integer? sig) (< 0 sig 32)))
|
|
||||||
sig
|
|
||||||
%unix-signal-handler)
|
|
||||||
(let retry ()
|
|
||||||
(receive (err hc flags) (%%unix-signal-handler sig)
|
|
||||||
(cond ((not err) hc)
|
|
||||||
((= err errno/intr) (retry))
|
|
||||||
(else (errno-error err %unix-signal-handler sig))))))
|
|
||||||
|
|
||||||
(define-foreign %%unix-signal-handler (scsh_get_sig (fixnum sig))
|
|
||||||
desc ; #f or errno
|
|
||||||
integer ; previous handler-code
|
|
||||||
integer) ; previous handler flags
|
|
||||||
|
|
||||||
(define-foreign %install-unix-scsh-handlers (install_scsh_handlers) ignore)
|
|
||||||
|
|
||||||
(define-foreign %%get-int-handlers (get_int_handlers) desc)
|
|
||||||
|
|
||||||
(define (%install-scsh-handlers)
|
|
||||||
(do ((sig 32 (- sig 1)))
|
|
||||||
((< sig 0))
|
|
||||||
(let ((i (%signal->interrupt sig)))
|
|
||||||
(if (not (or (= i -1)
|
|
||||||
(= sig signal/int) ; Leave ^c and
|
|
||||||
(= sig signal/alrm))) ; alarm handlers alone.
|
|
||||||
(vector-set! interrupt-handlers i
|
|
||||||
(vector-ref default-int-handler-vec i))))))
|
|
||||||
|
|
||||||
;;; I am ashamed to say the 33 below is completely bogus.
|
|
||||||
;;; What we want is a value that is 1 + max interrupt value.
|
|
||||||
|
|
||||||
(define int->sig-vec
|
|
||||||
(let ((v (make-vector 33 #f)))
|
|
||||||
(do ((sig 32 (- sig 1)))
|
|
||||||
((< sig 0))
|
|
||||||
(let ((i (%signal->interrupt sig)))
|
|
||||||
(if (not (= i -1)) (vector-set! v i sig))))
|
|
||||||
v))
|
|
||||||
|
|
||||||
(define (int->signal i) (and (<= 0 i 32) (vector-ref int->sig-vec i)))
|
|
Loading…
Reference in New Issue