[project @ 1999-11-25 16:54:14 by simonmar]
[ghc-hetmet.git] / ghc / lib / std / cbits / seekFile.c
index 2946fe2..1a9ec62 100644 (file)
@@ -1,7 +1,7 @@
 /* 
  * (c) The GRASP/AQUA Project, Glasgow University, 1994-1998
  *
- * $Id: seekFile.c,v 1.1 1998/04/10 10:54:49 simonm Exp $
+ * $Id: seekFile.c,v 1.4 1999/09/19 19:25:24 sof Exp $
  *
  * hSeek and hIsSeekable Runtime Support
  */
 #include <sys/stat.h>
 #endif
 
+/* Invoked by IO.hSeek only */
 StgInt
-seekFile(StgAddr fp, StgInt whence, StgInt size, StgByteArray d)
+seekFile(ptr, whence, size, d)
+StgForeignPtr ptr;
+StgInt whence;
+StgInt size;
+StgByteArray d;
 {
+    IOFileObject* fo = (IOFileObject*)ptr;
     struct stat sb;
-    long int offset;
+    off_t offset;
+    int posn_delta =0;
+    int rc = 0;
+
+    switch (whence) {
+     case 0:  whence=SEEK_SET; break;
+     case 1:  whence=SEEK_CUR; break;
+     case 2:  whence=SEEK_END; break;
+     default: whence=SEEK_SET; /* Should never happen, really */
+    }
 
     /*
      * We need to snatch the offset out of an MP_INT.  The bits are there sans sign,
@@ -45,8 +60,35 @@ seekFile(StgAddr fp, StgInt whence, StgInt size, StgByteArray d)
        return -1;
     }
 
-    /* Try to find out the file type & size for a physical file */
-    while (fstat(fileno((FILE *) fp), &sb) < 0) {
+    /* If we're doing a relative seek, see if we cannot deal 
+     * with the request without flushing the buffer..
+     *
+     * Note: the wording in the report is vague here, but 
+     * we only avoid flushing on *input* buffers and *not* output ones.
+     */
+    if ( whence == SEEK_CUR &&
+        (FILEOBJ_READABLE(fo) && !FILEOBJ_WRITEABLE(fo) &&
+         (fo->bufRPtr + (int)offset) < fo->bufWPtr &&
+         (fo->bufRPtr + (int)offset) >= 0) ) { /* The input buffer case */
+       fo->bufRPtr += (int)offset;
+       return 0;
+    } else if ( whence == SEEK_CUR && (FILEOBJ_READABLE(fo) && !FILEOBJ_WRITEABLE(fo)) ) {
+         /* We're seeking outside the input buffer,
+           record delta so that we can adjust the file position
+           reported from the underlying fd to get
+           at the real position we're at when we take into account
+           buffering.
+        */
+       posn_delta = fo->bufWPtr - fo->bufRPtr;  /* number of chars left in the buffer */
+        if (posn_delta < 0) posn_delta=0;
+    }
+
+    /* If we cannot seek within our current buffer, flush it. */
+    rc = flushBuffer(ptr);
+    if (rc < 0) return rc;
+
+    /* Try to find out the file type */
+    while (fstat(fo->fd, &sb) < 0) {
        /* highly unlikely */
        if (errno != EINTR) {
            cvtErrno();
@@ -54,60 +96,105 @@ seekFile(StgAddr fp, StgInt whence, StgInt size, StgByteArray d)
            return -1;
        }
     }
-    if (S_ISREG(sb.st_mode)) {
-       /* Verify that we are not seeking beyond end-of-file */
-       int posn;
-
-       switch (whence) {
-       case SEEK_SET:
-           posn = offset;
-           break;
-       case SEEK_CUR:
-           while ((posn = ftell((FILE *) fp)) == -1) {
-               /* the possibility seems awfully remote */
-               if (errno != EINTR) {
-                   cvtErrno();
-                   stdErrno();
-                   return -1;
-               }
-           }
-           posn += offset;
-           break;
-       case SEEK_END:
-           posn = sb.st_size + offset;
-           break;
+    if (S_ISFIFO(sb.st_mode)) {
+       ghc_errtype = ERR_UNSUPPORTEDOPERATION;
+       ghc_errstr = "can't seek on a pipe";
+       return -1;
+    }
+    while ( lseek(fo->fd, offset, whence) == -1) {
+       if (errno != EINTR) {
+           cvtErrno();
+           stdErrno();
+           return -1;
        }
-       if (posn > sb.st_size) {
-           ghc_errtype = ERR_INVALIDARGUMENT;
-           ghc_errstr = "seek position beyond end of file";
+    }
+    /* Clear EOF */
+    FILEOBJ_CLEAR_EOF(fo);
+    return 0;
+}
+
+/* Invoked by IO.hSeek only */
+StgInt
+seekFile_int64(ptr, whence, d)
+StgForeignPtr ptr;
+StgInt whence;
+StgInt64 d;
+{
+    IOFileObject* fo = (IOFileObject*)ptr;
+    struct stat sb;
+    off_t offset = d;
+    int posn_delta =0;
+    int rc = 0;
+
+    switch (whence) {
+     case 0: whence=SEEK_SET; break;
+     case 1: whence=SEEK_CUR; break;
+     case 2: whence=SEEK_END; break;
+     default: whence=SEEK_SET; break; /* Should never happen, really */
+    }
+
+    /* If we're doing a relative seek, see if we cannot deal 
+     * with the request without flushing the buffer..
+     *
+     * Note: the wording in the report is vague here, but 
+     * we only avoid flushing on *input* buffers and *not* output ones.
+     */
+    if ( whence == SEEK_CUR &&
+        (FILEOBJ_READABLE(fo) && !FILEOBJ_WRITEABLE(fo) &&
+         (fo->bufRPtr + (int)offset) < fo->bufWPtr &&
+         (fo->bufRPtr + (int)offset) >= 0) ) { /* The input buffer case */
+       fo->bufRPtr += (int)offset;
+       return 0;
+    } else if ( whence == SEEK_CUR && (FILEOBJ_READABLE(fo) && !FILEOBJ_WRITEABLE(fo)) ) {
+         /* We're seeking outside the input buffer,
+           record delta so that we can adjust the file position
+           reported from the underlying fd to get
+           at the real position we're at when we take into account
+           buffering.
+        */
+       posn_delta = fo->bufWPtr - fo->bufRPtr;  /* number of chars left in the buffer */
+        if (posn_delta < 0) posn_delta=0;
+    }
+
+    /* If we cannot seek within our current buffer, flush it. */
+    rc = flushBuffer(ptr);
+    if (rc < 0) return rc;
+
+    /* Try to find out the file type & size for a physical file */
+    while (fstat(fo->fd, &sb) < 0) {
+       /* highly unlikely */
+       if (errno != EINTR) {
+           cvtErrno();
+           stdErrno();
            return -1;
        }
-    } else if (S_ISFIFO(sb.st_mode)) {
+    }
+    if (S_ISFIFO(sb.st_mode)) {
        ghc_errtype = ERR_UNSUPPORTEDOPERATION;
        ghc_errstr = "can't seek on a pipe";
        return -1;
-    } else {
-        ghc_errtype = ERR_UNSUPPORTEDOPERATION;
-        ghc_errstr = "can't seek on a device";
-        return -1;
     }
-    while (fseek((FILE *) fp, offset, whence) != 0) {
+    while ( lseek(fo->fd, offset, whence) == -1) {
        if (errno != EINTR) {
            cvtErrno();
            stdErrno();
            return -1;
        }
     }
+    /* Clear EOF */
+    FILEOBJ_CLEAR_EOF(fo);
     return 0;
 }
 
 StgInt
-seekFileP(StgAddr fp)
+seekFileP(ptr)
+StgForeignPtr ptr;
 {
+    IOFileObject* fo = (IOFileObject*)ptr;
     struct stat sb;
 
     /* Try to find out the file type */
-    while (fstat(fileno((FILE *) fp), &sb) < 0) {
+    while (fstat(fo->fd, &sb) < 0) {
        /* highly unlikely */
        if (errno != EINTR) {
            cvtErrno();
@@ -115,12 +202,12 @@ seekFileP(StgAddr fp)
            return -1;
        }
     }
-    /* Regular files are okay */
-    if (S_ISREG(sb.st_mode)) {
-       return 1;
+    /* Pipes are not okay.. */
+    if (S_ISFIFO(sb.st_mode)) {
+       return 0;
     } 
-    /* For now, everything else is not */
+    /* ..for now, everything else is */
     else {
-       return 0;
+       return 1;
     }
 }