[project @ 1998-11-26 09:17:22 by sof]
[ghc-hetmet.git] / ghc / lib / std / cbits / filePutc.lc
index 980aa63..cf9ffe1 100644 (file)
@@ -9,24 +9,79 @@
 #include "stgio.h"
 #include "error.h"
 
+#define TERMINATE_LINE(x)   ((x) == '\n')
+
 StgInt
-filePutc(fp, c)
-StgForeignObj fp;
-StgInt c;
+filePutc(ptr, c)
+StgForeignObj ptr;
+StgChar c;
 {
-    int rc;
+    IOFileObject* fo = (IOFileObject*)ptr;
+    int rc = 0;
+
+    /* What filePutc needs to do:
+
+         - if there's no buffering => write it out.
+        - if the buffer is line-buffered
+               write out buffer (+char), iff buffer would be full afterwards ||
+                                             new char is the newline character
+               add to buffer , otherwise
+         - if the buffer is fully-buffered
+              write out buffer (+char), iff adding char fills up buffer.
+              add char to buffer, otherwise.
+
+     In the cases where a file is buffered, the invariant is that operations
+     that fill up a buffer also flushes them. A consequence of this here, is 
+     that we're guaranteed to be passed a buffer with space for (at least)
+     the one char we're adding.
 
-    /* Try to write a character */
-    while ((rc = putc((int) c, (FILE *) fp)) == EOF && errno == EINTR)
-       clearerr((FILE *) fp);
+     Supporting RW objects adds yet another twist, since we have to make
+     sure that if such objects have been read from just previously, we
+     flush(i.e., empty) the buffer first. (We could be smarter about this,
+     but aren't!)
+
+    */
+
+    if ( FILEOBJ_READABLE(fo) && FILEOBJ_JUST_READ(fo) ) {
+        rc = flushReadBuffer(ptr);
+        if (rc<0) return rc;
+    }
 
-    if (rc == EOF) {
+    fo->flags = (fo->flags & ~FILEOBJ_RW_READ) | FILEOBJ_RW_WRITE;
+             
+   /* check whether we can just add it to the buffer.. */
+    if ( FILEOBJ_UNBUFFERED(fo) ) {
+        ; 
+    } else {
+       /* We're buffered, add it to the pack */
+       ((char*)fo->buf)[fo->bufWPtr] = (char)c;
+       fo->bufWPtr++;
+      /* If the buffer filled up as a result, *or*
+         the added character terminated a line
+            => flush.
+      */
+      if ( FILEOBJ_BUFFER_FULL(fo) || 
+           (FILEOBJ_LINEBUFFERED(fo) && TERMINATE_LINE(c)) ) {
+        rc = writeBuffer(ptr, fo->bufWPtr);
+       /* Undo the write if we're blocking..*/
+       if (rc == FILEOBJ_BLOCKED_WRITE ) fo->bufWPtr--;
+      }
+      return rc;
+    }
+
+    if ( fo->flags & FILEOBJ_NONBLOCKING_IO && inputReady(ptr,0) != 1 )
+      return FILEOBJ_BLOCKED_WRITE;
+
+    /* Unbuffered, write the character directly. */
+    while ((rc = write(fo->fd, &c, 1)) == 0 && errno == EINTR) ;
+
+    if (rc == 0) {
        cvtErrno();
        stdErrno();
        return -1;
     }
-
     return 0;
+
 }
 
 \end{code}