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