Skip to content

Commit fe01d0d

Browse files
mscdexjasnell
authored andcommitted
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 <[email protected]>
1 parent eb7c3fb commit fe01d0d

File tree

1 file changed

+45
-13
lines changed

1 file changed

+45
-13
lines changed

src/node.cc

+45-13
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());
@@ -1416,7 +1449,7 @@ void AppendExceptionLine(Environment* env,
14161449
return;
14171450
env->set_printed_error(true);
14181451
uv_tty_reset_mode();
1419-
fprintf(stderr, "\n%s", arrow);
1452+
PrintErrorString("\n%s", arrow);
14201453
}
14211454

14221455

@@ -1444,10 +1477,10 @@ static void ReportException(Environment* env,
14441477
// range errors have a trace member set to undefined
14451478
if (trace.length() > 0 && !trace_value->IsUndefined()) {
14461479
if (arrow.IsEmpty() || !arrow->IsString()) {
1447-
fprintf(stderr, "%s\n", *trace);
1480+
PrintErrorString("%s\n", *trace);
14481481
} else {
14491482
node::Utf8Value arrow_string(env->isolate(), arrow);
1450-
fprintf(stderr, "%s\n%s\n", *arrow_string, *trace);
1483+
PrintErrorString("%s\n%s\n", *arrow_string, *trace);
14511484
}
14521485
} else {
14531486
// this really only happens for RangeErrors, since they're the only
@@ -1468,20 +1501,19 @@ static void ReportException(Environment* env,
14681501
name->IsUndefined()) {
14691502
// Not an error object. Just print as-is.
14701503
node::Utf8Value message(env->isolate(), er);
1471-
fprintf(stderr, "%s\n", *message);
1504+
PrintErrorString("%s\n", *message);
14721505
} else {
14731506
node::Utf8Value name_string(env->isolate(), name);
14741507
node::Utf8Value message_string(env->isolate(), message);
14751508

14761509
if (arrow.IsEmpty() || !arrow->IsString()) {
1477-
fprintf(stderr, "%s: %s\n", *name_string, *message_string);
1510+
PrintErrorString("%s: %s\n", *name_string, *message_string);
14781511
} else {
14791512
node::Utf8Value arrow_string(env->isolate(), arrow);
1480-
fprintf(stderr,
1481-
"%s\n%s: %s\n",
1482-
*arrow_string,
1483-
*name_string,
1484-
*message_string);
1513+
PrintErrorString("%s\n%s: %s\n",
1514+
*arrow_string,
1515+
*name_string,
1516+
*message_string);
14851517
}
14861518
}
14871519
}
@@ -2176,9 +2208,9 @@ void DLOpen(const FunctionCallbackInfo<Value>& args) {
21762208

21772209
static void OnFatalError(const char* location, const char* message) {
21782210
if (location) {
2179-
fprintf(stderr, "FATAL ERROR: %s %s\n", location, message);
2211+
PrintErrorString("FATAL ERROR: %s %s\n", location, message);
21802212
} else {
2181-
fprintf(stderr, "FATAL ERROR: %s\n", message);
2213+
PrintErrorString("FATAL ERROR: %s\n", message);
21822214
}
21832215
fflush(stderr);
21842216
ABORT();
@@ -3002,7 +3034,7 @@ static void RawDebug(const FunctionCallbackInfo<Value>& args) {
30023034
CHECK(args.Length() == 1 && args[0]->IsString() &&
30033035
"must be called with a single string");
30043036
node::Utf8Value message(args.GetIsolate(), args[0]);
3005-
fprintf(stderr, "%s\n", *message);
3037+
PrintErrorString("%s\n", *message);
30063038
fflush(stderr);
30073039
}
30083040

0 commit comments

Comments
 (0)