Skip to content

Commit 06ede7a

Browse files
committed
Unicode printf on Windows.
1 parent 7426153 commit 06ede7a

1 file changed

Lines changed: 153 additions & 13 deletions

File tree

nob.h

Lines changed: 153 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,8 @@
164164
# include <direct.h>
165165
# include <io.h>
166166
# include <shellapi.h>
167+
# include <ConsoleApi.h>
168+
# include <ConsoleApi2.h>
167169
#else
168170
# ifdef __APPLE__
169171
# include <mach-o/dyld.h>
@@ -202,8 +204,8 @@
202204
#endif
203205

204206
#define NOB_UNUSED(value) (void)(value)
205-
#define NOB_TODO(message) do { fprintf(stderr, "%s:%d: TODO: %s\n", __FILE__, __LINE__, message); abort(); } while(0)
206-
#define NOB_UNREACHABLE(message) do { fprintf(stderr, "%s:%d: UNREACHABLE: %s\n", __FILE__, __LINE__, message); abort(); } while(0)
207+
#define NOB_TODO(message) do { nob__fprintf(stderr, "%s:%d: TODO: %s\n", __FILE__, __LINE__, message); abort(); } while(0)
208+
#define NOB_UNREACHABLE(message) do { nob__fprintf(stderr, "%s:%d: UNREACHABLE: %s\n", __FILE__, __LINE__, message); abort(); } while(0)
207209

208210
#define NOB_ARRAY_LEN(array) (sizeof(array)/sizeof(array[0]))
209211
#define NOB_ARRAY_GET(array, index) \
@@ -874,6 +876,144 @@ void nob__cmd_append(Nob_Cmd *cmd, size_t n, ...)
874876
va_end(args);
875877
}
876878

879+
880+
#ifdef _WIN32
881+
882+
883+
WORD nob__unicode_debug_beg(HANDLE win_out)
884+
{
885+
#ifdef NOB_DEBUG_UNIOCDE
886+
BOOL b;
887+
CONSOLE_SCREEN_BUFFER_INFO info;
888+
WORD attributes;
889+
890+
b = GetConsoleScreenBufferInfo(win_out, &info);
891+
NOB_ASSERT(b != 0);
892+
attributes = info.wAttributes;
893+
b = SetConsoleTextAttribute(win_out, FOREGROUND_INTENSITY | FOREGROUND_GREEN);
894+
NOB_ASSERT(b != 0);
895+
return attributes;
896+
#else
897+
(void)win_out;
898+
return 0;
899+
#endif
900+
}
901+
902+
void nob__unicode_debug_end(HANDLE win_out, WORD attributes)
903+
{
904+
#ifdef NOB_DEBUG_UNIOCDE
905+
BOOL b;
906+
907+
b = SetConsoleTextAttribute(win_out, attributes);
908+
NOB_ASSERT(b != 0);
909+
#else
910+
(void)win_out;
911+
(void)attributes;
912+
#endif
913+
}
914+
915+
int nob__vfprintf(FILE *stream, const char *format, va_list args)
916+
{
917+
char *narrow_ptr;
918+
char narrow_buf[1024];
919+
size_t narrow_mark;
920+
int narrow_len_a;
921+
int narrow_len_b;
922+
HANDLE win_out;
923+
DWORD file_type;
924+
wchar_t *wide_ptr;
925+
wchar_t wide_buf[1024];
926+
size_t wide_mark;
927+
int wide_len_a;
928+
DWORD err;
929+
int wide_len_b;
930+
WORD revert;
931+
BOOL b;
932+
DWORD written;
933+
934+
narrow_ptr = narrow_buf;
935+
narrow_mark = 0;
936+
narrow_len_a = vsnprintf(narrow_buf, NOB_ARRAY_LEN(narrow_buf), format, args);
937+
NOB_ASSERT(narrow_len_a >= 0); /* vsnprintf failed, what now? */
938+
if (narrow_len_a >= NOB_ARRAY_LEN(narrow_buf)) {
939+
narrow_mark = nob_temp_save();
940+
narrow_ptr = (char*)nob_temp_alloc(narrow_len_a + 1);
941+
NOB_ASSERT(narrow_ptr); /* nob_temp_alloc failed, what now? */
942+
narrow_len_b = vsnprintf(narrow_ptr, narrow_len_a + 1, format, args);
943+
NOB_ASSERT(narrow_len_b == narrow_len_a); /* Second call to vsnprintf produced different result thatn first call, what now? */
944+
}
945+
NOB_ASSERT(stream == stdout || stream == stderr); /* It is user error to print to stdin or to non-standard stream. */
946+
win_out = GetStdHandle(stream == stdout ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);
947+
NOB_ASSERT(win_out != INVALID_HANDLE_VALUE); /* GetStdHandle failed, what now? */
948+
if (win_out == NULL) {
949+
return 0;
950+
}
951+
file_type = GetFileType(win_out);
952+
if (file_type == FILE_TYPE_CHAR) {
953+
wide_ptr = wide_buf;
954+
wide_mark = 0;
955+
wide_len_a = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, narrow_ptr, narrow_len_a, wide_buf, NOB_ARRAY_LEN(wide_buf));
956+
err = GetLastError();
957+
NOB_ASSERT((wide_len_a != 0) || (wide_len_a == 0 && err == ERROR_INSUFFICIENT_BUFFER)); /* MultiByteToWideChar failed, what now? */
958+
if (wide_len_a == 0 && err == ERROR_INSUFFICIENT_BUFFER) {
959+
wide_len_a = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, narrow_ptr, narrow_len_a, NULL, 0);
960+
NOB_ASSERT(wide_len_a > NOB_ARRAY_LEN(wide_buf)); /* MultiByteToWideChar failed, what now? */
961+
wide_mark = nob_temp_save();
962+
wide_ptr = (wchar_t*)nob_temp_alloc(wide_len_a * sizeof(wchar_t));
963+
NOB_ASSERT(wide_ptr); /* nob_temp_alloc failed, what now? */
964+
wide_len_b = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, narrow_ptr, narrow_len_a, wide_ptr, wide_len_a);
965+
NOB_ASSERT(wide_len_b == wide_len_a); /* MultiByteToWideChar failed, what now? */
966+
}
967+
revert = nob__unicode_debug_beg(win_out);
968+
b = WriteConsoleW(win_out, wide_ptr, (DWORD)wide_len_a, &written, NULL);
969+
nob__unicode_debug_end(win_out, revert);
970+
NOB_ASSERT(b != 0); /* WriteConsoleW failed, what now? */
971+
NOB_ASSERT(written == (DWORD)wide_len_a); /* WriteConsoleW failed, what now? */
972+
if (wide_ptr != wide_buf) {
973+
nob_temp_rewind(wide_mark);
974+
}
975+
} else {
976+
b = WriteFile(win_out, narrow_ptr, (DWORD)narrow_len_a, &written, NULL);
977+
NOB_ASSERT(b != 0); /* WriteFile failed, what now? */
978+
NOB_ASSERT(written == (DWORD)narrow_len_a); /* WriteFile failed, what now? */
979+
}
980+
if (narrow_ptr != narrow_buf) {
981+
nob_temp_rewind(narrow_mark);
982+
}
983+
return narrow_len_a;
984+
}
985+
986+
int nob__fprintf(FILE *stream, const char *fmt, ...)
987+
{
988+
va_list args;
989+
int len;
990+
991+
va_start(args, fmt);
992+
len = nob__vfprintf(stream, fmt, args);
993+
va_end(args);
994+
return len;
995+
}
996+
997+
int nob__printf(const char *fmt, ...)
998+
{
999+
va_list args;
1000+
int len;
1001+
1002+
va_start(args, fmt);
1003+
len = nob__vfprintf(stdout, fmt, args);
1004+
va_end(args);
1005+
return len;
1006+
}
1007+
1008+
#else
1009+
1010+
#define nob__vfprintf vfprintf
1011+
#define nob__fprintf fprintf
1012+
#define nob__printf printf
1013+
1014+
#endif // _WIN32
1015+
1016+
8771017
#ifdef _WIN32
8781018

8791019

@@ -1738,42 +1878,42 @@ NOBDEF void nob_default_log_handler(Nob_Log_Level level, const char *fmt, va_lis
17381878

17391879
switch (level) {
17401880
case NOB_INFO:
1741-
fprintf(stderr, "[INFO] ");
1881+
nob__fprintf(stderr, "[INFO] ");
17421882
break;
17431883
case NOB_WARNING:
1744-
fprintf(stderr, "[WARNING] ");
1884+
nob__fprintf(stderr, "[WARNING] ");
17451885
break;
17461886
case NOB_ERROR:
1747-
fprintf(stderr, "[ERROR] ");
1887+
nob__fprintf(stderr, "[ERROR] ");
17481888
break;
17491889
case NOB_NO_LOGS: return;
17501890
default:
17511891
NOB_UNREACHABLE("Nob_Log_Level");
17521892
}
17531893

1754-
vfprintf(stderr, fmt, args);
1755-
fprintf(stderr, "\n");
1894+
nob__vfprintf(stderr, fmt, args);
1895+
nob__fprintf(stderr, "\n");
17561896
}
17571897

17581898
NOBDEF void nob_cancer_log_handler(Nob_Log_Level level, const char *fmt, va_list args)
17591899
{
17601900
switch (level) {
17611901
case NOB_INFO:
1762-
fprintf(stderr, "ℹ️ \x1b[36m[INFO]\x1b[0m ");
1902+
nob__fprintf(stderr, "ℹ️ \x1b[36m[INFO]\x1b[0m ");
17631903
break;
17641904
case NOB_WARNING:
1765-
fprintf(stderr, "⚠️ \x1b[33m[WARNING]\x1b[0m ");
1905+
nob__fprintf(stderr, "⚠️ \x1b[33m[WARNING]\x1b[0m ");
17661906
break;
17671907
case NOB_ERROR:
1768-
fprintf(stderr, "🚨 \x1b[31m[ERROR]\x1b[0m ");
1908+
nob__fprintf(stderr, "🚨 \x1b[31m[ERROR]\x1b[0m ");
17691909
break;
17701910
case NOB_NO_LOGS: return;
17711911
default:
17721912
NOB_UNREACHABLE("Nob_Log_Level");
17731913
}
17741914

1775-
vfprintf(stderr, fmt, args);
1776-
fprintf(stderr, "\n");
1915+
nob__vfprintf(stderr, fmt, args);
1916+
nob__fprintf(stderr, "\n");
17771917
}
17781918

17791919
NOBDEF void nob_log(Nob_Log_Level level, const char *fmt, ...)
@@ -2787,7 +2927,7 @@ NOBDEF char *nob_temp_running_executable_path(void)
27872927
break;
27882928
return nob_temp_strndup(info.name, strlen(info.name));
27892929
#else
2790-
fprintf(stderr, "%s:%d: TODO: nob_temp_running_executable_path is not implemented for this platform\n", __FILE__, __LINE__);
2930+
nob__fprintf(stderr, "%s:%d: TODO: nob_temp_running_executable_path is not implemented for this platform\n", __FILE__, __LINE__);
27912931
return nob_temp_strdup("");
27922932
#endif
27932933
}

0 commit comments

Comments
 (0)