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