//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft shared
// source or premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license agreement,
// you are not authorized to use this source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the SOURCE.RTF on your install media or the root of your tools installation.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
/***
*_flsbuf.c - flush buffer and output character.
*
*       Copyright (c) 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_nolock" 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-06-93  SKS   Replace _CRTAPI* with __cdecl
*       04-26-93  CFW   Wide char enable.
*       05-06-93  CFW   Optimize wide char conversion.
*       11-05-93  GJF   Merged with NT SDK version (picked up _NTSUBSET_
*                       stuff).
*       10-17-94  BWT   Move wchar.h to non-POSIX build (ino_t definitions conflict)
*       02-06-94  CFW   assert -> _ASSERTE.
*       02-16-95  GJF   Appended Mac version of source file (somewhat cleaned
*                       up), with appropriate #ifdef-s.
*       06-12-95  GJF   Replaced _osfile[] with _osfile() (macro referencing
*                       field in ioinfo struct).
*       07-25-95  GJF   Replaced _osfile()with _osfile_safe().
*       12-07-95  SKS   Fix misspelling of _NTSUBSET_ (final _ was missing)
*       02-27-98  RKP   Added 64 bit support.
*       01-04-99  GJF   Changes for 64-bit size_t.
*       05-17-99  PML   Remove all Macintosh support.
*       09-11-03  SJ    Secure CRT Work - Assertions & Validations
*       03-13-04  MSL   Test lseek return for prefast
*       11-11-04  MR    Set errno to ERANGE for overflows - VSW#305392
*       08-22-05  AEI   Fixed VSW#546665: _lseek is replaced with _lseeki64, because
*                       _lseek returns -1 in case the file is bigger than 32 bit int max
*
*******************************************************************************/

#include <cruntime.h>
#include <stdio.h>
#include <file2.h>
#include <io.h>
#include <dbgint.h>
#include <malloc.h>
#ifdef  _POSIX_
#include <unistd.h>
#include <errno.h>
#else
#include <msdos.h>
#include <wchar.h>
#endif
#include <internal.h>
#include <mtdll.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 __cdecl _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 __cdecl _flswbuf (
        int ch,
        FILE *str
        )

#endif  /* _UNICODE */

{
#ifdef  _NTSUBSET_

        str->_flag |= _IOERR;
        return(_TEOF);

#else   /* ndef _NTSUBSET_ */

        FILE *stream;
        int charcount;
        int written;
        int fh;

        _ASSERTE(str != NULL);

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

        if (!(stream->_flag & (_IOWRT|_IORW))) {
                errno = EBADF;
                stream->_flag |= _IOERR;
                return(_TEOF);
        } else if ((stream->_flag & _IOSTRG)) {
                errno = ERANGE;
                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)) {

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

                charcount = (int)(stream->_ptr - stream->_base);
                stream->_ptr = stream->_base + sizeof(TCHAR);
                stream->_cnt = stream->_bufsiz - (int)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)
						{
                            if( _lseeki64(fh,0l,SEEK_END)==-1)
							{
								stream->_flag |= _IOERR;
								return(_TEOF);
							}
						}
#else
                        if (_osfile_safe(fh) & FAPPEND)
						{
							if( _lseeki64(fh,0L,SEEK_END)==-1)
							{
								stream->_flag |= _IOERR;
								return(_TEOF);
							}
						}
#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_ */
}
