Reactos
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}