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