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. 31 template <typename Character> 32 _Success_(return != nullptr) 33 static 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. 124 extern "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 129 extern "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. 141 extern "C" char* __cdecl gets(char* const result) 142 { 143 return common_gets(result, _CRT_UNBOUNDED_BUFFER_SIZE, true); 144 } 145 146 extern "C" wchar_t* __cdecl _getws(wchar_t* const result) 147 { 148 return common_gets(result, _CRT_UNBOUNDED_BUFFER_SIZE, true); 149 } 150