mirror of
https://github.com/reactos/reactos.git
synced 2025-06-02 07:56:16 +00:00
149 lines
5.3 KiB
C++
149 lines
5.3 KiB
C++
//
|
|
// gets.cpp
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
// Defines gets() and gets_s(), which read a line from stdin.
|
|
//
|
|
#include <corecrt_internal_stdio.h>
|
|
#include <corecrt_internal_securecrt.h>
|
|
|
|
|
|
|
|
// Reads a line of text from stdin, storing it in the 'result' buffer. This
|
|
// function supports two modes:
|
|
//
|
|
// [1] Insecure, which is used by the abominable gets() and _getws() functions.
|
|
// In this mode, the buffer is not bounds-checked; it is just assumed that
|
|
// the buffer is large enough. Text is read until a newline is reached or
|
|
// EOF is reached. This mode is enabled by passing _CRT_UNBOUNDED_BUFFER_SIZE
|
|
// as the buffer size.
|
|
//
|
|
// [2] Secure, which is used by the gets_s() and _getws_s() functions. In this
|
|
// mode, the buffer is bound-checked. If there is insufficient space in the
|
|
// buffer for the entire line of text, the line is read and discarded, the
|
|
// buffer is zero'ed, and nullptr is returned. If the buffer is larger than
|
|
// is required, the remaining space of the buffer is zero-filled.
|
|
//
|
|
// On success, 'result' is returned. On failure, nullptr is returned. If the
|
|
// 'return_early_if_eof_is_first' flag is true and the first read encounters EOF,
|
|
// the buffer is left unchanged and nullptr is returned.
|
|
template <typename Character>
|
|
_Success_(return != nullptr)
|
|
static Character* __cdecl common_gets(
|
|
_Out_writes_z_(result_size_in_characters) Character* const result,
|
|
_In_ size_t const result_size_in_characters,
|
|
_In_ bool const return_early_if_eof_is_first
|
|
) throw()
|
|
{
|
|
typedef __acrt_stdio_char_traits<Character> stdio_traits;
|
|
|
|
_VALIDATE_RETURN(result != nullptr, EINVAL, nullptr);
|
|
_VALIDATE_RETURN(result_size_in_characters > 0, EINVAL, nullptr);
|
|
|
|
Character* return_value = result;
|
|
|
|
_lock_file(stdin);
|
|
__try
|
|
{
|
|
if (!stdio_traits::validate_stream_is_ansi_if_required(stdin))
|
|
{
|
|
return_value = nullptr;
|
|
__leave;
|
|
}
|
|
|
|
// Special case: if the first character is EOF, treat it specially if
|
|
// we were asked to do so:
|
|
typename stdio_traits::int_type c = stdio_traits::getc_nolock(stdin);
|
|
if (c == stdio_traits::eof)
|
|
{
|
|
return_value = nullptr;
|
|
if (return_early_if_eof_is_first)
|
|
__leave;
|
|
}
|
|
|
|
// For the insecure version, we do no buffer size check and no debug fill:
|
|
if (result_size_in_characters == _CRT_UNBOUNDED_BUFFER_SIZE)
|
|
{
|
|
#pragma warning(push)
|
|
#pragma warning(disable:__WARNING_POTENTIAL_BUFFER_OVERFLOW_HIGH_PRIORITY) // 26015 - knowingly unsafe
|
|
Character* result_it = result;
|
|
while (c != '\n' && c != stdio_traits::eof)
|
|
{
|
|
*result_it++ = static_cast<Character>(c);
|
|
c = stdio_traits::getc_nolock(stdin);
|
|
}
|
|
*result_it = '\0';
|
|
#pragma warning(pop)
|
|
}
|
|
// For the secure version, we track the buffer size. If we run out of
|
|
// buffer space, we still read in the rest of the current line until we
|
|
// reach '\n' or EOF, but we discard the characters and reset the buffer
|
|
// to be zero-filled.
|
|
else
|
|
{
|
|
size_t available = result_size_in_characters;
|
|
|
|
Character* result_it = result;
|
|
while (c != '\n' && c != stdio_traits::eof)
|
|
{
|
|
if (available > 0)
|
|
{
|
|
--available;
|
|
*result_it++ = static_cast<Character>(c);
|
|
}
|
|
|
|
c = stdio_traits::getc_nolock(stdin);
|
|
}
|
|
|
|
// If we ran out of space, clear the buffer and return failure:
|
|
if (available == 0)
|
|
{
|
|
_RESET_STRING(result, result_size_in_characters);
|
|
_RETURN_BUFFER_TOO_SMALL_ERROR(result, result_size_in_characters, nullptr);
|
|
}
|
|
|
|
*result_it = '\0';
|
|
_FILL_STRING(result, result_size_in_characters, result_size_in_characters - available + 1);
|
|
}
|
|
}
|
|
__finally
|
|
{
|
|
_unlock_file(stdin);
|
|
}
|
|
__endtry
|
|
|
|
return return_value;
|
|
}
|
|
|
|
|
|
|
|
// Reads a line of text from stdin and stores it in the result buffer. If the
|
|
// line is longer than will fit in the buffer, the line is discarded, the buffer
|
|
// is reset, and nullptr is returned.
|
|
extern "C" char* __cdecl gets_s(char* const result, size_t const result_size_in_characters)
|
|
{
|
|
return common_gets(result, result_size_in_characters, false);
|
|
}
|
|
|
|
extern "C" wchar_t* __cdecl _getws_s(wchar_t* const result, size_t const result_size_in_characters)
|
|
{
|
|
return common_gets(result, result_size_in_characters, false);
|
|
}
|
|
|
|
|
|
|
|
// Reads a line of text from stdin and stores it in the result buffer. This
|
|
// function assumes there is sufficient room in the buffer. Do not use this
|
|
// function; use gets_s(), fgets(), _getws_s(), or fgetws() instead. If EOF
|
|
// is encountered on the first read from the stream, the buffer is left
|
|
// unmodified and nullptr is returned.
|
|
extern "C" char* __cdecl gets(char* const result)
|
|
{
|
|
return common_gets(result, _CRT_UNBOUNDED_BUFFER_SIZE, true);
|
|
}
|
|
|
|
extern "C" wchar_t* __cdecl _getws(wchar_t* const result)
|
|
{
|
|
return common_gets(result, _CRT_UNBOUNDED_BUFFER_SIZE, true);
|
|
}
|