[project @ 1998-12-02 13:17:09 by simonm]
[ghc-hetmet.git] / ghc / lib / std / cbits / filePutc.c
1 /* 
2  * (c) The GRASP/AQUA Project, Glasgow University, 1994-1998
3  *
4  * $Id: filePutc.c,v 1.3 1998/12/02 13:27:29 simonm Exp $
5  *
6  * hPutChar Runtime Support
7  */
8
9 #include "Rts.h"
10 #include "stgio.h"
11 #include "error.h"
12
13 #define TERMINATE_LINE(x)   ((x) == '\n')
14
15 StgInt
16 filePutc(ptr, c)
17 StgForeignPtr ptr;
18 StgChar c;
19 {
20     IOFileObject* fo = (IOFileObject*)ptr;
21     int rc = 0;
22
23     /* What filePutc needs to do:
24
25          - if there's no buffering => write it out.
26          - if the buffer is line-buffered
27                 write out buffer (+char), iff buffer would be full afterwards ||
28                                               new char is the newline character
29                 add to buffer , otherwise
30          - if the buffer is fully-buffered
31                write out buffer (+char), iff adding char fills up buffer.
32                add char to buffer, otherwise.
33
34      In the cases where a file is buffered, the invariant is that operations
35      that fill up a buffer also flushes them. A consequence of this here, is 
36      that we're guaranteed to be passed a buffer with space for (at least)
37      the one char we're adding.
38
39      Supporting RW objects adds yet another twist, since we have to make
40      sure that if such objects have been read from just previously, we
41      flush(i.e., empty) the buffer first. (We could be smarter about this,
42      but aren't!)
43
44     */
45
46     if ( FILEOBJ_READABLE(fo) && FILEOBJ_JUST_READ(fo) ) {
47         rc = flushReadBuffer(ptr);
48         if (rc<0) return rc;
49     }
50
51     fo->flags = (fo->flags & ~FILEOBJ_RW_READ) | FILEOBJ_RW_WRITE;
52               
53     /* check whether we can just add it to the buffer.. */
54     if ( FILEOBJ_UNBUFFERED(fo) ) {
55         ; 
56     } else {
57         /* We're buffered, add it to the pack */
58        ((char*)fo->buf)[fo->bufWPtr] = (char)c;
59        fo->bufWPtr++;
60       /* If the buffer filled up as a result, *or*
61          the added character terminated a line
62             => flush.
63       */
64       if ( FILEOBJ_BUFFER_FULL(fo) || 
65            (FILEOBJ_LINEBUFFERED(fo) && TERMINATE_LINE(c)) ) {
66         rc = writeBuffer(ptr, fo->bufWPtr);
67         /* Undo the write if we're blocking..*/
68         if (rc == FILEOBJ_BLOCKED_WRITE ) fo->bufWPtr--;
69       }
70       return rc;
71     }
72
73     if ( fo->flags & FILEOBJ_NONBLOCKING_IO && inputReady(ptr,0) != 1 )
74       return FILEOBJ_BLOCKED_WRITE;
75
76     /* Unbuffered, write the character directly. */
77     while ((rc = write(fo->fd, &c, 1)) == 0 && errno == EINTR) ;
78
79     if (rc == 0) {
80         cvtErrno();
81         stdErrno();
82         return -1;
83     }
84     return 0;
85
86 }