diff options
| author | David Oberhollenzer <david.oberhollenzer@sigma-star.at> | 2022-03-10 21:22:36 +0100 | 
|---|---|---|
| committer | David Oberhollenzer <david.oberhollenzer@sigma-star.at> | 2022-03-10 21:53:29 +0100 | 
| commit | 6447b191eef8bf8f4942569e55c9a0f297c67a8e (patch) | |
| tree | 8d38fa052c522ac4cfd9440c1c5297fce0571f5c /lib | |
| parent | 8b0e45bd2cf2ae97d26e7922f9dbf6c9903bb7c6 (diff) | |
Windows: redirect standard I/O and convert text to UTF-16
Preprocessor magic is used to redirect putc/fputc/fputs/printf/fprintf
to custom implementations.
The custom implementations try to figure out if we are printing to the
console and, if so, convert the resulting strings to UTF-16 and print
them through ConsoleWriteW. If the output is redirected to a file or
a pipe, the original (presummed) UTF-8 is kept.
Simply setting the console output codepage to UTF-8 does not work,
because the standard I/O facilities of MSVCRT either does not support
unicode (in non-wchar mode), or has half-broken support through fputs,
which can still break up multi-byte sequences through its internal
buffering.
Likewise, changing the codepage and using ConsoleWriteA, or trying to
use fputws did not work in a test VM either.
This approach is the one that worked most consistently among the
ones tried, but also has problems. E.g. it breaks when setting the
codepage to UTF-8 manually (using `chcp 65001`).
Signed-off-by: David Oberhollenzer <david.oberhollenzer@sigma-star.at>
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/compat/Makemodule.am | 1 | ||||
| -rw-r--r-- | lib/compat/w32_stdio.c | 125 | 
2 files changed, 126 insertions, 0 deletions
| diff --git a/lib/compat/Makemodule.am b/lib/compat/Makemodule.am index f9dce2e..0426075 100644 --- a/lib/compat/Makemodule.am +++ b/lib/compat/Makemodule.am @@ -4,6 +4,7 @@ libcompat_a_SOURCES += lib/compat/chdir.c include/compat.h  libcompat_a_SOURCES += lib/compat/path_to_windows.c  libcompat_a_SOURCES += lib/compat/w32_perror.c  libcompat_a_SOURCES += lib/compat/w32_wmain.c +libcompat_a_SOURCES += lib/compat/w32_stdio.c  libcompat_a_SOURCES += lib/compat/fnmatch.c  libcompat_a_SOURCES += lib/compat/getopt.c  libcompat_a_SOURCES += lib/compat/getopt_long.c diff --git a/lib/compat/w32_stdio.c b/lib/compat/w32_stdio.c new file mode 100644 index 0000000..3124899 --- /dev/null +++ b/lib/compat/w32_stdio.c @@ -0,0 +1,125 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * w32_stdio.c + * + * Copyright (C) 2021 David Oberhollenzer <goliath@infraroot.at> + */ +#include "config.h" +#include "compat.h" + +#include <stdio.h> +#include <stdlib.h> + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <stdarg.h> + +#undef fputc +#undef putc +#undef fputs +#undef fprintf +#undef printf + +static HANDLE get_handle(FILE *strm) +{ +	if (strm != stdout && strm != stderr) +		return INVALID_HANDLE_VALUE; + +	return GetStdHandle(strm == stderr ? +			    STD_ERROR_HANDLE : STD_OUTPUT_HANDLE); +} + +static BOOL isatty(HANDLE hnd) +{ +	if (hnd == INVALID_HANDLE_VALUE) +		return FALSE; + +	return (GetFileType(hnd) == FILE_TYPE_CHAR); +} + +int stfs_tools_fputs(const char *str, FILE *strm) +{ +	DWORD length; +	WCHAR *wstr; +	HANDLE hnd; +	int ret; + +	hnd = get_handle(strm); +	if (!isatty(hnd)) +		return fputs(str, strm); + +	length = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0); +	if (length <= 0) +		return EOF; + +	wstr = calloc(sizeof(wstr[0]), length); +	if (wstr == NULL) +		return EOF; + +	MultiByteToWideChar(CP_UTF8, 0, str, -1, wstr, length); + +	ret = WriteConsoleW(hnd, wstr, length, NULL, NULL) ? length : EOF; + +	free(wstr); +	return ret; +} + +int stfs_tools_fputc(int c, FILE *strm) +{ +	char str[2]; + +	str[0] = c; +	str[1] = '\0'; + +	return stfs_tools_fputs(str, strm); +} + +static int sqfs_printf_common(FILE *out, const char *fmt, va_list ap) +{ +	int ret, len; +	char *str; + +	len = _vscprintf(fmt, ap); +	if (len == -1) +		return -1; + +	str = malloc((size_t)len + 1); +	if (str == NULL) +		return -1; + +	ret = vsprintf(str, fmt, ap); +	if (ret == -1) { +		free(str); +		return -1; +	} + +	if (stfs_tools_fputs(str, out) == EOF) +		ret = -1; + +	free(str); +	return ret; +} + +int stfs_tools_printf(const char *fmt, ...) +{ +	va_list ap; +	int ret; + +	va_start(ap, fmt); +	ret = sqfs_printf_common(stdout, fmt, ap); +	va_end(ap); +	return ret; +} + +int stfs_tools_fprintf(FILE *strm, const char *fmt, ...) +{ +	va_list ap; +	int ret; + +	va_start(ap, fmt); +	ret = sqfs_printf_common(strm, fmt, ap); +	va_end(ap); +	return ret; +} +#endif | 
