104e0dc4aSTimo Kreuzer // 204e0dc4aSTimo Kreuzer // cgets.cpp 304e0dc4aSTimo Kreuzer // 404e0dc4aSTimo Kreuzer // Copyright (c) Microsoft Corporation. All rights reserved. 504e0dc4aSTimo Kreuzer // 604e0dc4aSTimo Kreuzer // Defines _cgets() and _cgets_s(), which read a string from the console. 704e0dc4aSTimo Kreuzer // 804e0dc4aSTimo Kreuzer #include <conio.h> 904e0dc4aSTimo Kreuzer #include <corecrt_internal_securecrt.h> 1004e0dc4aSTimo Kreuzer #include <stdlib.h> 1104e0dc4aSTimo Kreuzer 1204e0dc4aSTimo Kreuzer 1304e0dc4aSTimo Kreuzer 14*ffd69754STimo Kreuzer extern "C" { extern intptr_t __dcrt_lowio_console_input_handle; } 1504e0dc4aSTimo Kreuzer 1604e0dc4aSTimo Kreuzer 1704e0dc4aSTimo Kreuzer 1804e0dc4aSTimo Kreuzer // Use of the following buffer variables is primarily for syncronizing with 1904e0dc4aSTimo Kreuzer // _cgets_s. _cget_s fills the MBCS buffer and if the user passes in single 2004e0dc4aSTimo Kreuzer // character buffer and the unicode character is not converted to single byte 2104e0dc4aSTimo Kreuzer // MBC, then _cget_s should buffer that character so that next call to 2204e0dc4aSTimo Kreuzer // _cgetws_s can return the same character. 23*ffd69754STimo Kreuzer extern "C" { extern wchar_t __console_wchar_buffer; } 24*ffd69754STimo Kreuzer extern "C" { extern int __console_wchar_buffer_used; } 2504e0dc4aSTimo Kreuzer 2604e0dc4aSTimo Kreuzer 2704e0dc4aSTimo Kreuzer 2804e0dc4aSTimo Kreuzer // Reads a string from the console; always null-terminates the buffer. Returns 2904e0dc4aSTimo Kreuzer // 0 on success; returns an errno error code on failure. 3004e0dc4aSTimo Kreuzer extern "C" errno_t __cdecl _cgets_s(char* const source_string, size_t const size_in_bytes, size_t* const size_read) 3104e0dc4aSTimo Kreuzer { 3204e0dc4aSTimo Kreuzer _VALIDATE_CLEAR_OSSERR_RETURN_ERRCODE(source_string != nullptr, EINVAL); 3304e0dc4aSTimo Kreuzer _VALIDATE_CLEAR_OSSERR_RETURN_ERRCODE(size_in_bytes > 0, EINVAL); 3404e0dc4aSTimo Kreuzer _RESET_STRING(source_string, size_in_bytes); 3504e0dc4aSTimo Kreuzer 3604e0dc4aSTimo Kreuzer _VALIDATE_CLEAR_OSSERR_RETURN_ERRCODE(size_read != nullptr, EINVAL); 3704e0dc4aSTimo Kreuzer 3804e0dc4aSTimo Kreuzer errno_t error = 0; 3904e0dc4aSTimo Kreuzer char* string = source_string; 4004e0dc4aSTimo Kreuzer 4104e0dc4aSTimo Kreuzer __acrt_lock(__acrt_conio_lock); 4204e0dc4aSTimo Kreuzer __try 4304e0dc4aSTimo Kreuzer { 4404e0dc4aSTimo Kreuzer // The implementation of cgets is slightly tricky. The reason being, 4504e0dc4aSTimo Kreuzer // the code page for console is different from the CRT code page. 4604e0dc4aSTimo Kreuzer // What this means is the program may interpret character 4704e0dc4aSTimo Kreuzer // differently from it's acctual value. To fix this, what we really 4804e0dc4aSTimo Kreuzer // want to do is read the input as unicode string and then convert 4904e0dc4aSTimo Kreuzer // it to proper MBCS representation. 5004e0dc4aSTimo Kreuzer // 5104e0dc4aSTimo Kreuzer // This fix this we are really converting from Unicode to MBCS. 5204e0dc4aSTimo Kreuzer // This adds performance problem as we may endup doing this 5304e0dc4aSTimo Kreuzer // character by character. The basic problem here is that we have 5404e0dc4aSTimo Kreuzer // no way to know how many UNICODE characters will be needed to fit 5504e0dc4aSTimo Kreuzer // them in given size of MBCS buffer. To fix this issue we will be 5604e0dc4aSTimo Kreuzer // converting one Unicode character at a time to MBCS. This makes 5704e0dc4aSTimo Kreuzer // this slow, but then this is already console input, 5804e0dc4aSTimo Kreuzer *size_read = 0; 5904e0dc4aSTimo Kreuzer 6004e0dc4aSTimo Kreuzer size_t available = size_in_bytes - 1; 6104e0dc4aSTimo Kreuzer do 6204e0dc4aSTimo Kreuzer { 6304e0dc4aSTimo Kreuzer wchar_t wchar_buff[2]; 6404e0dc4aSTimo Kreuzer size_t sizeRead = 0; 6504e0dc4aSTimo Kreuzer 6604e0dc4aSTimo Kreuzer error = _cgetws_s(wchar_buff, _countof(wchar_buff), &sizeRead); 6704e0dc4aSTimo Kreuzer if (error != 0) 6804e0dc4aSTimo Kreuzer break; 6904e0dc4aSTimo Kreuzer 7004e0dc4aSTimo Kreuzer if (wchar_buff[0] == '\0') 7104e0dc4aSTimo Kreuzer break; 7204e0dc4aSTimo Kreuzer 7304e0dc4aSTimo Kreuzer int size_converted = 0; 7404e0dc4aSTimo Kreuzer errno_t const wctomb_result = wctomb_s(&size_converted, string, available, wchar_buff[0]); 7504e0dc4aSTimo Kreuzer if (wctomb_result != 0) 7604e0dc4aSTimo Kreuzer { 7704e0dc4aSTimo Kreuzer // Put the wide character back in the buffer so that the 7804e0dc4aSTimo Kreuzer // unutilized wide character is still in the stream: 7904e0dc4aSTimo Kreuzer __console_wchar_buffer = wchar_buff[0]; 8004e0dc4aSTimo Kreuzer __console_wchar_buffer_used = 1; 8104e0dc4aSTimo Kreuzer break; 8204e0dc4aSTimo Kreuzer } 8304e0dc4aSTimo Kreuzer 8404e0dc4aSTimo Kreuzer string += size_converted; 8504e0dc4aSTimo Kreuzer *size_read += size_converted; 8604e0dc4aSTimo Kreuzer available -= size_converted; 8704e0dc4aSTimo Kreuzer } 8804e0dc4aSTimo Kreuzer while (available > 0); 8904e0dc4aSTimo Kreuzer } 9004e0dc4aSTimo Kreuzer __finally 9104e0dc4aSTimo Kreuzer { 9204e0dc4aSTimo Kreuzer __acrt_unlock(__acrt_conio_lock); 9304e0dc4aSTimo Kreuzer } 9404e0dc4aSTimo Kreuzer 9504e0dc4aSTimo Kreuzer *string++ = '\0'; 9604e0dc4aSTimo Kreuzer 9704e0dc4aSTimo Kreuzer if (error != 0) 9804e0dc4aSTimo Kreuzer errno = error; 9904e0dc4aSTimo Kreuzer 10004e0dc4aSTimo Kreuzer return error; 10104e0dc4aSTimo Kreuzer } 10204e0dc4aSTimo Kreuzer 10304e0dc4aSTimo Kreuzer 10404e0dc4aSTimo Kreuzer 10504e0dc4aSTimo Kreuzer // Reads a string from the console via ReadConsole on a cooked console handle. 10604e0dc4aSTimo Kreuzer // string[0] must contain the maximum length of the string. The number of 10704e0dc4aSTimo Kreuzer // characters written is stored in string[1]. The return value is a pointer to 10804e0dc4aSTimo Kreuzer // string[2] on success; nullptr on failure. 10904e0dc4aSTimo Kreuzer extern "C" char* __cdecl _cgets(_Inout_z_ char* const string) 11004e0dc4aSTimo Kreuzer { 11104e0dc4aSTimo Kreuzer _VALIDATE_CLEAR_OSSERR_RETURN(string != nullptr, EINVAL, nullptr); 11204e0dc4aSTimo Kreuzer _VALIDATE_CLEAR_OSSERR_RETURN(string[0] > 0, EINVAL, nullptr); 11304e0dc4aSTimo Kreuzer 11404e0dc4aSTimo Kreuzer size_t const size_in_bytes = static_cast<size_t>(string[0]); 11504e0dc4aSTimo Kreuzer 11604e0dc4aSTimo Kreuzer size_t size_read = 0; 11704e0dc4aSTimo Kreuzer // warning 26018: Potential overflow of null terminated buffer using expression string+2 11804e0dc4aSTimo Kreuzer // Suppressing warning since _cgets is purposefully unsafe. 11904e0dc4aSTimo Kreuzer #pragma warning(suppress:__WARNING_POTENTIAL_BUFFER_OVERFLOW_NULLTERMINATED) 12004e0dc4aSTimo Kreuzer errno_t const result = _cgets_s(string + 2, size_in_bytes, &size_read); 12104e0dc4aSTimo Kreuzer 12204e0dc4aSTimo Kreuzer // warning 26018: Potential overflow of null terminated buffer using expression string[1] 12304e0dc4aSTimo Kreuzer // Suppressing warning since _cgets is purposefully unsafe. 12404e0dc4aSTimo Kreuzer #pragma warning(suppress:__WARNING_POTENTIAL_BUFFER_OVERFLOW_NULLTERMINATED) 12504e0dc4aSTimo Kreuzer string[1] = static_cast<char>(size_read); 12604e0dc4aSTimo Kreuzer 12704e0dc4aSTimo Kreuzer return result == 0 ? string + 2 : nullptr; 12804e0dc4aSTimo Kreuzer } 129