From fe01d0df7a43596020de3a6a2b6180c8a97d7d90 Mon Sep 17 00:00:00 2001 From: Brian White Date: Tue, 13 Oct 2015 17:18:15 -0400 Subject: [PATCH] src: fix exception message encoding on Windows The printf family of functions do not properly display UTF8 strings well on Windows. Use the appropriate wide character API instead if stderr is a tty. PR-URL: https://github.com/nodejs/node/pull/3288 Fixes: https://github.com/nodejs/node/issues/3284 Reviewed-By: Bert Belder --- src/node.cc | 58 +++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 45 insertions(+), 13 deletions(-) diff --git a/src/node.cc b/src/node.cc index b80996e..88b1a0b 100644 --- a/src/node.cc +++ b/src/node.cc @@ -51,6 +51,7 @@ #include #include #include +#include #if defined(NODE_HAVE_I18N_SUPPORT) #include @@ -156,6 +157,38 @@ static Isolate* node_isolate = nullptr; static v8::Platform* default_platform; +static void PrintErrorString(const char* format, ...) { + va_list ap; + va_start(ap, format); +#ifdef _WIN32 + HANDLE stderr_handle = GetStdHandle(STD_ERROR_HANDLE); + + // Check if stderr is something other than a tty/console + if (stderr_handle == INVALID_HANDLE_VALUE || + stderr_handle == nullptr || + uv_guess_handle(_fileno(stderr)) != UV_TTY) { + vfprintf(stderr, format, ap); + return; + } + + // Fill in any placeholders + int n = _vscprintf(format, ap); + std::vector out(n + 1); + vsprintf(out.data(), format, ap); + + // Get required wide buffer size + n = MultiByteToWideChar(CP_UTF8, 0, out.data(), -1, nullptr, 0); + + std::vector wbuf(n); + MultiByteToWideChar(CP_UTF8, 0, out.data(), -1, wbuf.data(), n); + WriteConsoleW(stderr_handle, wbuf.data(), n, nullptr, nullptr); +#else + vfprintf(stderr, format, ap); +#endif + va_end(ap); +} + + static void CheckImmediate(uv_check_t* handle) { Environment* env = Environment::from_immediate_check_handle(handle); HandleScope scope(env->isolate()); @@ -1416,7 +1449,7 @@ void AppendExceptionLine(Environment* env, return; env->set_printed_error(true); uv_tty_reset_mode(); - fprintf(stderr, "\n%s", arrow); + PrintErrorString("\n%s", arrow); } @@ -1444,10 +1477,10 @@ static void ReportException(Environment* env, // range errors have a trace member set to undefined if (trace.length() > 0 && !trace_value->IsUndefined()) { if (arrow.IsEmpty() || !arrow->IsString()) { - fprintf(stderr, "%s\n", *trace); + PrintErrorString("%s\n", *trace); } else { node::Utf8Value arrow_string(env->isolate(), arrow); - fprintf(stderr, "%s\n%s\n", *arrow_string, *trace); + PrintErrorString("%s\n%s\n", *arrow_string, *trace); } } else { // this really only happens for RangeErrors, since they're the only @@ -1468,20 +1501,19 @@ static void ReportException(Environment* env, name->IsUndefined()) { // Not an error object. Just print as-is. node::Utf8Value message(env->isolate(), er); - fprintf(stderr, "%s\n", *message); + PrintErrorString("%s\n", *message); } else { node::Utf8Value name_string(env->isolate(), name); node::Utf8Value message_string(env->isolate(), message); if (arrow.IsEmpty() || !arrow->IsString()) { - fprintf(stderr, "%s: %s\n", *name_string, *message_string); + PrintErrorString("%s: %s\n", *name_string, *message_string); } else { node::Utf8Value arrow_string(env->isolate(), arrow); - fprintf(stderr, - "%s\n%s: %s\n", - *arrow_string, - *name_string, - *message_string); + PrintErrorString("%s\n%s: %s\n", + *arrow_string, + *name_string, + *message_string); } } } @@ -2176,9 +2208,9 @@ void DLOpen(const FunctionCallbackInfo& args) { static void OnFatalError(const char* location, const char* message) { if (location) { - fprintf(stderr, "FATAL ERROR: %s %s\n", location, message); + PrintErrorString("FATAL ERROR: %s %s\n", location, message); } else { - fprintf(stderr, "FATAL ERROR: %s\n", message); + PrintErrorString("FATAL ERROR: %s\n", message); } fflush(stderr); ABORT(); @@ -3002,7 +3034,7 @@ static void RawDebug(const FunctionCallbackInfo& args) { CHECK(args.Length() == 1 && args[0]->IsString() && "must be called with a single string"); node::Utf8Value message(args.GetIsolate(), args[0]); - fprintf(stderr, "%s\n", *message); + PrintErrorString("%s\n", *message); fflush(stderr); } -- 2.7.4