303 lines
8.6 KiB
C
303 lines
8.6 KiB
C
/* Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; version 2 of the License.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
|
|
|
|
|
|
#include "mysys_priv.h"
|
|
#include "my_sys.h"
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
|
/* Windows console handling */
|
|
|
|
/*
|
|
TODO : Find a relationship between the following
|
|
two macros and get rid of one.
|
|
*/
|
|
|
|
/* Maximum line length on Windows console */
|
|
#define MAX_CONSOLE_LINE_SIZE 65535
|
|
|
|
/*
|
|
Maximum number of characters that can be entered
|
|
on single line in the console (including \r\n).
|
|
*/
|
|
#define MAX_NUM_OF_CHARS_TO_READ 24530
|
|
|
|
/**
|
|
Determine if a file is a windows console
|
|
|
|
@param file Input stream
|
|
|
|
@return
|
|
@retval 0 if file is not Windows console
|
|
@retval 1 if file is Windows console
|
|
*/
|
|
my_bool
|
|
my_win_is_console(FILE *file)
|
|
{
|
|
DWORD mode;
|
|
if (GetConsoleMode((HANDLE) _get_osfhandle(_fileno(file)), &mode))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
Read line from Windows console using Unicode API
|
|
and translate input to session character set.
|
|
Note, as Windows API breaks supplementary characters
|
|
into two wchar_t pieces, we cannot read and convert individual
|
|
wchar_t values separately. So let's use a buffer for
|
|
Unicode console input, and then convert it to "cs" in a single shot.
|
|
String is terminated with '\0' character.
|
|
|
|
@param cs [IN] Character string to convert to.
|
|
@param mbbuf [OUT] Write input data here.
|
|
@param mbbufsize [IN] Number of bytes available in mbbuf.
|
|
@param nread [OUT] Number of bytes read.
|
|
|
|
@retval Pointer to mbbuf, or NULL on I/0 error.
|
|
*/
|
|
char *
|
|
my_win_console_readline(const CHARSET_INFO *cs, char *mbbuf, size_t mbbufsize,
|
|
size_t *nread)
|
|
{
|
|
uint dummy_errors;
|
|
static wchar_t u16buf[MAX_CONSOLE_LINE_SIZE + 1];
|
|
size_t mblen= 0;
|
|
DWORD console_mode;
|
|
DWORD nchars;
|
|
|
|
HANDLE console= GetStdHandle(STD_INPUT_HANDLE);
|
|
|
|
DBUG_ASSERT(mbbufsize > 0); /* Need space for at least trailing '\0' */
|
|
GetConsoleMode(console, &console_mode);
|
|
SetConsoleMode(console, ENABLE_LINE_INPUT |
|
|
ENABLE_PROCESSED_INPUT | ENABLE_ECHO_INPUT);
|
|
|
|
if (!ReadConsoleW(console, u16buf, MAX_NUM_OF_CHARS_TO_READ, &nchars, NULL))
|
|
{
|
|
SetConsoleMode(console, console_mode);
|
|
return NULL;
|
|
}
|
|
|
|
*nread= nchars;
|
|
|
|
/* Set length of string */
|
|
if (nchars >= 2 && u16buf[nchars - 2] == L'\r')
|
|
nchars-= 2;
|
|
else if ((nchars == MAX_NUM_OF_CHARS_TO_READ) &&
|
|
(u16buf[nchars - 1] == L'\r'))
|
|
/* Special case 1 - \r\n straddles the boundary */
|
|
nchars--;
|
|
else if ((nchars == 1) && (u16buf[0] == L'\n'))
|
|
/* Special case 2 - read a single '\n'*/
|
|
nchars--;
|
|
|
|
SetConsoleMode(console, console_mode);
|
|
|
|
/* Convert Unicode to session character set */
|
|
if (nchars != 0)
|
|
mblen= my_convert(mbbuf, mbbufsize - 1, cs,
|
|
(const char *) u16buf, nchars * sizeof(wchar_t),
|
|
&my_charset_utf16le_bin, &dummy_errors);
|
|
|
|
DBUG_ASSERT(mblen < mbbufsize); /* Safety */
|
|
mbbuf[mblen]= 0;
|
|
return mbbuf;
|
|
}
|
|
|
|
|
|
/**
|
|
Translate client charset to Windows wchars for console I/O.
|
|
Unlike copy_and_convert(), in case of a wrong multi-byte sequence
|
|
we don't print '?' character, we fallback to ISO-8859-1 instead.
|
|
This gives a better idea how binary data (e.g. BLOB) look like.
|
|
|
|
@param cs Character set of the input string
|
|
@param from Input string
|
|
@param from_length Length of the input string
|
|
@param to[OUT] Write Unicode data here
|
|
@param to_chars Number of characters available in "to"
|
|
*/
|
|
static size_t
|
|
my_mbstou16s(const CHARSET_INFO *cs, const uchar * from, size_t from_length,
|
|
wchar_t *to, size_t to_chars)
|
|
{
|
|
const CHARSET_INFO *to_cs= &my_charset_utf16le_bin;
|
|
const uchar *from_end= from + from_length;
|
|
wchar_t *to_orig= to, *to_end= to + to_chars;
|
|
my_charset_conv_mb_wc mb_wc= cs->cset->mb_wc;
|
|
my_charset_conv_wc_mb wc_mb= to_cs->cset->wc_mb;
|
|
while (from < from_end)
|
|
{
|
|
int cnvres;
|
|
my_wc_t wc;
|
|
if ((cnvres= (*mb_wc)(cs, &wc, from, from_end)) > 0)
|
|
{
|
|
if (!wc)
|
|
break;
|
|
from+= cnvres;
|
|
}
|
|
else if (cnvres == MY_CS_ILSEQ)
|
|
{
|
|
wc= (my_wc_t) (uchar) *from; /* Fallback to ISO-8859-1 */
|
|
from+= 1;
|
|
}
|
|
else if (cnvres > MY_CS_TOOSMALL)
|
|
{
|
|
/*
|
|
A correct multibyte sequence detected
|
|
But it doesn't have Unicode mapping.
|
|
*/
|
|
wc= '?';
|
|
from+= (-cnvres); /* Note: cnvres is negative here */
|
|
}
|
|
else /* Incomplete character */
|
|
{
|
|
wc= (my_wc_t) (uchar) *from; /* Fallback to ISO-8859-1 */
|
|
from+= 1;
|
|
}
|
|
outp:
|
|
if ((cnvres= (*wc_mb)(to_cs, wc, (uchar *) to, (uchar *) to_end)) > 0)
|
|
{
|
|
/* We can never convert only a part of wchar_t */
|
|
DBUG_ASSERT((cnvres % sizeof(wchar_t)) == 0);
|
|
/* cnvres returns number of bytes, convert to number of wchar_t's */
|
|
to+= cnvres / sizeof(wchar_t);
|
|
}
|
|
else if (cnvres == MY_CS_ILUNI && wc != '?')
|
|
{
|
|
wc= '?';
|
|
goto outp;
|
|
}
|
|
else
|
|
break; /* Not enough space */
|
|
}
|
|
return to - to_orig;
|
|
}
|
|
|
|
|
|
/**
|
|
Write a string in the given character set to Windows console.
|
|
As Window breaks supplementary characters into two parts,
|
|
we cannot use a simple loop sending the result of
|
|
cs->cset->mb_wc() to console.
|
|
So we converts string from client charset to an array of wchar_t,
|
|
then write the array to console in a single shot.
|
|
|
|
@param cs Character set of the string
|
|
@param data String to print
|
|
@param datalen Length of input string in bytes
|
|
*/
|
|
void
|
|
my_win_console_write(const CHARSET_INFO *cs, const char *data, size_t datalen)
|
|
{
|
|
static wchar_t u16buf[MAX_CONSOLE_LINE_SIZE + 1];
|
|
size_t nchars= my_mbstou16s(cs, (const uchar *) data, datalen,
|
|
u16buf, sizeof(u16buf));
|
|
DWORD nwritten;
|
|
WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE),
|
|
u16buf, (DWORD) nchars, &nwritten, NULL);
|
|
}
|
|
|
|
|
|
/**
|
|
Write a single-byte character to console.
|
|
Note: one should not send parts of the same multi-byte character
|
|
in separate consequent my_win_console_putc() calls.
|
|
For multi-byte characters use my_win_colsole_write() instead.
|
|
|
|
@param cs Character set of the input character
|
|
@param c Character (single byte)
|
|
*/
|
|
void
|
|
my_win_console_putc(const CHARSET_INFO *cs, int c)
|
|
{
|
|
char ch= (char) c;
|
|
my_win_console_write(cs, &ch, 1);
|
|
}
|
|
|
|
|
|
/**
|
|
Write a 0-terminated string to Windows console.
|
|
|
|
@param cs Character set of the string to print
|
|
@param data String to print
|
|
*/
|
|
void
|
|
my_win_console_fputs(const CHARSET_INFO *cs, const char *data)
|
|
{
|
|
my_win_console_write(cs, data, strlen(data));
|
|
}
|
|
|
|
|
|
/*
|
|
Handle formatted output on the Windows console.
|
|
*/
|
|
void
|
|
my_win_console_vfprintf(const CHARSET_INFO *cs, const char *fmt, va_list args)
|
|
{
|
|
static char buff[MAX_CONSOLE_LINE_SIZE + 1];
|
|
size_t len= vsnprintf(buff, sizeof(buff) - 1, fmt, args);
|
|
my_win_console_write(cs, buff, len);
|
|
}
|
|
|
|
|
|
#include <shellapi.h>
|
|
|
|
/**
|
|
Translate Unicode command line parameters to the given character set
|
|
(Typically to utf8mb4).
|
|
Translated parameters are allocated using my_once_alloc().
|
|
|
|
@param tocs Character set to convert parameters to.
|
|
@param[OUT] argc Write number of parameters here
|
|
@param[OUT] argv Write pointer to allocated parameters here.
|
|
*/
|
|
int
|
|
my_win_translate_command_line_args(const CHARSET_INFO *cs, int *argc, char ***argv)
|
|
{
|
|
int i, ac;
|
|
char **av;
|
|
wchar_t *command_line= GetCommandLineW();
|
|
wchar_t **wargs= CommandLineToArgvW(command_line, &ac);
|
|
size_t nbytes= (ac + 1) * sizeof(char *);
|
|
|
|
/* Allocate new command line parameter */
|
|
av= (char **) my_once_alloc(nbytes, MYF(MY_ZEROFILL));
|
|
|
|
for(i= 0; i < ac; i++)
|
|
{
|
|
uint dummy_errors;
|
|
size_t arg_len= wcslen(wargs[i]);
|
|
size_t len, alloced_len= arg_len * cs->mbmaxlen + 1;
|
|
av[i]= (char *) my_once_alloc(alloced_len, MYF(0));
|
|
len= my_convert(av[i], alloced_len, cs,
|
|
(const char *) wargs[i], arg_len * sizeof(wchar_t),
|
|
&my_charset_utf16le_bin, &dummy_errors);
|
|
DBUG_ASSERT(len < alloced_len);
|
|
av[i][len]= '\0';
|
|
}
|
|
*argv= av;
|
|
*argc= ac;
|
|
/* Cleanup on exit */
|
|
LocalFree((HLOCAL) wargs);
|
|
return 0;
|
|
}
|
|
|
|
#endif /* _WIN32 */
|