Skip to content

Commit 2d35607

Browse files
committed
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: #3288 Fixes: #3284 Reviewed-By: Bert Belder <bertbelder@gmail.com>
1 parent 0a7076b commit 2d35607

File tree

1 file changed

+45
-13
lines changed

1 file changed

+45
-13
lines changed

src/node.cc

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
#include <stdlib.h>
5252
#include <string.h>
5353
#include <sys/types.h>
54+
#include <vector>
5455

5556
#if defined(NODE_HAVE_I18N_SUPPORT)
5657
#include <unicode/uvernum.h>
@@ -156,6 +157,38 @@ static Isolate* node_isolate = nullptr;
156157
static v8::Platform* default_platform;
157158

158159

160+
static void PrintErrorString(const char* format, ...) {
161+
va_list ap;
162+
va_start(ap, format);
163+
#ifdef _WIN32
164+
HANDLE stderr_handle = GetStdHandle(STD_ERROR_HANDLE);
165+
166+
// Check if stderr is something other than a tty/console
167+
if (stderr_handle == INVALID_HANDLE_VALUE ||
168+
stderr_handle == nullptr ||
169+
uv_guess_handle(_fileno(stderr)) != UV_TTY) {
170+
vfprintf(stderr, format, ap);
171+
return;
172+
}
173+
174+
// Fill in any placeholders
175+
int n = _vscprintf(format, ap);
176+
std::vector<char> out(n + 1);
177+
vsprintf(out.data(), format, ap);
178+
179+
// Get required wide buffer size
180+
n = MultiByteToWideChar(CP_UTF8, 0, out.data(), -1, nullptr, 0);
181+
182+
std::vector<wchar_t> wbuf(n);
183+
MultiByteToWideChar(CP_UTF8, 0, out.data(), -1, wbuf.data(), n);
184+
WriteConsoleW(stderr_handle, wbuf.data(), n, nullptr, nullptr);
185+
#else
186+
vfprintf(stderr, format, ap);
187+
#endif
188+
va_end(ap);
189+
}
190+
191+
159192
static void CheckImmediate(uv_check_t* handle) {
160193
Environment* env = Environment::from_immediate_check_handle(handle);
161194
HandleScope scope(env->isolate());
@@ -1404,7 +1437,7 @@ void AppendExceptionLine(Environment* env,
14041437
return;
14051438
env->set_printed_error(true);
14061439
uv_tty_reset_mode();
1407-
fprintf(stderr, "\n%s", arrow);
1440+
PrintErrorString("\n%s", arrow);
14081441
}
14091442

14101443

@@ -1432,10 +1465,10 @@ static void ReportException(Environment* env,
14321465
// range errors have a trace member set to undefined
14331466
if (trace.length() > 0 && !trace_value->IsUndefined()) {
14341467
if (arrow.IsEmpty() || !arrow->IsString()) {
1435-
fprintf(stderr, "%s\n", *trace);
1468+
PrintErrorString("%s\n", *trace);
14361469
} else {
14371470
node::Utf8Value arrow_string(env->isolate(), arrow);
1438-
fprintf(stderr, "%s\n%s\n", *arrow_string, *trace);
1471+
PrintErrorString("%s\n%s\n", *arrow_string, *trace);
14391472
}
14401473
} else {
14411474
// this really only happens for RangeErrors, since they're the only
@@ -1456,20 +1489,19 @@ static void ReportException(Environment* env,
14561489
name->IsUndefined()) {
14571490
// Not an error object. Just print as-is.
14581491
node::Utf8Value message(env->isolate(), er);
1459-
fprintf(stderr, "%s\n", *message);
1492+
PrintErrorString("%s\n", *message);
14601493
} else {
14611494
node::Utf8Value name_string(env->isolate(), name);
14621495
node::Utf8Value message_string(env->isolate(), message);
14631496

14641497
if (arrow.IsEmpty() || !arrow->IsString()) {
1465-
fprintf(stderr, "%s: %s\n", *name_string, *message_string);
1498+
PrintErrorString("%s: %s\n", *name_string, *message_string);
14661499
} else {
14671500
node::Utf8Value arrow_string(env->isolate(), arrow);
1468-
fprintf(stderr,
1469-
"%s\n%s: %s\n",
1470-
*arrow_string,
1471-
*name_string,
1472-
*message_string);
1501+
PrintErrorString("%s\n%s: %s\n",
1502+
*arrow_string,
1503+
*name_string,
1504+
*message_string);
14731505
}
14741506
}
14751507
}
@@ -2164,9 +2196,9 @@ void DLOpen(const FunctionCallbackInfo<Value>& args) {
21642196

21652197
static void OnFatalError(const char* location, const char* message) {
21662198
if (location) {
2167-
fprintf(stderr, "FATAL ERROR: %s %s\n", location, message);
2199+
PrintErrorString("FATAL ERROR: %s %s\n", location, message);
21682200
} else {
2169-
fprintf(stderr, "FATAL ERROR: %s\n", message);
2201+
PrintErrorString("FATAL ERROR: %s\n", message);
21702202
}
21712203
fflush(stderr);
21722204
ABORT();
@@ -2985,7 +3017,7 @@ static void RawDebug(const FunctionCallbackInfo<Value>& args) {
29853017
CHECK(args.Length() == 1 && args[0]->IsString() &&
29863018
"must be called with a single string");
29873019
node::Utf8Value message(args.GetIsolate(), args[0]);
2988-
fprintf(stderr, "%s\n", *message);
3020+
PrintErrorString("%s\n", *message);
29893021
fflush(stderr);
29903022
}
29913023

0 commit comments

Comments
 (0)