xref: /reactos/sdk/lib/ucrt/conio/cgetws.cpp (revision e3e520d1)
1 //
2 // cgetws.cpp
3 //
4 //      Copyright (c) Microsoft Corporation. All rights reserved.
5 //
6 // Defines _cgetws() and _cgetws_s(), which read a wide string from the console.
7 //
8 #include <conio.h>
9 #include <corecrt_internal_lowio.h>
10 #include <corecrt_internal_securecrt.h>
11 #include <stdlib.h>
12 
13 // Use of the following buffer variables is primarily for syncronizing with
14 // _cgets_s. _cget_s fills the MBCS buffer and if the user passes in single
15 // character buffer and the unicode character is not converted to single byte
16 // MBC, then _cget_s should buffer that character so that next call to
17 // _cgetws_s can return the same character.
18 extern "C" { wchar_t __console_wchar_buffer      = 0; }
19 extern "C" { int     __console_wchar_buffer_used = 0; }
20 
21 
22 
23 // Reads a string from the console; always null-terminates the buffer.  Returns
24 // 0 on success; returns an errno error code on failure.
25 extern "C" errno_t __cdecl _cgetws_s(wchar_t* const string_buffer, size_t const size_in_words, size_t* const size_read)
26 {
27     _VALIDATE_CLEAR_OSSERR_RETURN_ERRCODE(string_buffer != nullptr, EINVAL);
28     _VALIDATE_CLEAR_OSSERR_RETURN_ERRCODE(size_in_words > 0, EINVAL);
29     _RESET_STRING(string_buffer, size_in_words);
30 
31     _VALIDATE_CLEAR_OSSERR_RETURN_ERRCODE(size_read != nullptr, EINVAL);
32 
33     __acrt_lock(__acrt_conio_lock);
34     errno_t retval = 0;
35     __try
36     {
37         wchar_t* string = string_buffer;
38 
39         // We need to decrement size_in_words because ReadConsole reads as many
40         // characters as the parameter passed.  It doesn't null-terminate:
41         size_t size_remaining = size_in_words - 1;
42         *size_read = 0;
43 
44         // If size_in_words was 1, then there's only room for the null terminator:
45         if (size_remaining == 0)
46             __leave;
47 
48         // If there is a buffered character, first fill with the buffered
49         // character then proceed with reading from the console:
50         if (__console_wchar_buffer_used != 0)
51         {
52             *string++ = __console_wchar_buffer;
53             --size_remaining;
54             (*size_read)++;
55 
56             if (__console_wchar_buffer == L'\0')
57                 size_remaining = 0;
58 
59             __console_wchar_buffer = 0;
60         }
61 
62         if (size_remaining == 0)
63             __leave;
64 
65         /*
66         * __dcrt_lowio_console_input_handle, the handle to the console input, is created the first
67         * time that either _getch() or _cgets() or _kbhit() is called.
68         */
69 
70         if (__dcrt_lowio_ensure_console_input_initialized() == FALSE)
71         {
72             __acrt_errno_map_os_error(GetLastError());
73             retval = errno;
74             __leave;
75         }
76 
77         ULONG old_state;
78         __dcrt_get_input_console_mode(&old_state);
79         __dcrt_set_input_console_mode(ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_ECHO_INPUT);
80 
81         ULONG wchars_read;
82         BOOL read_console_result = __dcrt_read_console(
83             string,
84             static_cast<DWORD>(size_remaining),
85             &wchars_read);
86 
87         if (!read_console_result)
88         {
89             __acrt_errno_map_os_error(GetLastError());
90             retval = errno;
91             __leave;
92         }
93 
94         // Set the length of the string and null terminate it:
95         if (wchars_read >= 2 && string[wchars_read - 2] == L'\r')
96         {
97             *size_read += wchars_read - 2;
98             string[wchars_read - 2] = L'\0';
99         }
100         else if (wchars_read == size_remaining && string[wchars_read - 1] == L'\r')
101         {
102             // Special case 1:  \r\n straddles the boundary:
103             string[wchars_read - 1] = L'\0';
104             *size_read += wchars_read - 1;
105         }
106         else if (wchars_read == 1 && string[0] == L'\n')
107         {
108             // Special case 2:  Read a single \n:
109             string[0] = L'\0';
110             *size_read += 0;
111         }
112         else
113         {
114             *size_read += wchars_read;
115             string[wchars_read] = L'\0';
116         }
117 
118         __dcrt_set_input_console_mode(old_state);
119     }
120     __finally
121     {
122         __acrt_unlock(__acrt_conio_lock);
123     }
124     __endtry
125 
126     return retval;
127 }
128 
129 
130 
131 // Reads a string from the console via ReadConsole on a cooked console handle.
132 // string[0] must contain the maximum length of the string.  The number of
133 // characters written is stored in string[1].  The return value is a pointer to
134 // string[2] on success; nullptr on failure.
135 //
136 // Note that _cgetws() does NOT check the pushback character buffer, so it will
137 // not return any character that is pushed back by a call to _ungetwch().
138 extern "C" wchar_t* __cdecl _cgetws(_Inout_z_ wchar_t* const string)
139 {
140     _VALIDATE_CLEAR_OSSERR_RETURN(string != nullptr, EINVAL, nullptr);
141     _VALIDATE_CLEAR_OSSERR_RETURN(string[0] > 0,     EINVAL, nullptr);
142 
143     size_t const size_in_words = static_cast<size_t>(string[0]);
144 
145     size_t size_read = 0;
146     // warning 26018: Potential overflow of null terminated buffer using expression string+2
147     // Suppressing warning since _cgetws is purposefully unsafe.
148 #pragma warning(suppress:__WARNING_POTENTIAL_BUFFER_OVERFLOW_NULLTERMINATED)
149     errno_t const result = _cgetws_s(string + 2, size_in_words, &size_read);
150 
151     // warning 26018: Potential overflow of null terminated buffer using expression string[1]
152     // Suppressing warning since _cgetws is purposefully unsafe.
153 #pragma warning(suppress:__WARNING_POTENTIAL_BUFFER_OVERFLOW_NULLTERMINATED)
154     string[1] = static_cast<wchar_t>(size_read);
155 
156     return result == 0 ? string + 2 : nullptr;
157 }
158