1 /* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org>
2 *
3 * GNU Library General Public License (LGPL) version 2 or later.
4 *
5 * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details.
6 */
7
8 #include "_stdio.h"
9
10
11 /* Given a writing stream with no buffered output, write the
12 * data in 'buf' (which may be the stream's bufstart) of size
13 * 'bufsize' to the stream. If a write error occurs, set the
14 * stream's error indicator and (if buffering) buffer as much
15 * data as possible (FBF) or only up to '\n' (LBF) to implement
16 * "as if fputc()" clause in the standard.
17 *
18 * Returns the number of bytes written and/or buffered.
19 *
20 * Notes:
21 * Calling with bufsize == 0 is permitted, and buf is ignored in
22 * that case.
23 * We implement fflush() by setting bufpos to bufstart and passing
24 * bufstart as the buf arg. If there is a write error, the
25 * unwritten buffered data will simply be moved to the beginning
26 * of the buffer. Since the data obviously fits in the buffer
27 * and since there will be no '\n' chars in the buffer in the LBF
28 * case, no data will be lost.
29 * NOT THREADSAFE! Assumes stream already locked if necessary.
30 */
31
__stdio_WRITE(register FILE * stream,register const unsigned char * buf,size_t bufsize)32 size_t attribute_hidden __stdio_WRITE(register FILE *stream,
33 register const unsigned char *buf, size_t bufsize)
34 {
35 size_t todo;
36 ssize_t rv, stodo;
37
38 __STDIO_STREAM_VALIDATE(stream);
39 assert(stream->__filedes >= -1);
40 assert(__STDIO_STREAM_IS_WRITING(stream));
41 assert(!__STDIO_STREAM_BUFFER_WUSED(stream)); /* Buffer must be empty. */
42
43 todo = bufsize;
44
45 while (todo != 0) {
46 stodo = (todo <= SSIZE_MAX) ? todo : SSIZE_MAX;
47 rv = __WRITE(stream, (char *) buf, stodo);
48 if (rv >= 0) {
49 #ifdef __UCLIBC_MJN3_ONLY__
50 #warning TODO: Make custom stream write return check optional.
51 #endif
52 #ifdef __UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__
53 assert(rv <= stodo);
54 if (rv > stodo) { /* Wrote more than stodo! */
55 /* abort(); */
56 }
57 #endif
58 todo -= rv;
59 buf += rv;
60 } else {
61
62 __STDIO_STREAM_SET_ERROR(stream);
63
64 /* We buffer data on "transient" errors, but discard it
65 * on "hard" ones. Example of a hard error:
66 *
67 * close(fileno(stdout));
68 * printf("Hi there 1\n"); // EBADF
69 * dup2(good_fd, fileno(stdout));
70 * printf("Hi there 2\n"); // buffers new data
71 *
72 * This program should not print "Hi there 1" to good_fd.
73 * The rationale is that the caller of writing operation
74 * should check for error and act on it.
75 * If he didn't, then future users of the stream
76 * have no idea what to do.
77 * It's least confusing to at least not burden them with
78 * some hidden buffered crap in the buffer.
79 */
80 if (errno != EINTR && errno != EAGAIN) {
81 /* do we have other "soft" errors? */
82 break;
83 }
84 #ifdef __STDIO_BUFFERS
85 stodo = __STDIO_STREAM_BUFFER_SIZE(stream);
86 if (stodo != 0) {
87 unsigned char *s;
88
89 if (stodo > todo) {
90 stodo = todo;
91 }
92
93 s = stream->__bufstart;
94
95 do {
96 *s = *buf;
97 if ((*s == '\n')
98 && __STDIO_STREAM_IS_LBF(stream)
99 ) {
100 break;
101 }
102 ++s;
103 ++buf;
104 } while (--stodo);
105
106 stream->__bufpos = s;
107
108 todo -= (s - stream->__bufstart);
109 }
110 #endif /* __STDIO_BUFFERS */
111
112 bufsize -= todo;
113 break;
114 }
115 }
116
117 __STDIO_STREAM_VALIDATE(stream);
118 return bufsize;
119 }
120