mirror of
https://github.com/reactos/reactos.git
synced 2025-04-21 04:37:15 +00:00
182 lines
5.9 KiB
C++
182 lines
5.9 KiB
C++
//
|
|
// ungetwc.cpp
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
// Defines ungetwc(), which pushes a wide character back into a stream.
|
|
//
|
|
#include <corecrt_internal_stdio.h>
|
|
|
|
|
|
|
|
// Pushes a character ("ungets" it) back into a stream. It is possible to push
|
|
// back one character. It may not be possible to push back more than one
|
|
// character in a row. Returns the pushed-back character on success; returns
|
|
// WEOF on failure. Ungetting WEOF is expressly forbidden.
|
|
extern "C" wint_t __cdecl ungetwc(wint_t const c, FILE* const stream)
|
|
{
|
|
_VALIDATE_RETURN(stream != nullptr, EINVAL, WEOF);
|
|
|
|
wint_t return_value = WEOF;
|
|
|
|
_lock_file(stream);
|
|
__try
|
|
{
|
|
return_value = _ungetwc_nolock(c, stream);
|
|
}
|
|
__finally
|
|
{
|
|
_unlock_file(stream);
|
|
}
|
|
__endtry
|
|
|
|
return return_value;
|
|
}
|
|
|
|
|
|
|
|
// Helper function for _ungetwc_nolock() that handles text mode ungetting.
|
|
static wint_t __cdecl ungetwc_text_mode_nolock(wint_t const c, __crt_stdio_stream const stream) throw()
|
|
{
|
|
// The stream is open in text mode, and we need to do the unget differently
|
|
// depending on whether the stream is open in ANSI or Unicode mode.
|
|
__crt_lowio_text_mode const text_mode = _textmode_safe(_fileno(stream.public_stream()));
|
|
|
|
int count = 0;
|
|
char characters[MB_LEN_MAX] = { 0 };
|
|
|
|
// If the file is open in ANSI mode, we need to convert the wide character
|
|
// to multibyte so that we can unget the multibyte character back into the
|
|
// stream:
|
|
if (text_mode == __crt_lowio_text_mode::ansi)
|
|
{
|
|
// If conversion fails, errno is set by wctomb_s and we can just return:
|
|
if (wctomb_s(&count, characters, MB_LEN_MAX, c) != 0)
|
|
return WEOF;
|
|
}
|
|
// Otherwise, the file is open in Unicode mode. This means the characters
|
|
// in the stream were originally Unicode (and not multibyte). Hence, we
|
|
// do not need to translate back to multibyte. This is true for both UTF-16
|
|
// and UTF-8, because the lowio read converts UTF-8 data to UTF-16.
|
|
else
|
|
{
|
|
char const* c_bytes = reinterpret_cast<char const*>(&c);
|
|
characters[0] = c_bytes[0];
|
|
characters[1] = c_bytes[1];
|
|
count = 2;
|
|
}
|
|
|
|
// At this point, the file must be buffered, so we know the base is non-null.
|
|
// First we need to ensure there is sufficient room in the buffer to store
|
|
// the translated data:
|
|
if (stream->_ptr < stream->_base + count)
|
|
{
|
|
if (stream->_cnt)
|
|
return WEOF;
|
|
|
|
if (count > stream->_bufsiz)
|
|
return WEOF;
|
|
|
|
stream->_ptr = count + stream->_base;
|
|
}
|
|
|
|
for (int i = count - 1; i >= 0; --i)
|
|
{
|
|
*--stream->_ptr = characters[i];
|
|
}
|
|
|
|
stream->_cnt += count;
|
|
|
|
stream.unset_flags(_IOEOF);
|
|
stream.set_flags(_IOREAD);
|
|
return static_cast<wint_t>(0xffff & c);
|
|
}
|
|
|
|
|
|
|
|
// Helper function for _ungetwc_nolock() that handles binary mode ungetting
|
|
static wint_t __cdecl ungetwc_binary_mode_nolock(wint_t const c, __crt_stdio_stream const stream) throw()
|
|
{
|
|
wchar_t const wide_c = static_cast<wchar_t>(c);
|
|
|
|
// At this point, the file must be buffered, so we know the base is non-null.
|
|
// First, we need to ensure there is sufficient room in the buffer to store
|
|
// the character:
|
|
if (stream->_ptr < stream->_base + sizeof(wchar_t))
|
|
{
|
|
// If we've already ungotten one character and it has not yet been read,
|
|
// there may not be room for this unget. In this case, there's nothing
|
|
// we can do so we simply fail:
|
|
if (stream->_cnt)
|
|
return WEOF;
|
|
|
|
if (sizeof(wchar_t) > stream->_bufsiz)
|
|
return WEOF;
|
|
|
|
stream->_ptr = sizeof(wchar_t) + stream->_base;
|
|
}
|
|
|
|
wchar_t*& wide_stream_ptr = reinterpret_cast<wchar_t*&>(stream->_ptr);
|
|
|
|
// If the stream is string-backed, we cannot modify the buffer. We retreat
|
|
// the stream pointer and test if the character being ungotten is the same
|
|
// as the character that was last read. If they are the same, then we allow
|
|
// the unget (because we don't have to modify the buffer). If they are not
|
|
// the same, then we re-advance the stream pointer and fail:
|
|
if (stream.is_string_backed())
|
|
{
|
|
if (*--wide_stream_ptr != wide_c)
|
|
{
|
|
++wide_stream_ptr;
|
|
return WEOF;
|
|
}
|
|
}
|
|
// Otherwise, the stream is file-backed and open in binary mode, and we can
|
|
// just write the character to the front of the stream:
|
|
else
|
|
{
|
|
*--wide_stream_ptr = wide_c;
|
|
}
|
|
|
|
stream->_cnt += sizeof(wchar_t);
|
|
|
|
stream.unset_flags(_IOEOF);
|
|
stream.set_flags(_IOREAD);
|
|
|
|
return static_cast<wint_t>(wide_c);
|
|
}
|
|
|
|
|
|
|
|
extern "C" wint_t __cdecl _ungetwc_nolock(wint_t const c, FILE* const public_stream)
|
|
{
|
|
__crt_stdio_stream const stream(public_stream);
|
|
|
|
// Ungetting WEOF is expressly forbidden:
|
|
if (c == WEOF)
|
|
return WEOF;
|
|
|
|
// To unget, the stream must currently be in read mode, _or_ it must be open
|
|
// for update (read and write) and must not _currently_ be in write mode:
|
|
bool const is_in_read_mode = stream.has_all_of(_IOREAD);
|
|
bool const is_in_update_mode = stream.has_all_of(_IOUPDATE);
|
|
bool const is_in_write_mode = stream.has_all_of(_IOWRITE);
|
|
|
|
if (!is_in_read_mode && !(is_in_update_mode && !is_in_write_mode))
|
|
return WEOF;
|
|
|
|
// If the stream is currently unbuffered, buffer it:
|
|
if (stream->_base == nullptr)
|
|
__acrt_stdio_allocate_buffer_nolock(stream.public_stream());
|
|
|
|
// If the stream is file-backed and is open in text mode, we need to perform
|
|
// text mode translations:
|
|
if (!stream.is_string_backed() && (_osfile_safe(_fileno(stream.public_stream())) & FTEXT) != 0)
|
|
{
|
|
return ungetwc_text_mode_nolock(c, stream);
|
|
}
|
|
|
|
// Otherwise, the stream is string-backed or is a file-backed file open in
|
|
// binary mode; we can simply push the character back into the stream:
|
|
return ungetwc_binary_mode_nolock(c, stream);
|
|
}
|