xref: /reactos/sdk/lib/ucrt/conio/cgets.cpp (revision e98e9000)
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 
14ffd69754STimo 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.
23ffd69754STimo Kreuzer extern "C" { extern wchar_t __console_wchar_buffer; }
24ffd69754STimo 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.
_cgets_s(char * const source_string,size_t const size_in_bytes,size_t * const size_read)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     }
94*e98e9000STimo Kreuzer     __endtry
9504e0dc4aSTimo Kreuzer 
9604e0dc4aSTimo Kreuzer     *string++ = '\0';
9704e0dc4aSTimo Kreuzer 
9804e0dc4aSTimo Kreuzer     if (error != 0)
9904e0dc4aSTimo Kreuzer         errno = error;
10004e0dc4aSTimo Kreuzer 
10104e0dc4aSTimo Kreuzer     return error;
10204e0dc4aSTimo Kreuzer }
10304e0dc4aSTimo Kreuzer 
10404e0dc4aSTimo Kreuzer 
10504e0dc4aSTimo Kreuzer 
10604e0dc4aSTimo Kreuzer // Reads a string from the console via ReadConsole on a cooked console handle.
10704e0dc4aSTimo Kreuzer // string[0] must contain the maximum length of the string.  The number of
10804e0dc4aSTimo Kreuzer // characters written is stored in string[1].  The return value is a pointer to
10904e0dc4aSTimo Kreuzer // string[2] on success; nullptr on failure.
_cgets(_Inout_z_ char * const string)11004e0dc4aSTimo Kreuzer extern "C" char* __cdecl _cgets(_Inout_z_ char* const string)
11104e0dc4aSTimo Kreuzer {
11204e0dc4aSTimo Kreuzer     _VALIDATE_CLEAR_OSSERR_RETURN(string != nullptr, EINVAL, nullptr);
11304e0dc4aSTimo Kreuzer     _VALIDATE_CLEAR_OSSERR_RETURN(string[0] > 0,     EINVAL, nullptr);
11404e0dc4aSTimo Kreuzer 
11504e0dc4aSTimo Kreuzer     size_t const size_in_bytes = static_cast<size_t>(string[0]);
11604e0dc4aSTimo Kreuzer 
11704e0dc4aSTimo Kreuzer     size_t size_read = 0;
11804e0dc4aSTimo Kreuzer     // warning 26018: Potential overflow of null terminated buffer using expression string+2
11904e0dc4aSTimo Kreuzer     // Suppressing warning since _cgets is purposefully unsafe.
12004e0dc4aSTimo Kreuzer #pragma warning(suppress:__WARNING_POTENTIAL_BUFFER_OVERFLOW_NULLTERMINATED)
12104e0dc4aSTimo Kreuzer     errno_t const result = _cgets_s(string + 2, size_in_bytes, &size_read);
12204e0dc4aSTimo Kreuzer 
12304e0dc4aSTimo Kreuzer     // warning 26018: Potential overflow of null terminated buffer using expression string[1]
12404e0dc4aSTimo Kreuzer     // Suppressing warning since _cgets is purposefully unsafe.
12504e0dc4aSTimo Kreuzer #pragma warning(suppress:__WARNING_POTENTIAL_BUFFER_OVERFLOW_NULLTERMINATED)
12604e0dc4aSTimo Kreuzer     string[1] = static_cast<char>(size_read);
12704e0dc4aSTimo Kreuzer 
12804e0dc4aSTimo Kreuzer     return result == 0 ? string + 2 : nullptr;
12904e0dc4aSTimo Kreuzer }
130