/***
*_flsbuf.c - flush buffer and output character.
*
*	Copyright (c) 1985-1993, Microsoft Corporation. All rights reserved.
*
*Purpose:
*	defines _flsbuf() - flush a file buffer and output a character.
*	defines _flswbuf() - flush a file buffer and output a wide character.
*	If no buffer, make one.
*
*Revision History:
*	09-01-83  RN	initial version
*	06-26-85  TC	added code to handle variable length buffers
*	06-08-87  JCR	When buffer is allocated or when first write to buffer
*			occurs, if stream is in append mode, then position file
*			pointer to end.
*	07-20-87  SKS	Change first parameter "ch" from (char) to (int)
*	09-28-87  JCR	Corrected _iob2 indexing (now uses _iob_index() macro).
*	11-05-87  JCR	Re-wrote for simplicity and for new stderr/stdout
*			handling
*	12-11-87  JCR	Added "_LOAD_DS" to declaration
*	01-11-88  JCR	Merged mthread version into normal code
*	01-13-88  SKS	Changed bogus "_fileno_lk" to "fileno"
*	06-06-88  JCR	Optimized _iob2 references
*	06-13-88  JCR	Use near pointer to reference _iob[] entries
*	06-28-88  JCR	Support for dynamic buffer allocation for stdout/stderr
*	07-28-88  GJF	Set stream->_cnt to 0 if _IOREAD is set.
*	08-25-88  GJF	Added checked that OS2 is defined whenever M_I386 is.
*	06-20-89  PHG	Removed FP_OFF macro call.
*	08-28-89  JCR	Removed _NEAR_ for 386
*	02-15-90  GJF	_iob[], _iob2[] merge. Also, fixed copyright and
*			indents.
*	03-16-90  GJF	Replaced cdecl _LOAD_DS with _CALLTYPE1, added #include
*			<cruntime.h> and removed #include <register.h>. Also,
*			removed some leftover 16-bit support.
*	03-27-90  GJF	Added #include <io.h>.
*	07-23-90  SBM	Replaced <assertm.h> by <assert.h>
*	08-07-90  SBM	Restored descriptive text in assertion
*	08-14-90  SBM	Compiles cleanly with -W3
*	10-03-90  GJF	New-style function declarator.
*	01-22-91  GJF	ANSI naming.
*	03-25-91  DJM	POSIX support
*	08-26-92  GJF	Include unistd.h for POSIX build.
*	04-26-93  CFW	Wide char enable.
*	05-06-93  CFW	Optimize wide char conversion.
*
*******************************************************************************/

#include <cruntime.h>
#include <stdio.h>
#include <file2.h>
#include <io.h>
#include <assert.h>
#include <malloc.h>
#ifdef _POSIX_
#include <unistd.h>
#include <errno.h>
#else
#include <msdos.h>
#endif
#include <internal.h>
#ifdef MTHREAD
#include <os2dll.h>
#endif
#include <wchar.h>
#include <tchar.h>

#ifndef _UNICODE

/***
*int _flsbuf(ch, stream) - flush buffer and output character.
*
*Purpose:
*	flush a buffer if this stream has one. if not, try to get one. put the
*	next output char (ch) into the buffer (or output it immediately if this
*	stream can't have a buffer). called only from putc. intended for use
*	only within library.
*
*	[NOTE: Multi-thread - It is assumed that the caller has aquired
*	the stream lock.]
*
*Entry:
*	FILE *stream - stream to flish and write on
*	int ch - character to output.
*
*Exit:
*	returns -1 if FILE is actually a string, or if can't write ch to
*	unbuffered file, or if we flush a buffer but the number of chars
*	written doesn't agree with buffer size.  Otherwise returns ch.
*	all fields in FILE struct can be affected except _file.
*
*Exceptions:
*
*******************************************************************************/

int _CRTAPI1 _flsbuf (
	int ch,
	FILE *str
	)

#else /* _UNICODE */

/***
*int _flswbuf(ch, stream) - flush buffer and output wide character.
*
*Purpose:
*	flush a buffer if this stream has one. if not, try to get one. put the
*	next output wide char (ch) into the buffer (or output it immediately if this
*	stream can't have a buffer). called only from putwc. intended for use
*	only within library.
*
*	[NOTE: Multi-thread - It is assumed that the caller has aquired
*	the stream lock.]
*
*Entry:
*	FILE *stream - stream to flish and write on
*	int ch - wide character to output.
*
*Exit:
*	returns -1 if FILE is actually a string, or if can't write ch to
*	unbuffered file, or if we flush a buffer but the number of wide chars
*	written doesn't agree with buffer size.  Otherwise returns ch.
*	all fields in FILE struct can be affected except _file.
*
*Exceptions:
*
*******************************************************************************/

int _CRTAPI1 _flswbuf (
	int ch,
	FILE *str
	)

#endif /* _UNICODE */

{
#ifdef _NTSUBSET_
        str->_flag |= _IOERR;
        return(_TEOF);
#else
	REG1 FILE *stream;
	REG2 int charcount;
	REG3 int written;
	int fh;

	assert(str != NULL);

	/* Init file handle and pointers */
	stream = str;
#ifdef _POSIX_
	fh = fileno(stream);
#else
	fh = _fileno(stream);
#endif

	if (!(stream->_flag & (_IOWRT|_IORW)) || (stream->_flag & _IOSTRG)) {
#ifdef _POSIX_
		errno = EBADF;
#endif
		stream->_flag |= _IOERR;
		return(_TEOF);
	}

	/* Check that _IOREAD is not set or, if it is, then so is _IOEOF. Note
	   that _IOREAD and IOEOF both being set implies switching from read to
	   write at end-of-file, which is allowed by ANSI. Note that resetting
	   the _cnt and _ptr fields amounts to doing an fflush() on the stream
	   in this case. Note also that the _cnt field has to be reset to 0 for
	   the error path as well (i.e., _IOREAD set but _IOEOF not set) as
	   well as the non-error path. */

	if (stream->_flag & _IOREAD) {
		stream->_cnt = 0;
		if (stream->_flag & _IOEOF) {
			stream->_ptr = stream->_base;
			stream->_flag &= ~_IOREAD;
		}
		else {
			stream->_flag |= _IOERR;
			return(_TEOF);
		}
	}

	stream->_flag |= _IOWRT;
	stream->_flag &= ~_IOEOF;
	written = charcount = stream->_cnt = 0;

	/* Get a buffer for this stream, if necessary. */
	if (!anybuf(stream)) {

		/* Do NOT get a buffer if (1) stream is stdout/stderr, and
		   (2) stream is NOT a tty.
		   [If stdout/stderr is a tty, we do NOT set up single char
		   buffering. This is so that later temporary buffering will
		   not be thwarted by the _IONBF bit being set (see
		   _stbuf/_ftbuf usage).]
		*/
		if (!( ((stream==stdout) || (stream==stderr))
#ifdef _POSIX_
		&& (isatty(fh)) ))
#else
		&& (_isatty(fh)) ))
#endif

			_getbuf(stream);

	} /* end !anybuf() */

	/* If big buffer is assigned to stream... */
	if (bigbuf(stream)) {

		assert(("inconsistent IOB fields", stream->_ptr - stream->_base >= 0));

		charcount = stream->_ptr - stream->_base;
		stream->_ptr = stream->_base + sizeof(TCHAR);
		stream->_cnt = stream->_bufsiz - sizeof(TCHAR);

		if (charcount > 0)
#ifdef _POSIX_
			written = write(fh, stream->_base, charcount);
#else
			written = _write(fh, stream->_base, charcount);
#endif
		else
#ifdef _POSIX_
			if (stream->_flag & _IOAPPEND)
			    lseek(fh,0l,SEEK_END);
#else
			if (_osfile[fh] & FAPPEND)
				_lseek(fh,0L,SEEK_END);
#endif

#ifndef _UNICODE
		*stream->_base = (char)ch;
#else /* _UNICODE */
		*(wchar_t *)(stream->_base) = (wchar_t)(ch & 0xffff);
#endif /* _UNICODE */
	}

    /* Perform single character output (either _IONBF or no buffering) */
	else {
		charcount = sizeof(TCHAR);
#ifndef _UNICODE
#ifdef _POSIX_
		written = write(fh, &ch, charcount);
#else
		written = _write(fh, &ch, charcount);
#endif
#else /* _UNICODE */
		{
			char mbc[4];

			*(wchar_t *)mbc = (wchar_t)(ch & 0xffff);
#ifdef _POSIX_
			written = write(fh, mbc, charcount);
#else
			written = _write(fh, mbc, charcount);
#endif
		}
#endif /* _UNICODE */
	}

	/* See if the _write() was successful. */
	if (written != charcount) {
		stream->_flag |= _IOERR;
		return(_TEOF);
	}

#ifndef _UNICODE
	return(ch & 0xff);
#else /* _UNICODE */
	return(ch & 0xffff);
#endif /* _UNICODE */
#endif /* _NTSUBSET */
}
