[project @ 2001-05-18 14:18:34 by simonmar]
[ghc-hetmet.git] / ghc / lib / std / cbits / writeFile.c
1 /* 
2  * (c) The GRASP/AQUA Project, Glasgow University, 1994-1998
3  *
4  * $Id: writeFile.c,v 1.14 2000/04/12 17:33:16 simonmar Exp $
5  *
6  * hPutStr 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 StgInt
21 writeFileObject(StgForeignPtr ptr, StgInt bytes)
22 {
23     int rc=0;
24     IOFileObject* fo = (IOFileObject*)ptr;
25
26     /* If we've got a r/w file object in our hand, flush the
27        (input) buffer contents first.
28     */
29     if ( FILEOBJ_READABLE(fo) && FILEOBJ_JUST_READ(fo) ) {
30        fo->flags = (fo->flags & ~FILEOBJ_RW_READ) | FILEOBJ_RW_WRITE;
31        rc = flushReadBuffer(ptr);
32        if (rc < 0) return rc;
33     }
34
35     return (writeBuffer(ptr, bytes));
36 }
37
38 StgInt
39 writeBuffer(StgForeignPtr ptr, StgInt bytes)
40 {
41     int count;
42     IOFileObject* fo = (IOFileObject*)ptr;
43
44     char *pBuf = (char *) fo->buf + fo->bufRPtr;
45
46     bytes -= fo->bufRPtr;
47
48     /* Disallow short writes */
49     if (bytes == 0  || fo->buf == NULL) {
50         fo->bufRPtr = 0;
51         return 0;
52     }
53
54     while ((count = 
55                (
56 #ifdef USE_WINSOCK
57                  fo->flags & FILEOBJ_WINSOCK ?
58                  send(fo->fd,  pBuf, bytes, 0) :
59                  write(fo->fd, pBuf, bytes))) < bytes) {
60 #else
61                  write(fo->fd, pBuf, bytes))) < bytes) {
62 #endif
63         if ( count == -1 && errno == EAGAIN) {
64             errno = 0;
65             return FILEOBJ_BLOCKED_WRITE;
66         }
67         else if ( count == -1 && errno != EINTR ) {
68             cvtErrno();
69             stdErrno();
70             return -1;
71         }
72         else {
73             bytes -= count;
74             pBuf  += count;
75             fo->bufRPtr += count;
76         }
77     }
78     /* Signal that we've emptied the buffer */
79     fo->bufRPtr = 0;
80     fo->bufWPtr = 0;
81     return 0;
82 }
83
84
85 /* ToDo: there's currently no way for writeBuf to return both a
86  * partial write and an indication that the write blocked.  It needs
87  * two calls: one to get the partial result, and the next one to block.
88  * This matches Unix write/2, but is rather a waste.
89  */
90
91 StgInt
92 writeBuf(StgForeignPtr ptr, StgAddr buf, StgInt off, StgInt len)
93 {
94     IOFileObject* fo = (IOFileObject*)ptr;
95     int count, total_count;
96     int rc = 0;
97     char *pBuf = (char *) buf+off;
98
99     if (len == 0)
100         return 0;
101
102     /* First of all, check if we do need to flush the buffer .. */
103     /* Note - in the case of line buffering, we do not currently check
104        whether we need to flush buffer due to line terminators in the
105        buffer we're outputting */
106     if ( fo->buf != NULL                     &&   /* buffered and */
107          (fo->bufWPtr + len < (fo->bufSize))      /* there's room */
108        ) {
109        /* Block copying is likely to be cheaper than flush, followed by write */
110        memcpy(((char*)fo->buf + fo->bufWPtr), pBuf, len);
111        fo->bufWPtr += len;
112        return len;
113     }
114     /* If we do overflow, flush current contents of the buffer and
115        directly output the chunk.
116        (no attempt at splitting up the chunk is currently made)
117     */       
118     if ( fo->buf != NULL                     &&    /* buffered and */
119          (fo->bufWPtr + len >= (fo->bufSize))       /* there's not room */
120        ) {
121        /* Flush buffer */
122        rc = writeFileObject(ptr, fo->bufWPtr);
123        /* ToDo: undo buffer fill if we're blocking.. */
124        if (rc != 0) { 
125            return rc;
126        }
127     }
128
129     total_count = 0;
130
131     while ((count = 
132                (
133 #ifdef USE_WINSOCK
134                  fo->flags & FILEOBJ_WINSOCK ?
135                  send(fo->fd,  pBuf, (int)len, 0) :
136                  write(fo->fd, pBuf, (int)len))) < len ) {
137 #else
138                  write(fo->fd, pBuf, (int)len))) < len ) {
139 #endif
140         if ( count >= 0 ) {
141             len -= count;
142             pBuf += count;
143             total_count += count;
144             continue;
145         } else if ( errno == EAGAIN ) {
146             errno = 0;
147             if (total_count > 0)
148                 return total_count; /* partial write */
149             else
150                 return FILEOBJ_BLOCKED_WRITE;
151         } else if ( errno != EINTR ) {
152             cvtErrno();
153             stdErrno();
154             return -1;
155         }
156     }
157
158     total_count += count;
159     return total_count;
160 }
161
162 StgInt
163 writeBufBA(StgForeignPtr ptr, StgByteArray buf, StgInt off, StgInt len)
164
165     return (writeBuf(ptr,(StgAddr)buf, off, len)); 
166 }
167
168 /* -----------------------------------------------------------------------------
169  * write_  is just a simple wrapper around write/2 that restarts
170  * on EINTR and returns FILEOBJ_BLOCKED_WRITE on EAGAIN.
171  * -------------------------------------------------------------------------- */
172
173 StgInt
174 write_(StgForeignPtr ptr, StgAddr buf, StgInt len)
175 {
176     IOFileObject* fo = (IOFileObject*)ptr;
177     int rc;
178
179     while ((rc = 
180                (
181 #ifdef USE_WINSOCK
182                  fo->flags & FILEOBJ_WINSOCK ?
183                  send(fo->fd,  buf, (int)len, 0) :
184                  write(fo->fd, buf, (int)len))) < 0 ) {
185 #else
186                  write(fo->fd, buf, (int)len))) < 0 ) {
187 #endif
188         if ( errno == EAGAIN ) {
189             errno = 0;
190             return FILEOBJ_BLOCKED_WRITE;
191         } else if ( errno != EINTR ) {
192             cvtErrno();
193             stdErrno();
194             return -1;
195         }
196     }
197     return rc;
198 }