/* 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;
}
