Commit bf64fa70 authored by sof's avatar sof
Browse files

[project @ 1998-08-14 12:42:01 by sof]

Beefed up IO stub functions to not have to rely on stdio any longer
parent fb49b1af
%
%
% (c) The GRASP/AQUA Project, Glasgow University, 1998
%
\subsection[allocMem.lc]{Allocating memory off heap}
\begin{code}
#include "rtsdefs.h"
#include "stgio.h"
StgAddr
allocMemory__(sz)
StgInt sz;/* bytes*/
{
StgAddr ptr;
if ( (ptr = malloc(sz*sizeof(char))) == NULL) {
ghc_errtype = ERR_RESOURCEEXHAUSTED;
ghc_errstr = "malloc failed";
return NULL;
}
return ptr;
}
\end{code}
......@@ -8,14 +8,29 @@
#include "rtsdefs.h"
#include "stgio.h"
StgInt __really_close_stdfiles=1;
StgInt
closeFile(fp)
StgForeignObj fp;
closeFile(ptr,flush_buf)
StgForeignObj ptr;
StgInt flush_buf;
{
int rc;
IOFileObject* fo = (IOFileObject*)ptr;
int rc = 0;
int unlocked=1;
if ( unlockFile(fileno((FILE *) fp)) ) {
/* Already closed, shouldn't occur. */
if ( fo == NULL ) {
return 0;
}
if ( flush_buf != 0 && (fo->flags & FILEOBJ_FLUSH) ) {
writeFileObject(ptr,fo->bufWPtr);
}
/* If the flush failed, we ignore this and soldier on.. */
if ( unlockFile(fo->fd) ) {
/* If the file has already been unlocked (or an entry
for it in the locking tables couldn't be found), could
mean two things:
......@@ -30,25 +45,36 @@ StgForeignObj fp;
(file descriptors to non regular files.)
We proceed with attempting to close the file,
but don't flag the error should fclose() return
but don't flag the error should close() return
EBADF
*/
unlocked=0;
}
while ((rc = fclose((FILE *) fp)) != 0) {
/* See above comment */
if ( errno != EINTR && (!unlocked && errno != EBADF ) ) {
/* Closing file descriptors that refer to standard channels
is problematic, so we back off from doing this by default,
just closing them at the Handle level. If you insist on
closing them, setting the (global) variable
__really_close_stdfiles to 0 turns off this behaviour.
*/
if ( (fo->flags & FILEOBJ_STD) && __really_close_stdfiles ) {
;
} else {
/* Regardless of success or otherwise, the fd field gets smashed. */
while ( (rc = close(fo->fd)) != 0 ) {
/* See above unlockFile() comment */
if ( errno != EINTR && (!unlocked && errno != EBADF ) ) {
cvtErrno();
stdErrno();
fo->fd = -1;
return rc;
}
}
}
fo->fd = -1;
return 0;
}
\end{code}
......@@ -25,20 +25,16 @@
#endif
StgInt
setTerminalEcho(fp, on)
StgForeignObj fp;
setTerminalEcho(ptr, on)
StgForeignObj ptr;
StgInt on;
{
IOFileObject* fo = (IOFileObject*)ptr;
struct termios tios;
int fd, rc;
while ( (fd = fileno((FILE*)fp)) < 0) {
if (errno != EINTR) {
cvtErrno();
stdErrno();
return -1;
}
}
fd = fo->fd;
while ( (rc = tcgetattr(fd,&tios)) == -1) {
if (errno != EINTR) {
cvtErrno();
......@@ -64,19 +60,15 @@ StgInt on;
}
StgInt
getTerminalEcho(fp)
StgForeignObj fp;
getTerminalEcho(ptr)
StgForeignObj ptr;
{
IOFileObject* fo = (IOFileObject*)ptr;
struct termios tios;
int fd, rc;
while ( (fd = fileno((FILE*)fp)) < 0) {
if (errno != EINTR) {
cvtErrno();
stdErrno();
return -1;
}
}
fd = fo->fd;
while ( (rc = tcgetattr(fd,&tios)) == -1) {
if (errno != EINTR) {
cvtErrno();
......@@ -88,19 +80,15 @@ StgForeignObj fp;
}
StgInt
isTerminalDevice(fp)
StgForeignObj fp;
isTerminalDevice(ptr)
StgForeignObj ptr;
{
IOFileObject* fo = (IOFileObject*)ptr;
struct termios tios;
int fd, rc;
while ( (fd = fileno((FILE*)fp)) < 0) {
if (errno != EINTR) {
cvtErrno();
stdErrno();
return -1;
}
}
fd = fo -> fd;
while ( (rc = tcgetattr(fd,&tios)) == -1) {
if (errno == ENOTTY) return 0;
if (errno != EINTR) {
......
......@@ -13,6 +13,19 @@ int ghc_errtype = 0;
char *ghc_errstr = NULL;
StgAddr
getErrStr__()
{ return ((StgAddr)ghc_errstr); }
StgInt
getErrNo__()
{ return ((StgInt)ghc_errno); }
StgInt
getErrType__()
{ return ((StgInt)ghc_errtype); }
/* Collect all of the grotty #ifdef's in one place. */
void cvtErrno(STG_NO_ARGS)
......
......@@ -9,10 +9,15 @@
#include "stgio.h"
StgInt
fileEOF(fp)
StgForeignObj fp;
fileEOF(ptr)
StgForeignObj ptr;
{
if (fileLookAhead(fp) != EOF)
IOFileObject* fo = (IOFileObject*)ptr;
if ( FILEOBJ_IS_EOF(fo) )
return 1;
if (fileLookAhead(ptr) != EOF)
return 0;
else if (ghc_errtype == ERR_EOF)
return 1;
......
......@@ -9,30 +9,88 @@
#include "stgio.h"
#include "error.h"
#define EOT 4
/* Pre-condition: only ever called on a readable fileObject */
StgInt
fileGetc(fp)
StgForeignObj fp;
fileGetc(ptr)
StgForeignObj ptr;
{
int c;
IOFileObject* fo = (IOFileObject*)ptr;
int l,rc=0;
unsigned char c;
#if 0
fprintf(stderr, "fgc: %d %d %d\n", fo->bufRPtr, fo->bufWPtr, fo->flags);
#endif
/*
fileGetc does the following:
- if the input is buffered, try fetch the char from buffer.
- failing that,
- if the input stream is 'connected' to an output stream,
flush it before requesting any input.
- if unbuffered, read in one character.
- if line-buffered, read in one line, returning the first.
- if block-buffered, fill up block, returning the first.
*/
if (feof((FILE *) fp)) {
ghc_errtype = ERR_EOF;
ghc_errstr = "";
return EOF;
if ( FILEOBJ_WRITEABLE(fo) && FILEOBJ_JUST_WRITTEN(fo) && FILEOBJ_NEEDS_FLUSHING(fo) ) {
rc = flushBuffer(ptr);
if (rc < 0) return rc;
}
/* Try to read a character */
while ((c = getc((FILE *) fp)) == EOF && errno == EINTR)
clearerr((FILE *) fp);
fo->flags = (fo->flags & ~FILEOBJ_RW_WRITE) | FILEOBJ_RW_READ;
if (feof((FILE *) fp)) {
if ( FILEOBJ_IS_EOF(fo) ) {
ghc_errtype = ERR_EOF;
ghc_errstr = "";
} else if (c == EOF) {
cvtErrno();
stdErrno();
return -1;
}
if ( FILEOBJ_BUFFER_EMPTY(fo) ) {
;
} else if ( FILEOBJ_UNBUFFERED(fo) && !FILEOBJ_HAS_PUSHBACKS(fo) ) {
;
} else if ( FILEOBJ_UNBUFFERED(fo) ) { /* Unbuffered stream has pushbacks, retrieve them */
c=((unsigned char*)(fo->buf))[fo->bufRPtr++];
return (int)c;
} else {
c=((unsigned char*)(fo->buf))[fo->bufRPtr];
fo->bufRPtr++;
return (int)c;
}
/* Nothing in the buffer, go out and fetch a byte for our customer,
filling up the buffer if needs be.
*/
if ( FILEOBJ_UNBUFFERED(fo) ) {
return (readChar(ptr));
} else if ( FILEOBJ_LINEBUFFERED(fo) ) {
/* if input stream is connect to an output stream, flush it first */
if ( fo->connectedTo != NULL &&
fo->connectedTo->fd != -1 &&
(fo->connectedTo->flags & FILEOBJ_WRITE) ) {
rc = flushFile((StgForeignObj)fo->connectedTo);
}
if (rc < 0) return rc;
rc = fill_up_line_buffer(fo);
if (rc < 0) return rc;
c=((unsigned char*)(fo->buf))[fo->bufRPtr];
fo->bufRPtr++;
return (int)c;
} else { /* Fully-buffered */
rc = readBlock(ptr);
if (rc < 0) return rc;
c=((unsigned char*)(fo->buf))[fo->bufRPtr];
fo->bufRPtr++;
return (int)c;
}
return c;
}
\end{code}
......@@ -9,19 +9,89 @@
#include "stgio.h"
StgInt
fileLookAhead(fp)
StgForeignObj fp;
fileLookAhead(ptr)
StgForeignObj ptr;
{
int c;
if ((c = fileGetc(fp)) == EOF) {
return c;
} else if (ungetc(c, (FILE *) fp) == EOF) {
cvtErrno();
stdErrno();
return EOF;
} else
return c;
IOFileObject* fo = (IOFileObject*)ptr;
int c, rc;
#if 0
fprintf(stderr, "flh: %d %d %d\n",fo->bufRPtr, fo->bufWPtr, fo->flags);
#endif
/*
* fileLookahead reads the next character (hopefully from the buffer),
* before putting it back and returning the char.
*
*/
if ( FILEOBJ_IS_EOF(fo) ) {
ghc_errtype = ERR_EOF;
ghc_errstr = "";
return -1;
}
if ( (c = fileGetc(ptr)) < 0 ) {
return c;
}
rc = ungetChar(ptr,c);
if ( rc < 0 ) {
return rc;
} else {
return c;
}
}
StgInt
ungetChar(ptr,c)
StgForeignObj ptr;
StgInt c;
{
IOFileObject* fo = (IOFileObject*)ptr;
int rc = 0, sz = 0;
#if 0
fprintf(stderr, "ug: %d %d %c\n",fo->bufRPtr, fo->bufWPtr,(char)c, fo->flags);
#endif
/* Sanity check */
if ( !FILEOBJ_READABLE(fo) ) {
ghc_errno = GHC_EINVAL;
ghc_errstr = "object not readable";
return -1;
}
/* For an unbuffered file object, we lazily
allocate a pushback buffer. The sizeof the pushback
buffer is (globally) configurable.
*/
sz = getPushbackBufSize();
if ( FILEOBJ_UNBUFFERED(fo) && fo->buf==NULL && sz > 0 ) {
if ((fo->buf = malloc(sz*sizeof(char))) == NULL ) {
return -1;
}
fo->bufSize = sz;
((unsigned char*)fo->buf)[sz-1]=(unsigned char)c;
fo->bufWPtr = sz; /* Points one past the end of pushback buffer */
fo->bufRPtr = sz-1; /* points to current char. */
return 0;
}
if ( fo->bufWPtr > 0 && fo->bufRPtr > 0 ) {
fo->bufRPtr -= 1;
((unsigned char*)fo->buf)[fo->bufRPtr]=(unsigned char)c;
return 0;
} else if ( fo->buf != NULL &&
fo->bufSize > 0 &&
fo->bufWPtr == 0 &&
fo->bufRPtr==0 ) { /* empty buffer waiting to be filled up */
fo->bufRPtr=fo->bufSize-1;
((unsigned char*)fo->buf)[fo->bufRPtr]=(unsigned char)c;
fo->bufWPtr=fo->bufSize;
return 0;
} else {
return -1;
}
}
\end{code}
#ifndef FILEOBJECT_H
#define FILEOBJECT_H
/* a good idea? */
#include <stdio.h>
/*
IOFileObjects are used as part of the IO.Handle
implementation, ensuring that when handles are
finalised, buffers are flushed and FILE* objects
are closed (we really should be using file descriptors
here..)
*/
typedef struct _IOFileObject {
int fd;
void* buf;
int bufWPtr; /* points to next position to write,
bufRPtr >= bufWPtr <= bufSize.
For read-only files, bufWPtr = bufSize
bufWPtr = 0 => buffer is empty.
*/
int bufRPtr; /* points to the next char to read
-1 >= bufRPtr <= bufWPtr
For write-only files, bufRPtr = 0
bufRPtr == -1 => buffer is empty.
*/
int bufSize;
int flags;
struct _IOFileObject* connectedTo;
} IOFileObject;
#define FILEOBJ_FLUSH 1
#define FILEOBJ_LB 2
#define FILEOBJ_BB 4
#define FILEOBJ_EOF 8
#define FILEOBJ_READ 16
#define FILEOBJ_WRITE 32
#define FILEOBJ_STD 64
#define FILEOBJ_NONBLOCKING_IO 128
/* The next two flags are used for RW file objects only.
They indicate whether the last operation was a read or a write.
(Need this info to determine whether a RW file object's
buffer should be flushed before doing a subsequent
read or write).
*/
#define FILEOBJ_RW_READ 256
#define FILEOBJ_RW_WRITE 512
#define FILEOBJ_IS_EOF(x) ((x)->flags & FILEOBJ_EOF)
#define FILEOBJ_SET_EOF(x) ((x)->flags |= FILEOBJ_EOF)
#define FILEOBJ_CLEAR_EOF(x) ((x)->flags &= ~FILEOBJ_EOF)
#define FILEOBJ_CLEAR_ERR(x) FILEOBJ_CLEAR_EOF(x)
#define FILEOBJ_BLOCKED_READ -5
#define FILEOBJ_BLOCKED_WRITE -6
#define FILEOBJ_BLOCKED_CONN_WRITE -7
#define FILEOBJ_UNBUFFERED(x) (!((x)->flags & FILEOBJ_LB) && !((x)->flags & FILEOBJ_BB))
#define FILEOBJ_LINEBUFFERED(x) ((x)->flags & FILEOBJ_LB)
#define FILEOBJ_BLOCKBUFFERED(x) ((x)->flags & FILEOBJ_BB)
#define FILEOBJ_BUFFER_FULL(x) ((x)->bufWPtr >= (x)->bufSize)
#define FILEOBJ_BUFFER_EMPTY(x) ((x)->bufRPtr == (x)->bufWPtr)
#define FILEOBJ_HAS_PUSHBACKS(x) ((x)->buf != NULL && (x)->bufRPtr >= 0 && (x)->bufRPtr < (x)->bufWPtr)
#define FILEOBJ_READABLE(x) ((x)->flags & FILEOBJ_READ)
#define FILEOBJ_WRITEABLE(x) ((x)->flags & FILEOBJ_WRITE)
#define FILEOBJ_JUST_READ(x) ((x)->flags & FILEOBJ_RW_READ)
#define FILEOBJ_JUST_WRITTEN(x) ((x)->flags & FILEOBJ_RW_WRITE)
#define FILEOBJ_NEEDS_FLUSHING(x) (!FILEOBJ_BUFFER_EMPTY(x))
#define FILEOBJ_RW(x) (FILEOBJ_READABLE(x) && FILEOBJ_WRITEABLE(x))
#endif /* FILEOBJECT_H */
%
% (c) The GRASP/AQUA Project, Glasgow University, 1998
%
\subsection[fileObject.lc]{Managing file objects}
\begin{code}
#include "rtsdefs.h"
#include "stgio.h"
#include "fileObject.h"
void
setBufFlags(fo, flg)
StgForeignObj fo;
StgInt flg;
{
((IOFileObject*)fo)->flags = flg;
return;
}
void
setBufWPtr(fo, len)
StgForeignObj fo;
StgInt len;
{
((IOFileObject*)fo)->bufWPtr = len;
return;
}
StgInt
getBufWPtr(fo)
StgForeignObj fo;
{
return (((IOFileObject*)fo)->bufWPtr);
}
StgInt
getBufSize(fo)
StgForeignObj fo;
{
return (((IOFileObject*)fo)->bufSize);
}
void
setBuf(fo, buf,sz)
StgForeignObj fo;
StgAddr buf;
StgInt sz;
{
((IOFileObject*)fo)->buf = buf;
((IOFileObject*)fo)->bufSize = sz;
return;
}
StgAddr
getBuf(fo)
StgForeignObj fo;
{ return (((IOFileObject*)fo)->buf); }
StgAddr
getWriteableBuf(ptr)
StgForeignObj ptr;
{
/* getWriteableBuf() is called prior to starting to pack
a Haskell string into the IOFileObject buffer. It takes
care of flushing the (input) buffer in the case we're
dealing with a RW handle.
*/
IOFileObject* fo = (IOFileObject*)ptr;
if ( FILEOBJ_READABLE(fo) && FILEOBJ_JUST_READ(fo) ) {
flushReadBuffer(ptr); /* ignoring return code */
/* Ahead of time really, but indicate that we're (just about to) write */
}
fo->flags = (fo->flags & ~FILEOBJ_RW_READ) | FILEOBJ_RW_WRITE;
return (fo->buf);
}
StgAddr
getBufStart(fo,count)
StgForeignObj fo;
StgInt count;
{ return ((char*)((IOFileObject*)fo)->buf + (((IOFileObject*)fo)->bufRPtr) - count); }
StgInt
getFileFd(fo)
StgForeignObj fo;
{ return (((IOFileObject*)fo)->fd); }
StgInt
getConnFileFd(fo)
StgForeignObj fo;
{ return (((IOFileObject*)fo)->connectedTo->fd); }
void
setFd(fo,fp)
StgForeignObj fo;
StgInt fp;
{ ((IOFileObject*)fo)->fd = fp;
return;
}
void
setConnectedTo(fo, fw, flg)
StgForeignObj fo;
StgForeignObj fw;
StgInt flg;
{
if( flg && (! isatty(((IOFileObject*)fo)->fd) || !isatty(((IOFileObject*)fw)->fd)) ) {
return;
}
((IOFileObject*)fo)->connectedTo = (IOFileObject*)fw;
return;
}
static int __pushback_buf_size__ = 2;
void
setPushbackBufSize(i)
StgInt i;
{ __pushback_buf_size__ = (i > 0 ? i : 0); }
StgInt
getPushbackBufSize()
{ return (__pushback_buf_size__); }
void
clearNonBlockingIOFlag__ (ptr)
StgForeignObj ptr;
{ ((IOFileObject*)ptr)->flags &= ~FILEOBJ_NONBLOCKING_IO; }
void
setNonBlockingIOFlag__ (ptr)
StgForeignObj ptr;
{ ((IOFileObject*)ptr)->flags |= FILEOBJ_NONBLOCKING_IO; }
void
clearConnNonBlockingIOFlag__ (ptr)