1 /* Copyright (c) 2000, 2021, Oracle and/or its affiliates.
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License, version 2.0,
5 as published by the Free Software Foundation.
6
7 This program is also distributed with certain software (including
8 but not limited to OpenSSL) that is licensed under separate terms,
9 as designated in a particular file or component or in included license
10 documentation. The authors of MySQL hereby grant you an additional
11 permission to link the program and your derivative works with the
12 separately licensed software that they have included with MySQL.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License, version 2.0, for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
22
23
24 #include "mysys_priv.h"
25 #include "my_sys.h"
26
27 #ifdef _WIN32
28
29
30 /* Windows console handling */
31
32 /*
33 TODO : Find a relationship between the following
34 two macros and get rid of one.
35 */
36
37 /* Maximum line length on Windows console */
38 #define MAX_CONSOLE_LINE_SIZE 65535
39
40 /*
41 Maximum number of characters that can be entered
42 on single line in the console (including \r\n).
43 */
44 #define MAX_NUM_OF_CHARS_TO_READ 24530
45
46 /**
47 Determine if a file is a windows console
48
49 @param file Input stream
50
51 @return
52 @retval 0 if file is not Windows console
53 @retval 1 if file is Windows console
54 */
55 my_bool
my_win_is_console(FILE * file)56 my_win_is_console(FILE *file)
57 {
58 DWORD mode;
59 if (GetConsoleMode((HANDLE) _get_osfhandle(_fileno(file)), &mode))
60 return 1;
61 return 0;
62 }
63
64
65 /**
66 Read line from Windows console using Unicode API
67 and translate input to session character set.
68 Note, as Windows API breaks supplementary characters
69 into two wchar_t pieces, we cannot read and convert individual
70 wchar_t values separately. So let's use a buffer for
71 Unicode console input, and then convert it to "cs" in a single shot.
72 String is terminated with '\0' character.
73
74 @param cs [IN] Character string to convert to.
75 @param mbbuf [OUT] Write input data here.
76 @param mbbufsize [IN] Number of bytes available in mbbuf.
77 @param nread [OUT] Number of bytes read.
78
79 @retval Pointer to mbbuf, or NULL on I/0 error.
80 */
81 char *
my_win_console_readline(const CHARSET_INFO * cs,char * mbbuf,size_t mbbufsize,size_t * nread)82 my_win_console_readline(const CHARSET_INFO *cs, char *mbbuf, size_t mbbufsize,
83 size_t *nread)
84 {
85 uint dummy_errors;
86 static wchar_t u16buf[MAX_CONSOLE_LINE_SIZE + 1];
87 size_t mblen= 0;
88 DWORD console_mode;
89 DWORD nchars;
90
91 HANDLE console= GetStdHandle(STD_INPUT_HANDLE);
92
93 assert(mbbufsize > 0); /* Need space for at least trailing '\0' */
94 GetConsoleMode(console, &console_mode);
95 SetConsoleMode(console, ENABLE_LINE_INPUT |
96 ENABLE_PROCESSED_INPUT | ENABLE_ECHO_INPUT);
97
98 if (!ReadConsoleW(console, u16buf, MAX_NUM_OF_CHARS_TO_READ, &nchars, NULL))
99 {
100 SetConsoleMode(console, console_mode);
101 return NULL;
102 }
103
104 *nread= nchars;
105
106 /* Set length of string */
107 if (nchars >= 2 && u16buf[nchars - 2] == L'\r')
108 nchars-= 2;
109 else if ((nchars == MAX_NUM_OF_CHARS_TO_READ) &&
110 (u16buf[nchars - 1] == L'\r'))
111 /* Special case 1 - \r\n straddles the boundary */
112 nchars--;
113 else if ((nchars == 1) && (u16buf[0] == L'\n'))
114 /* Special case 2 - read a single '\n'*/
115 nchars--;
116
117 SetConsoleMode(console, console_mode);
118
119 /* Convert Unicode to session character set */
120 if (nchars != 0)
121 mblen= my_convert(mbbuf, mbbufsize - 1, cs,
122 (const char *) u16buf, nchars * sizeof(wchar_t),
123 &my_charset_utf16le_bin, &dummy_errors);
124
125 assert(mblen < mbbufsize); /* Safety */
126 mbbuf[mblen]= 0;
127 return mbbuf;
128 }
129
130
131 /**
132 Translate client charset to Windows wchars for console I/O.
133 Unlike copy_and_convert(), in case of a wrong multi-byte sequence
134 we don't print '?' character, we fallback to ISO-8859-1 instead.
135 This gives a better idea how binary data (e.g. BLOB) look like.
136
137 @param cs Character set of the input string
138 @param from Input string
139 @param from_length Length of the input string
140 @param to[OUT] Write Unicode data here
141 @param to_chars Number of characters available in "to"
142 */
143 static size_t
my_mbstou16s(const CHARSET_INFO * cs,const uchar * from,size_t from_length,wchar_t * to,size_t to_chars)144 my_mbstou16s(const CHARSET_INFO *cs, const uchar * from, size_t from_length,
145 wchar_t *to, size_t to_chars)
146 {
147 const CHARSET_INFO *to_cs= &my_charset_utf16le_bin;
148 const uchar *from_end= from + from_length;
149 wchar_t *to_orig= to, *to_end= to + to_chars;
150 my_charset_conv_mb_wc mb_wc= cs->cset->mb_wc;
151 my_charset_conv_wc_mb wc_mb= to_cs->cset->wc_mb;
152 while (from < from_end)
153 {
154 int cnvres;
155 my_wc_t wc;
156 if ((cnvres= (*mb_wc)(cs, &wc, from, from_end)) > 0)
157 {
158 if (!wc)
159 break;
160 from+= cnvres;
161 }
162 else if (cnvres == MY_CS_ILSEQ)
163 {
164 wc= (my_wc_t) (uchar) *from; /* Fallback to ISO-8859-1 */
165 from+= 1;
166 }
167 else if (cnvres > MY_CS_TOOSMALL)
168 {
169 /*
170 A correct multibyte sequence detected
171 But it doesn't have Unicode mapping.
172 */
173 wc= '?';
174 from+= (-cnvres); /* Note: cnvres is negative here */
175 }
176 else /* Incomplete character */
177 {
178 wc= (my_wc_t) (uchar) *from; /* Fallback to ISO-8859-1 */
179 from+= 1;
180 }
181 outp:
182 if ((cnvres= (*wc_mb)(to_cs, wc, (uchar *) to, (uchar *) to_end)) > 0)
183 {
184 /* We can never convert only a part of wchar_t */
185 assert((cnvres % sizeof(wchar_t)) == 0);
186 /* cnvres returns number of bytes, convert to number of wchar_t's */
187 to+= cnvres / sizeof(wchar_t);
188 }
189 else if (cnvres == MY_CS_ILUNI && wc != '?')
190 {
191 wc= '?';
192 goto outp;
193 }
194 else
195 break; /* Not enough space */
196 }
197 return to - to_orig;
198 }
199
200
201 /**
202 Write a string in the given character set to Windows console.
203 As Window breaks supplementary characters into two parts,
204 we cannot use a simple loop sending the result of
205 cs->cset->mb_wc() to console.
206 So we converts string from client charset to an array of wchar_t,
207 then write the array to console in a single shot.
208
209 @param cs Character set of the string
210 @param data String to print
211 @param datalen Length of input string in bytes
212 */
213 void
my_win_console_write(const CHARSET_INFO * cs,const char * data,size_t datalen)214 my_win_console_write(const CHARSET_INFO *cs, const char *data, size_t datalen)
215 {
216 static wchar_t u16buf[MAX_CONSOLE_LINE_SIZE + 1];
217 size_t nchars= my_mbstou16s(cs, (const uchar *) data, datalen,
218 u16buf, sizeof(u16buf) / sizeof(u16buf[0]));
219 DWORD nwritten;
220 WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE),
221 u16buf, (DWORD) nchars, &nwritten, NULL);
222 }
223
224
225 /**
226 Write a single-byte character to console.
227 Note: one should not send parts of the same multi-byte character
228 in separate consequent my_win_console_putc() calls.
229 For multi-byte characters use my_win_colsole_write() instead.
230
231 @param cs Character set of the input character
232 @param c Character (single byte)
233 */
234 void
my_win_console_putc(const CHARSET_INFO * cs,int c)235 my_win_console_putc(const CHARSET_INFO *cs, int c)
236 {
237 char ch= (char) c;
238 my_win_console_write(cs, &ch, 1);
239 }
240
241
242 /**
243 Write a 0-terminated string to Windows console.
244
245 @param cs Character set of the string to print
246 @param data String to print
247 */
248 void
my_win_console_fputs(const CHARSET_INFO * cs,const char * data)249 my_win_console_fputs(const CHARSET_INFO *cs, const char *data)
250 {
251 my_win_console_write(cs, data, strlen(data));
252 }
253
254
255 /*
256 Handle formatted output on the Windows console.
257 */
258 void
my_win_console_vfprintf(const CHARSET_INFO * cs,const char * fmt,va_list args)259 my_win_console_vfprintf(const CHARSET_INFO *cs, const char *fmt, va_list args)
260 {
261 static char buff[MAX_CONSOLE_LINE_SIZE + 1];
262 size_t len= vsnprintf(buff, sizeof(buff) - 1, fmt, args);
263 my_win_console_write(cs, buff, len);
264 }
265
266
267 #include <shellapi.h>
268
269 /**
270 Translate Unicode command line parameters to the given character set
271 (Typically to utf8mb4).
272 Translated parameters are allocated using my_once_alloc().
273
274 @param tocs Character set to convert parameters to.
275 @param[OUT] argc Write number of parameters here
276 @param[OUT] argv Write pointer to allocated parameters here.
277 */
278 int
my_win_translate_command_line_args(const CHARSET_INFO * cs,int * argc,char *** argv)279 my_win_translate_command_line_args(const CHARSET_INFO *cs, int *argc, char ***argv)
280 {
281 int i, ac;
282 char **av;
283 wchar_t *command_line= GetCommandLineW();
284 wchar_t **wargs= CommandLineToArgvW(command_line, &ac);
285 size_t nbytes= (ac + 1) * sizeof(char *);
286
287 /* Allocate new command line parameter */
288 av= (char **) my_once_alloc(nbytes, MYF(MY_ZEROFILL));
289
290 for(i= 0; i < ac; i++)
291 {
292 uint dummy_errors;
293 size_t arg_len= wcslen(wargs[i]);
294 size_t len, alloced_len= arg_len * cs->mbmaxlen + 1;
295 av[i]= (char *) my_once_alloc(alloced_len, MYF(0));
296 len= my_convert(av[i], alloced_len, cs,
297 (const char *) wargs[i], arg_len * sizeof(wchar_t),
298 &my_charset_utf16le_bin, &dummy_errors);
299 assert(len < alloced_len);
300 av[i][len]= '\0';
301 }
302 *argv= av;
303 *argc= ac;
304 /* Cleanup on exit */
305 LocalFree((HLOCAL) wargs);
306 return 0;
307 }
308
309 #endif /* _WIN32 */
310