Reactos
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.
18extern "C" { wchar_t __console_wchar_buffer = 0; }
19extern "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.
25extern "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().
138extern "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}