| /* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> |
| * |
| * GNU Library General Public License (LGPL) version 2 or later. |
| * |
| * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. |
| */ |
| |
| #include "_stdio.h" |
| |
| |
| /* Given a writing stream with no buffered output, write the |
| * data in 'buf' (which may be the stream's bufstart) of size |
| * 'bufsize' to the stream. If a write error occurs, set the |
| * stream's error indicator and (if buffering) buffer as much |
| * data as possible (FBF) or only up to '\n' (LBF) to implement |
| * "as if fputc()" clause in the standard. |
| * |
| * Returns the number of bytes written and/or buffered. |
| * |
| * Notes: |
| * Calling with bufsize == 0 is permitted, and buf is ignored in |
| * that case. |
| * We implement fflush() by setting bufpos to bufstart and passing |
| * bufstart as the buf arg. If there is a write error, the |
| * unwritten buffered data will simply be moved to the beginning |
| * of the buffer. Since the data obviously fits in the buffer |
| * and since there will be no '\n' chars in the buffer in the LBF |
| * case, no data will be lost. |
| * NOT THREADSAFE! Assumes stream already locked if necessary. |
| */ |
| |
| size_t attribute_hidden __stdio_WRITE(register FILE *stream, |
| register const unsigned char *buf, size_t bufsize) |
| { |
| size_t todo; |
| ssize_t rv, stodo; |
| |
| __STDIO_STREAM_VALIDATE(stream); |
| assert(stream->__filedes >= -1); |
| assert(__STDIO_STREAM_IS_WRITING(stream)); |
| assert(!__STDIO_STREAM_BUFFER_WUSED(stream)); /* Buffer must be empty. */ |
| |
| todo = bufsize; |
| |
| while (todo != 0) { |
| stodo = (todo <= SSIZE_MAX) ? todo : SSIZE_MAX; |
| rv = __WRITE(stream, (char *) buf, stodo); |
| if (rv >= 0) { |
| #ifdef __UCLIBC_MJN3_ONLY__ |
| #warning TODO: Make custom stream write return check optional. |
| #endif |
| #ifdef __UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__ |
| assert(rv <= stodo); |
| if (rv > stodo) { /* Wrote more than stodo! */ |
| /* abort(); */ |
| } |
| #endif |
| todo -= rv; |
| buf += rv; |
| } else { |
| |
| __STDIO_STREAM_SET_ERROR(stream); |
| |
| /* We buffer data on "transient" errors, but discard it |
| * on "hard" ones. Example of a hard error: |
| * |
| * close(fileno(stdout)); |
| * printf("Hi there 1\n"); // EBADF |
| * dup2(good_fd, fileno(stdout)); |
| * printf("Hi there 2\n"); // buffers new data |
| * |
| * This program should not print "Hi there 1" to good_fd. |
| * The rationale is that the caller of writing operation |
| * should check for error and act on it. |
| * If he didn't, then future users of the stream |
| * have no idea what to do. |
| * It's least confusing to at least not burden them with |
| * some hidden buffered crap in the buffer. |
| */ |
| if (errno != EINTR && errno != EAGAIN) { |
| /* do we have other "soft" errors? */ |
| break; |
| } |
| #ifdef __STDIO_BUFFERS |
| stodo = __STDIO_STREAM_BUFFER_SIZE(stream); |
| if (stodo != 0) { |
| unsigned char *s; |
| |
| if (stodo > todo) { |
| stodo = todo; |
| } |
| |
| s = stream->__bufstart; |
| |
| do { |
| *s = *buf; |
| if ((*s == '\n') |
| && __STDIO_STREAM_IS_LBF(stream) |
| ) { |
| break; |
| } |
| ++s; |
| ++buf; |
| } while (--stodo); |
| |
| stream->__bufpos = s; |
| |
| todo -= (s - stream->__bufstart); |
| } |
| #endif /* __STDIO_BUFFERS */ |
| |
| bufsize -= todo; |
| break; |
| } |
| } |
| |
| __STDIO_STREAM_VALIDATE(stream); |
| return bufsize; |
| } |