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