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