mirror of
https://github.com/reactos/reactos.git
synced 2025-05-13 14:20:31 +00:00

Imported from https://www.nuget.org/packages/Microsoft.Windows.SDK.CRTSource/10.0.22621.3 License: MIT
213 lines
6.8 KiB
C++
213 lines
6.8 KiB
C++
//
|
|
// _flsbuf.cpp
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
// Functions that flush a stdio stream buffer and write a character.
|
|
//
|
|
#include <corecrt_internal_stdio.h>
|
|
#include <corecrt_internal_ptd_propagation.h>
|
|
|
|
static bool __cdecl stream_is_at_end_of_file_nolock(
|
|
__crt_stdio_stream const stream
|
|
) throw()
|
|
{
|
|
if (stream.has_any_of(_IOEOF))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// If there is any data in the buffer, then we are not at the end of the file.
|
|
if (stream.has_big_buffer() && (stream->_ptr == stream->_base))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
HANDLE const os_handle = reinterpret_cast<HANDLE>(_get_osfhandle(stream.lowio_handle()));
|
|
|
|
// If we fail at querying for the file size, proceed as though we cannot
|
|
// gather that information. For example, this will fail with pipes.
|
|
if (os_handle == INVALID_HANDLE_VALUE)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Both SetFilePointerEx and GetFileSizeEx are valid ways to determine the
|
|
// length of a file. We can use that equivalence to check for end-of-file.
|
|
|
|
// This is a racy condition to check - a write or read from another process
|
|
// could interfere with the size reported from GetFileSizeEx.
|
|
// In that case, the write function looking to switch from read to write mode
|
|
// will fail in the usual manner when the write was not possible.
|
|
LARGE_INTEGER current_position;
|
|
if (!SetFilePointerEx(os_handle, {}, ¤t_position, FILE_CURRENT))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
LARGE_INTEGER eof_position;
|
|
if (!GetFileSizeEx(os_handle, &eof_position))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return current_position.QuadPart == eof_position.QuadPart;
|
|
}
|
|
|
|
template <typename Character>
|
|
static bool __cdecl write_buffer_nolock(
|
|
Character const c,
|
|
__crt_stdio_stream const stream,
|
|
__crt_cached_ptd_host& ptd
|
|
) throw()
|
|
{
|
|
typedef __acrt_stdio_char_traits<Character> stdio_traits;
|
|
|
|
int const fh = _fileno(stream.public_stream());
|
|
|
|
// If the stream is buffered, write the buffer if there are characters to
|
|
// be written, then push the character 'c' into the buffer:
|
|
if (stream.has_big_buffer())
|
|
{
|
|
_ASSERTE(("inconsistent IOB fields", stream->_ptr - stream->_base >= 0));
|
|
|
|
int const chars_to_write = static_cast<int>(stream->_ptr - stream->_base);
|
|
stream->_ptr = stream->_base + sizeof(Character);
|
|
stream->_cnt = stream->_bufsiz - static_cast<int>(sizeof(Character));
|
|
|
|
int chars_written = 0;
|
|
if (chars_to_write > 0)
|
|
{
|
|
chars_written = _write_internal(fh, stream->_base, chars_to_write, ptd);
|
|
}
|
|
else
|
|
{
|
|
if (_osfile_safe(fh) & FAPPEND)
|
|
{
|
|
if (_lseeki64(fh, 0, SEEK_END) == -1)
|
|
{
|
|
stream.set_flags(_IOERROR);
|
|
return stdio_traits::eof;
|
|
}
|
|
}
|
|
}
|
|
|
|
*reinterpret_cast<Character*>(stream->_base) = c;
|
|
return chars_written == chars_to_write;
|
|
}
|
|
// Otherwise, perform a single character write (if we get here, either
|
|
// _IONBF is set or there is no buffering):
|
|
else
|
|
{
|
|
return _write_internal(fh, reinterpret_cast<char const*>(&c), sizeof(c), ptd) == sizeof(Character);
|
|
}
|
|
}
|
|
|
|
// Flushes the buffer of the given stream and writes the character. If the stream
|
|
// does not have a buffer, one is obtained for it. The character 'c' is written
|
|
// into the buffer after the buffer is flushed. This function is only intended
|
|
// to be called from within the library. Returns (W)EOF on failure; otherwise
|
|
// returns the character 'c' that was written to the file (note that 'c' may be
|
|
// truncated if the int value cannot be represented by the Character type).
|
|
template <typename Character>
|
|
static int __cdecl common_flush_and_write_nolock(
|
|
int const c,
|
|
__crt_stdio_stream const stream,
|
|
__crt_cached_ptd_host& ptd
|
|
) throw()
|
|
{
|
|
typedef __acrt_stdio_char_traits<Character> stdio_traits;
|
|
|
|
unsigned const character_mask = (1 << (CHAR_BIT * sizeof(Character))) - 1;
|
|
|
|
_ASSERTE(stream.valid());
|
|
|
|
int const fh = _fileno(stream.public_stream());
|
|
|
|
if (!stream.has_any_of(_IOWRITE | _IOUPDATE))
|
|
{
|
|
ptd.get_errno().set(EBADF);
|
|
stream.set_flags(_IOERROR);
|
|
return stdio_traits::eof;
|
|
}
|
|
else if (stream.is_string_backed())
|
|
{
|
|
ptd.get_errno().set(ERANGE);
|
|
stream.set_flags(_IOERROR);
|
|
return stdio_traits::eof;
|
|
}
|
|
|
|
// 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.has_any_of(_IOREAD))
|
|
{
|
|
bool const switch_to_write_mode = stream_is_at_end_of_file_nolock(stream);
|
|
stream->_cnt = 0; // in either case, flush buffer
|
|
|
|
if (switch_to_write_mode)
|
|
{
|
|
stream->_ptr = stream->_base;
|
|
stream.unset_flags(_IOREAD);
|
|
}
|
|
else
|
|
{
|
|
stream.set_flags(_IOERROR);
|
|
return stdio_traits::eof;
|
|
}
|
|
}
|
|
|
|
stream.set_flags(_IOWRITE);
|
|
stream.unset_flags(_IOEOF);
|
|
|
|
stream->_cnt = 0;
|
|
|
|
// Get a buffer for this stream, if one is necessary:
|
|
if (!stream.has_any_buffer())
|
|
{
|
|
// If the stream uses temporary buffering, we do not set up a single character buffer;
|
|
// this is so that later temporary buffering will not be thwarted by
|
|
// the _IONBF flag being set. (See _stbuf() and _ftbuf() for more
|
|
//information on stdout and stderr buffering.)
|
|
if (!__acrt_should_use_temporary_buffer(stream.public_stream()))
|
|
{
|
|
__acrt_stdio_allocate_buffer_nolock(stream.public_stream());
|
|
}
|
|
}
|
|
|
|
// Write the character; return (W)EOF if it fails:
|
|
if (!write_buffer_nolock(static_cast<Character>(c & character_mask), stream, ptd))
|
|
{
|
|
stream.set_flags(_IOERROR);
|
|
return stdio_traits::eof;
|
|
}
|
|
|
|
return c & character_mask;
|
|
}
|
|
|
|
|
|
|
|
extern "C" int __cdecl __acrt_stdio_flush_and_write_narrow_nolock(
|
|
int const c,
|
|
FILE* const stream,
|
|
__crt_cached_ptd_host& ptd
|
|
)
|
|
{
|
|
return common_flush_and_write_nolock<char>(c, __crt_stdio_stream(stream), ptd);
|
|
}
|
|
|
|
|
|
|
|
extern "C" int __cdecl __acrt_stdio_flush_and_write_wide_nolock(
|
|
int const c,
|
|
FILE* const stream,
|
|
__crt_cached_ptd_host& ptd
|
|
)
|
|
{
|
|
return common_flush_and_write_nolock<wchar_t>(c, __crt_stdio_stream(stream), ptd);
|
|
}
|