/* 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"

#undef fputc
#undef fputc_unlocked
#undef putc
#undef putc_unlocked

libc_hidden_proto(__fputc_unlocked)

#ifdef __DO_UNLOCKED

int __fputc_unlocked(int c, register FILE *stream)
{
	__STDIO_STREAM_VALIDATE(stream);

	/* First the fast path.  We're good to go if putc macro enabled. */
	if (__STDIO_STREAM_CAN_USE_BUFFER_ADD(stream)) {
		__STDIO_STREAM_BUFFER_ADD(stream, ((unsigned char) c));
		return (unsigned char) c;
	}

	/* Next quickest... writing and narrow oriented, but macro
	 * disabled and/or buffer is full. */
	if (__STDIO_STREAM_IS_NARROW_WRITING(stream)
		|| !__STDIO_STREAM_TRANS_TO_WRITE(stream, __FLAG_NARROW)
		) {
		if (__STDIO_STREAM_IS_FAKE_VSNPRINTF(stream)) {
			return (unsigned char) c;
		}

		if (__STDIO_STREAM_BUFFER_SIZE(stream)) { /* Do we have a buffer? */
			/* The buffer is full and/or the stream is line buffered. */
			if (!__STDIO_STREAM_BUFFER_WAVAIL(stream) /* Buffer full? */
				&& __STDIO_COMMIT_WRITE_BUFFER(stream) /* Commit failed! */
				) {
				goto BAD;
			}
#ifdef __UCLIBC_MJN3_ONLY__
#warning CONSIDER: Should we fail if the commit fails but we now have room?
#endif

			__STDIO_STREAM_BUFFER_ADD(stream, ((unsigned char) c));

			if (__STDIO_STREAM_IS_LBF(stream)) {
				if ((((unsigned char) c) == '\n')
					&& __STDIO_COMMIT_WRITE_BUFFER(stream)) {
					/* Commit failed! */
					__STDIO_STREAM_BUFFER_UNADD(stream); /* Undo the write! */
					goto BAD;
				}
			}
		} else {
			/* NOTE: Do not try to save space by moving uc to the top of
			 * the file, as that dramaticly increases runtime. */
			unsigned char uc = (unsigned char) c;
			if (! __stdio_WRITE(stream, &uc, 1)) {
				goto BAD;
			}
		}
		return (unsigned char) c;
	}

 BAD:
	return EOF;
}
libc_hidden_def(__fputc_unlocked)

/* exposing these would be fundamentally *wrong*! fix you, instead! */
/* libc_hidden_proto(fputc_unlocked) */
strong_alias(__fputc_unlocked,fputc_unlocked)
/* exposing these would be fundamentally *wrong*! fix you, instead! */
/* libc_hidden_def(fputc_unlocked) */

libc_hidden_proto(putc_unlocked)
strong_alias(__fputc_unlocked,putc_unlocked)
libc_hidden_def(putc_unlocked)
#ifndef __UCLIBC_HAS_THREADS__
strong_alias(__fputc_unlocked,fputc)

libc_hidden_proto(putc)
strong_alias(__fputc_unlocked,putc)
libc_hidden_def(putc)
#endif

#elif defined __UCLIBC_HAS_THREADS__

libc_hidden_proto(fputc)
int fputc(int c, register FILE *stream)
{
	if (stream->__user_locking != 0) {
		return __PUTC_UNLOCKED_MACRO(c, stream);
	} else {
		int retval;
		__STDIO_ALWAYS_THREADLOCK(stream);
		retval = __PUTC_UNLOCKED_MACRO(c, stream);
		__STDIO_ALWAYS_THREADUNLOCK(stream);
		return retval;
	}
}
libc_hidden_def(fputc)

libc_hidden_proto(putc)
strong_alias(fputc,putc)
libc_hidden_def(putc)

#endif
