[project @ 2001-05-18 14:18:34 by simonmar]
[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.12 2000/08/07 23:37:23 qrczak Exp $
5  *
6  * hPutChar Runtime Support
7  */
8
9 #include "Rts.h"
10 #include "stgio.h"
11
12 #if defined(HAVE_WINSOCK_H) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
13 #define USE_WINSOCK
14 #endif
15
16 #ifdef USE_WINSOCK
17 #include <winsock.h>
18 #endif
19
20 #define TERMINATE_LINE(x)   ((x) == '\n')
21
22 StgInt
23 filePutc(StgForeignPtr ptr, StgChar c)
24 {
25     IOFileObject* fo = (IOFileObject*)ptr;
26     int rc = 0;
27     unsigned char byte = (unsigned char) c;
28
29     /* What filePutc needs to do:
30
31          - if there's no buffering => write it out.
32          - if the buffer is line-buffered
33                 write out buffer (+char), iff buffer would be full afterwards ||
34                                               new char is the newline character
35                 add to buffer , otherwise
36          - if the buffer is fully-buffered
37                write out buffer (+char), iff adding char fills up buffer.
38                add char to buffer, otherwise.
39
40      In the cases where a file is buffered, the invariant is that operations
41      that fill up a buffer also flushes them. A consequence of this here, is 
42      that we're guaranteed to be passed a buffer with space for (at least)
43      the one char we're adding.
44
45      Supporting RW objects adds yet another twist, since we have to make
46      sure that if such objects have been read from just previously, we
47      flush(i.e., empty) the buffer first. (We could be smarter about this,
48      but aren't!)
49
50      Only the lower 8 bits of a character are written. The data are supposed
51      to be already converted to the stream's 8-bit encoding.
52
53     */
54
55     if ( FILEOBJ_READABLE(fo) && FILEOBJ_JUST_READ(fo) ) {
56         rc = flushReadBuffer(ptr);
57         if (rc<0) return rc;
58     }
59
60     fo->flags = (fo->flags & ~FILEOBJ_RW_READ) | FILEOBJ_RW_WRITE;
61               
62     /* check whether we can just add it to the buffer.. */
63     if ( FILEOBJ_UNBUFFERED(fo) ) {
64         ; 
65     } else {
66         /* We're buffered, add it to the pack */
67        ((unsigned char*)fo->buf)[fo->bufWPtr] = byte;
68        fo->bufWPtr++;
69       /* If the buffer filled up as a result, *or*
70          the added character terminated a line
71             => flush.
72       */
73       if ( FILEOBJ_BUFFER_FULL(fo) || 
74            (FILEOBJ_LINEBUFFERED(fo) && TERMINATE_LINE(c)) ) {
75         rc = writeBuffer(ptr, fo->bufWPtr);
76         /* Undo the write if we're blocking..*/
77         if (rc == FILEOBJ_BLOCKED_WRITE ) fo->bufWPtr--;
78       }
79       return rc;
80     }
81
82     /* Unbuffered, write the character directly. */
83     while ((rc = (
84 #ifdef USE_WINSOCK
85                  fo->flags & FILEOBJ_WINSOCK ?
86                  send(fo->fd, &byte, 1, 0) :
87                  write(fo->fd, &byte, 1))) <= 0) {
88 #else
89                  write(fo->fd, &byte, 1))) <= 0) {
90 #endif
91
92         if ( rc == -1 && errno == EAGAIN) {
93             errno = 0;
94             return FILEOBJ_BLOCKED_WRITE;
95         } else if (rc == 0 || (rc == -1 && errno != EINTR)) {
96             cvtErrno();
97             stdErrno();
98             return -1;
99         }
100     }
101
102     return 0;
103 }