xref: /reactos/sdk/lib/ucrt/conio/cgets.cpp (revision b09b5584)
1 //
2 // cgets.cpp
3 //
4 //      Copyright (c) Microsoft Corporation. All rights reserved.
5 //
6 // Defines _cgets() and _cgets_s(), which read a string from the console.
7 //
8 #include <conio.h>
9 #include <corecrt_internal_securecrt.h>
10 #include <stdlib.h>
11 
12 
13 
14 extern "C" { extern intptr_t __dcrt_lowio_console_input_handle; }
15 
16 
17 
18 // Use of the following buffer variables is primarily for syncronizing with
19 // _cgets_s. _cget_s fills the MBCS buffer and if the user passes in single
20 // character buffer and the unicode character is not converted to single byte
21 // MBC, then _cget_s should buffer that character so that next call to
22 // _cgetws_s can return the same character.
23 extern "C" { extern wchar_t __console_wchar_buffer; }
24 extern "C" { extern int     __console_wchar_buffer_used; }
25 
26 
27 
28 // Reads a string from the console; always null-terminates the buffer.  Returns
29 // 0 on success; returns an errno error code on failure.
30 extern "C" errno_t __cdecl _cgets_s(char* const source_string, size_t const size_in_bytes, size_t* const size_read)
31 {
32     _VALIDATE_CLEAR_OSSERR_RETURN_ERRCODE(source_string != nullptr, EINVAL);
33     _VALIDATE_CLEAR_OSSERR_RETURN_ERRCODE(size_in_bytes > 0,        EINVAL);
34     _RESET_STRING(source_string, size_in_bytes);
35 
36     _VALIDATE_CLEAR_OSSERR_RETURN_ERRCODE(size_read != nullptr, EINVAL);
37 
38     errno_t error  = 0;
39     char*   string = source_string;
40 
41     __acrt_lock(__acrt_conio_lock);
42     __try
43     {
44         // The implementation of cgets is slightly tricky. The reason being,
45         // the code page for console is different from the CRT code page.
46         // What this means is the program may interpret character
47         // differently from it's acctual value. To fix this, what we really
48         // want to do is read the input as unicode string and then convert
49         // it to proper MBCS representation.
50         //
51         // This fix this we are really converting from Unicode to MBCS.
52         // This adds performance problem as we may endup doing this
53         // character by character. The basic problem here is that we have
54         // no way to know how many UNICODE characters will be needed to fit
55         // them in given size of MBCS buffer. To fix this issue we will be
56         // converting one Unicode character at a time to MBCS. This makes
57         // this slow, but then this is already console input,
58         *size_read = 0;
59 
60         size_t available = size_in_bytes - 1;
61         do
62         {
63             wchar_t wchar_buff[2];
64             size_t sizeRead = 0;
65 
66             error = _cgetws_s(wchar_buff, _countof(wchar_buff), &sizeRead);
67             if (error != 0)
68                 break;
69 
70             if (wchar_buff[0] == '\0')
71                 break;
72 
73             int size_converted = 0;
74             errno_t const wctomb_result = wctomb_s(&size_converted, string, available, wchar_buff[0]);
75             if (wctomb_result != 0)
76             {
77                 // Put the wide character back in the buffer so that the
78                 // unutilized wide character is still in the stream:
79                 __console_wchar_buffer = wchar_buff[0];
80                 __console_wchar_buffer_used = 1;
81                 break;
82             }
83 
84             string     += size_converted;
85             *size_read += size_converted;
86             available  -= size_converted;
87         }
88         while (available > 0);
89     }
90     __finally
91     {
92         __acrt_unlock(__acrt_conio_lock);
93     }
94     __endtry
95 
96     *string++ = '\0';
97 
98     if (error != 0)
99         errno = error;
100 
101     return error;
102 }
103 
104 
105 
106 // Reads a string from the console via ReadConsole on a cooked console handle.
107 // string[0] must contain the maximum length of the string.  The number of
108 // characters written is stored in string[1].  The return value is a pointer to
109 // string[2] on success; nullptr on failure.
110 extern "C" char* __cdecl _cgets(_Inout_z_ char* const string)
111 {
112     _VALIDATE_CLEAR_OSSERR_RETURN(string != nullptr, EINVAL, nullptr);
113     _VALIDATE_CLEAR_OSSERR_RETURN(string[0] > 0,     EINVAL, nullptr);
114 
115     size_t const size_in_bytes = static_cast<size_t>(string[0]);
116 
117     size_t size_read = 0;
118     // warning 26018: Potential overflow of null terminated buffer using expression string+2
119     // Suppressing warning since _cgets is purposefully unsafe.
120 #pragma warning(suppress:__WARNING_POTENTIAL_BUFFER_OVERFLOW_NULLTERMINATED)
121     errno_t const result = _cgets_s(string + 2, size_in_bytes, &size_read);
122 
123     // warning 26018: Potential overflow of null terminated buffer using expression string[1]
124     // Suppressing warning since _cgets is purposefully unsafe.
125 #pragma warning(suppress:__WARNING_POTENTIAL_BUFFER_OVERFLOW_NULLTERMINATED)
126     string[1] = static_cast<char>(size_read);
127 
128     return result == 0 ? string + 2 : nullptr;
129 }
130