Reactos
at master 149 lines 5.4 kB view raw
1// 2// gets.cpp 3// 4// Copyright (c) Microsoft Corporation. All rights reserved. 5// 6// Defines gets() and gets_s(), which read a line from stdin. 7// 8#include <corecrt_internal_stdio.h> 9#include <corecrt_internal_securecrt.h> 10 11 12 13// Reads a line of text from stdin, storing it in the 'result' buffer. This 14// function supports two modes: 15// 16// [1] Insecure, which is used by the abominable gets() and _getws() functions. 17// In this mode, the buffer is not bounds-checked; it is just assumed that 18// the buffer is large enough. Text is read until a newline is reached or 19// EOF is reached. This mode is enabled by passing _CRT_UNBOUNDED_BUFFER_SIZE 20// as the buffer size. 21// 22// [2] Secure, which is used by the gets_s() and _getws_s() functions. In this 23// mode, the buffer is bound-checked. If there is insufficient space in the 24// buffer for the entire line of text, the line is read and discarded, the 25// buffer is zero'ed, and nullptr is returned. If the buffer is larger than 26// is required, the remaining space of the buffer is zero-filled. 27// 28// On success, 'result' is returned. On failure, nullptr is returned. If the 29// 'return_early_if_eof_is_first' flag is true and the first read encounters EOF, 30// the buffer is left unchanged and nullptr is returned. 31template <typename Character> 32_Success_(return != nullptr) 33static Character* __cdecl common_gets( 34 _Out_writes_z_(result_size_in_characters) Character* const result, 35 _In_ size_t const result_size_in_characters, 36 _In_ bool const return_early_if_eof_is_first 37 ) throw() 38{ 39 typedef __acrt_stdio_char_traits<Character> stdio_traits; 40 41 _VALIDATE_RETURN(result != nullptr, EINVAL, nullptr); 42 _VALIDATE_RETURN(result_size_in_characters > 0, EINVAL, nullptr); 43 44 Character* return_value = result; 45 46 _lock_file(stdin); 47 __try 48 { 49 if (!stdio_traits::validate_stream_is_ansi_if_required(stdin)) 50 { 51 return_value = nullptr; 52 __leave; 53 } 54 55 // Special case: if the first character is EOF, treat it specially if 56 // we were asked to do so: 57 typename stdio_traits::int_type c = stdio_traits::getc_nolock(stdin); 58 if (c == stdio_traits::eof) 59 { 60 return_value = nullptr; 61 if (return_early_if_eof_is_first) 62 __leave; 63 } 64 65 // For the insecure version, we do no buffer size check and no debug fill: 66 if (result_size_in_characters == _CRT_UNBOUNDED_BUFFER_SIZE) 67 { 68#pragma warning(push) 69#pragma warning(disable:__WARNING_POTENTIAL_BUFFER_OVERFLOW_HIGH_PRIORITY) // 26015 - knowingly unsafe 70 Character* result_it = result; 71 while (c != '\n' && c != stdio_traits::eof) 72 { 73 *result_it++ = static_cast<Character>(c); 74 c = stdio_traits::getc_nolock(stdin); 75 } 76 *result_it = '\0'; 77#pragma warning(pop) 78 } 79 // For the secure version, we track the buffer size. If we run out of 80 // buffer space, we still read in the rest of the current line until we 81 // reach '\n' or EOF, but we discard the characters and reset the buffer 82 // to be zero-filled. 83 else 84 { 85 size_t available = result_size_in_characters; 86 87 Character* result_it = result; 88 while (c != '\n' && c != stdio_traits::eof) 89 { 90 if (available > 0) 91 { 92 --available; 93 *result_it++ = static_cast<Character>(c); 94 } 95 96 c = stdio_traits::getc_nolock(stdin); 97 } 98 99 // If we ran out of space, clear the buffer and return failure: 100 if (available == 0) 101 { 102 _RESET_STRING(result, result_size_in_characters); 103 _RETURN_BUFFER_TOO_SMALL_ERROR(result, result_size_in_characters, nullptr); 104 } 105 106 *result_it = '\0'; 107 _FILL_STRING(result, result_size_in_characters, result_size_in_characters - available + 1); 108 } 109 } 110 __finally 111 { 112 _unlock_file(stdin); 113 } 114 __endtry 115 116 return return_value; 117} 118 119 120 121// Reads a line of text from stdin and stores it in the result buffer. If the 122// line is longer than will fit in the buffer, the line is discarded, the buffer 123// is reset, and nullptr is returned. 124extern "C" char* __cdecl gets_s(char* const result, size_t const result_size_in_characters) 125{ 126 return common_gets(result, result_size_in_characters, false); 127} 128 129extern "C" wchar_t* __cdecl _getws_s(wchar_t* const result, size_t const result_size_in_characters) 130{ 131 return common_gets(result, result_size_in_characters, false); 132} 133 134 135 136// Reads a line of text from stdin and stores it in the result buffer. This 137// function assumes there is sufficient room in the buffer. Do not use this 138// function; use gets_s(), fgets(), _getws_s(), or fgetws() instead. If EOF 139// is encountered on the first read from the stream, the buffer is left 140// unmodified and nullptr is returned. 141extern "C" char* __cdecl gets(char* const result) 142{ 143 return common_gets(result, _CRT_UNBOUNDED_BUFFER_SIZE, true); 144} 145 146extern "C" wchar_t* __cdecl _getws(wchar_t* const result) 147{ 148 return common_gets(result, _CRT_UNBOUNDED_BUFFER_SIZE, true); 149}