From 240c2409e341f389edf2865c7913d2b010f5223d Mon Sep 17 00:00:00 2001 From: KxD-Work Date: Wed, 8 Jan 2025 16:15:46 -0400 Subject: [PATCH 01/27] add ifdef to minirent.h static functions to avoid warnings --- nob.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nob.h b/nob.h index 022a98e..76c8b4e 100644 --- a/nob.h +++ b/nob.h @@ -545,9 +545,11 @@ struct dirent typedef struct DIR DIR; +#ifdef NOB_IMPLEMENTATION static DIR *opendir(const char *dirpath); static struct dirent *readdir(DIR *dirp); static int closedir(DIR *dirp); +#endif // NOB_IMPLEMENTATION #endif // _WIN32 // minirent.h HEADER END //////////////////////////////////////// From e581be799e69d8ec5f34aba39a1affc5dcc95286 Mon Sep 17 00:00:00 2001 From: KxD-Work Date: Wed, 8 Jan 2025 16:20:37 -0400 Subject: [PATCH 02/27] refactor nob_win32_error_message, nob_mkdir_if_not_exists, nob_file_exists, using ascii versions of some functions --- nob.h | 61 ++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/nob.h b/nob.h index 76c8b4e..8cc9c2a 100644 --- a/nob.h +++ b/nob.h @@ -583,18 +583,17 @@ char *nob_win32_error_message(DWORD err) { NOB_WIN32_ERR_MSG_SIZE, NULL); if (errMsgSize == 0) { - if (GetLastError() != ERROR_MR_MID_NOT_FOUND) { - if (sprintf(win32ErrMsg, "Could not get error message for 0x%lX", err) > 0) { - return (char *)&win32ErrMsg; + char *newErrMsg = NULL; + if (GetLastError() == ERROR_MR_MID_NOT_FOUND) { + newErrMsg = "Invalid Win32 error code"; } else { - return NULL; + newErrMsg = "Could not get error message"; } - } else { - if (sprintf(win32ErrMsg, "Invalid Windows Error code (0x%lX)", err) > 0) { + + if (snprintf(win32ErrMsg, sizeof(win32ErrMsg), "%s for 0x%lX", newErrMsg, err) > 0) { return (char *)&win32ErrMsg; } else { - return NULL; - } + return newErrMsg; } } @@ -646,10 +645,19 @@ static char nob_temp[NOB_TEMP_CAPACITY] = {0}; bool nob_mkdir_if_not_exists(const char *path) { #ifdef _WIN32 - int result = mkdir(path); + int result = CreateDirectoryA(path, NULL); + if (result == 0) { + DWORD err = GetLastError(); + if (err == ERROR_ALREADY_EXISTS) { + nob_log(NOB_INFO, "directory `%s` already exists", path); + return true; + } + nob_log(NOB_ERROR, "could not create directory `%s`: %s", path, nob_win32_error_message(err)); + return false; + } #else int result = mkdir(path, 0755); -#endif + if (result < 0) { if (errno == EEXIST) { nob_log(NOB_INFO, "directory `%s` already exists", path); @@ -658,7 +666,7 @@ bool nob_mkdir_if_not_exists(const char *path) nob_log(NOB_ERROR, "could not create directory `%s`: %s", path, strerror(errno)); return false; } - +#endif nob_log(NOB_INFO, "created directory `%s`", path); return true; } @@ -667,7 +675,7 @@ bool nob_copy_file(const char *src_path, const char *dst_path) { nob_log(NOB_INFO, "copying %s -> %s", src_path, dst_path); #ifdef _WIN32 - if (!CopyFile(src_path, dst_path, FALSE)) { + if (!CopyFileA(src_path, dst_path, FALSE)) { nob_log(NOB_ERROR, "Could not copy file: %s", nob_win32_error_message(GetLastError())); return false; } @@ -758,9 +766,9 @@ Nob_Proc nob_cmd_run_async_redirect(Nob_Cmd cmd, Nob_Cmd_Redirect redirect) #ifdef _WIN32 // https://docs.microsoft.com/en-us/windows/win32/procthread/creating-a-child-process-with-redirected-input-and-output - STARTUPINFO siStartInfo; + STARTUPINFOA siStartInfo; ZeroMemory(&siStartInfo, sizeof(siStartInfo)); - siStartInfo.cb = sizeof(STARTUPINFO); + siStartInfo.cb = sizeof(STARTUPINFOA); // NOTE: theoretically setting NULL to std handles should not be a problem // https://docs.microsoft.com/en-us/windows/console/getstdhandle?redirectedfrom=MSDN#attachdetach-behavior // TODO: check for errors in GetStdHandle @@ -874,7 +882,7 @@ Nob_Fd nob_fd_open_for_read(const char *path) saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); saAttr.bInheritHandle = TRUE; - Nob_Fd result = CreateFile( + Nob_Fd result = CreateFileA( path, GENERIC_READ, 0, @@ -908,7 +916,7 @@ Nob_Fd nob_fd_open_for_write(const char *path) saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); saAttr.bInheritHandle = TRUE; - Nob_Fd result = CreateFile( + Nob_Fd result = CreateFileA( path, // name of the write GENERIC_WRITE, // open for writing 0, // do not share @@ -1300,7 +1308,7 @@ int nob_needs_rebuild(const char *output_path, const char **input_paths, size_t #ifdef _WIN32 BOOL bSuccess; - HANDLE output_path_fd = CreateFile(output_path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); + HANDLE output_path_fd = CreateFileA(output_path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); if (output_path_fd == INVALID_HANDLE_VALUE) { // NOTE: if output does not exist it 100% must be rebuilt if (GetLastError() == ERROR_FILE_NOT_FOUND) return 1; @@ -1317,7 +1325,7 @@ int nob_needs_rebuild(const char *output_path, const char **input_paths, size_t for (size_t i = 0; i < input_paths_count; ++i) { const char *input_path = input_paths[i]; - HANDLE input_path_fd = CreateFile(input_path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); + HANDLE input_path_fd = CreateFileA(input_path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); if (input_path_fd == INVALID_HANDLE_VALUE) { // NOTE: non-existing input is an error cause it is needed for building in the first place nob_log(NOB_ERROR, "Could not open file %s: %s", input_path, nob_win32_error_message(GetLastError())); @@ -1385,7 +1393,7 @@ bool nob_rename(const char *old_path, const char *new_path) { nob_log(NOB_INFO, "renaming %s -> %s", old_path, new_path); #ifdef _WIN32 - if (!MoveFileEx(old_path, new_path, MOVEFILE_REPLACE_EXISTING)) { + if (!MoveFileExA(old_path, new_path, MOVEFILE_REPLACE_EXISTING)) { nob_log(NOB_ERROR, "could not rename %s to %s: %s", old_path, new_path, nob_win32_error_message(GetLastError())); return false; } @@ -1514,9 +1522,14 @@ bool nob_sv_end_with(Nob_String_View sv, const char *cstr) int nob_file_exists(const char *file_path) { #if _WIN32 - // TODO: distinguish between "does not exists" and other errors DWORD dwAttrib = GetFileAttributesA(file_path); - return dwAttrib != INVALID_FILE_ATTRIBUTES; + if(dwAttrib == INVALID_FILE_ATTRIBUTES){ + DWORD err = GetLastError(); + if (err == ERROR_FILE_NOT_FOUND) return 0; + nob_log(NOB_ERROR, "Could not check if file %s exists: %s", file_path, nob_win32_error_message(err)); + return -1; + } + return 1; #else struct stat statbuf; if (stat(file_path, &statbuf) < 0) { @@ -1531,14 +1544,14 @@ int nob_file_exists(const char *file_path) const char *nob_get_current_dir_temp() { #ifdef _WIN32 - DWORD nBufferLength = GetCurrentDirectory(0, NULL); + DWORD nBufferLength = GetCurrentDirectoryA(0, NULL); if (nBufferLength == 0) { nob_log(NOB_ERROR, "could not get current directory: %s", nob_win32_error_message(GetLastError())); return NULL; } char *buffer = (char*) nob_temp_alloc(nBufferLength); - if (GetCurrentDirectory(nBufferLength, buffer) == 0) { + if (GetCurrentDirectoryA(nBufferLength, buffer) == 0) { nob_log(NOB_ERROR, "could not get current directory: %s", nob_win32_error_message(GetLastError())); return NULL; } @@ -1558,7 +1571,7 @@ const char *nob_get_current_dir_temp() bool nob_set_current_dir(const char *path) { #ifdef _WIN32 - if (!SetCurrentDirectory(path)) { + if (!SetCurrentDirectoryA(path)) { nob_log(NOB_ERROR, "could not set current directory to %s: %s", path, nob_win32_error_message(GetLastError())); return false; } From 045e2933f8f6dd8363d8a18a4797303e303dcb17 Mon Sep 17 00:00:00 2001 From: KillerxDBr <97485370+KillerxDBr@users.noreply.github.com> Date: Mon, 27 Jan 2025 23:01:42 -0400 Subject: [PATCH 03/27] fix for win32 nob_file_exists returning zero if file OR dir not found --- nob.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nob.h b/nob.h index 8cc9c2a..0b2bd41 100644 --- a/nob.h +++ b/nob.h @@ -1525,7 +1525,7 @@ int nob_file_exists(const char *file_path) DWORD dwAttrib = GetFileAttributesA(file_path); if(dwAttrib == INVALID_FILE_ATTRIBUTES){ DWORD err = GetLastError(); - if (err == ERROR_FILE_NOT_FOUND) return 0; + if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) return 0; nob_log(NOB_ERROR, "Could not check if file %s exists: %s", file_path, nob_win32_error_message(err)); return -1; } From b7c1bd30a6c3226252767df83377f8151799f749 Mon Sep 17 00:00:00 2001 From: KxD-Work Date: Tue, 4 Feb 2025 11:51:39 -0400 Subject: [PATCH 04/27] fix indentation in nob_win32_error_message --- nob.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/nob.h b/nob.h index 5d1ea9e..7e2605d 100644 --- a/nob.h +++ b/nob.h @@ -614,13 +614,13 @@ char *nob_win32_error_message(DWORD err) { char *newErrMsg = NULL; if (GetLastError() == ERROR_MR_MID_NOT_FOUND) { newErrMsg = "Invalid Win32 error code"; - } else { + } else { newErrMsg = "Could not get error message"; - } + } if (snprintf(win32ErrMsg, sizeof(win32ErrMsg), "%s for 0x%lX", newErrMsg, err) > 0) { - return (char *)&win32ErrMsg; - } else { + return (char *)&win32ErrMsg; + } else { return newErrMsg; } } From b050a869c9100a79374731dba02a70dc40f55ab1 Mon Sep 17 00:00:00 2001 From: KillerxDBr <97485370+KillerxDBr@users.noreply.github.com> Date: Sat, 15 Feb 2025 21:07:52 -0400 Subject: [PATCH 05/27] Update minirent to use WinApi ANSI functions --- nob.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/nob.h b/nob.h index 7e2605d..92ffee5 100644 --- a/nob.h +++ b/nob.h @@ -564,7 +564,7 @@ Nob_String_View nob_sv_from_parts(const char *data, size_t count); #else // _WIN32 #define WIN32_LEAN_AND_MEAN -#include "windows.h" +#include struct dirent { @@ -1661,7 +1661,7 @@ bool nob_set_current_dir(const char *path) struct DIR { HANDLE hFind; - WIN32_FIND_DATA data; + WIN32_FIND_DATAA data; struct dirent *dirent; }; @@ -1675,7 +1675,7 @@ DIR *opendir(const char *dirpath) DIR *dir = (DIR*)NOB_REALLOC(NULL, sizeof(DIR)); memset(dir, 0, sizeof(DIR)); - dir->hFind = FindFirstFile(buffer, &dir->data); + dir->hFind = FindFirstFileA(buffer, &dir->data); if (dir->hFind == INVALID_HANDLE_VALUE) { // TODO: opendir should set errno accordingly on FindFirstFile fail // https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror @@ -1701,7 +1701,7 @@ struct dirent *readdir(DIR *dirp) dirp->dirent = (struct dirent*)NOB_REALLOC(NULL, sizeof(struct dirent)); memset(dirp->dirent, 0, sizeof(struct dirent)); } else { - if(!FindNextFile(dirp->hFind, &dirp->data)) { + if(!FindNextFileA(dirp->hFind, &dirp->data)) { if (GetLastError() != ERROR_NO_MORE_FILES) { // TODO: readdir should set errno accordingly on FindNextFile fail // https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror From 2e39e85ead72952702c631b69b99ed05cfda02d9 Mon Sep 17 00:00:00 2001 From: KillerxDBr <97485370+KillerxDBr@users.noreply.github.com> Date: Mon, 10 Mar 2025 20:27:32 -0400 Subject: [PATCH 06/27] Fix 'nob_read_entire_file' to handle files larger than 2GB on Win32 --- nob.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/nob.h b/nob.h index ae89d65..5daa539 100644 --- a/nob.h +++ b/nob.h @@ -1498,7 +1498,13 @@ bool nob_read_entire_file(const char *path, Nob_String_Builder *sb) FILE *f = fopen(path, "rb"); if (f == NULL) nob_return_defer(false); if (fseek(f, 0, SEEK_END) < 0) nob_return_defer(false); + +#ifndef _WIN32 long m = ftell(f); +#else + long long m = _ftelli64(f); +#endif + if (m < 0) nob_return_defer(false); if (fseek(f, 0, SEEK_SET) < 0) nob_return_defer(false); From d6c1cd0660e7935a2948561e7f50a7996a744c5f Mon Sep 17 00:00:00 2001 From: yhr0x43 Date: Mon, 5 May 2025 17:20:16 -0500 Subject: [PATCH 07/27] Support Unicode error message --- nob.h | 7 +++++++ tests/win32_error.c | 1 + 2 files changed, 8 insertions(+) diff --git a/nob.h b/nob.h index 523ba8c..3da87c3 100644 --- a/nob.h +++ b/nob.h @@ -703,8 +703,15 @@ Nob_Log_Level nob_minimal_log_level = NOB_INFO; char *nob_win32_error_message(DWORD err) { static char win32ErrMsg[NOB_WIN32_ERR_MSG_SIZE] = {0}; +#ifdef UNICODE + WCHAR lpBuffer[NOB_WIN32_ERR_MSG_SIZE]; + DWORD errMsgSize = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, LANG_USER_DEFAULT, lpBuffer, + NOB_WIN32_ERR_MSG_SIZE, NULL); + errMsgSize = WideCharToMultiByte(CP_UTF8, 0, lpBuffer, errMsgSize, win32ErrMsg, NOB_WIN32_ERR_MSG_SIZE, NULL, NULL); +#else DWORD errMsgSize = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, LANG_USER_DEFAULT, win32ErrMsg, NOB_WIN32_ERR_MSG_SIZE, NULL); +#endif if (errMsgSize == 0) { char *newErrMsg = NULL; diff --git a/tests/win32_error.c b/tests/win32_error.c index cc86920..2f65218 100644 --- a/tests/win32_error.c +++ b/tests/win32_error.c @@ -1,3 +1,4 @@ +#define UNICODE #define NOB_IMPLEMENTATION #include "nob.h" From 8a5b2d9427794face64684c03481ca815483abe5 Mon Sep 17 00:00:00 2001 From: yhr0x43 Date: Tue, 6 May 2025 10:36:22 -0500 Subject: [PATCH 08/27] Crude workarund to use winapi with utf-8 string --- nob.h | 148 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 145 insertions(+), 3 deletions(-) diff --git a/nob.h b/nob.h index 3da87c3..1ba7cb2 100644 --- a/nob.h +++ b/nob.h @@ -796,7 +796,13 @@ static char nob_temp[NOB_TEMP_CAPACITY] = {0}; bool nob_mkdir_if_not_exists(const char *path) { #ifdef _WIN32 +#ifdef UNICODE + WCHAR wPath[MAX_PATH]; + MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH * sizeof(WCHAR)); + int result = CreateDirectoryW(wPath, NULL); +#else int result = CreateDirectoryA(path, NULL); +#endif if (result == 0) { DWORD err = GetLastError(); if (err == ERROR_ALREADY_EXISTS) { @@ -826,10 +832,20 @@ bool nob_copy_file(const char *src_path, const char *dst_path) { nob_log(NOB_INFO, "copying %s -> %s", src_path, dst_path); #ifdef _WIN32 +#ifdef UNICODE + WCHAR wsrc_path[MAX_PATH], wdst_path[MAX_PATH]; + MultiByteToWideChar(CP_UTF8, 0, src_path, -1, wsrc_path, MAX_PATH * sizeof(WCHAR)); + MultiByteToWideChar(CP_UTF8, 0, dst_path, -1, wdst_path, MAX_PATH * sizeof(WCHAR)); + if (!CopyFileW(wsrc_path, wdst_path, FALSE)) { + nob_log(NOB_ERROR, "Could not copy file: %s", nob_win32_error_message(GetLastError())); + return false; + } +#else if (!CopyFileA(src_path, dst_path, FALSE)) { nob_log(NOB_ERROR, "Could not copy file: %s", nob_win32_error_message(GetLastError())); return false; } +#endif return true; #else int src_fd = -1; @@ -916,10 +932,15 @@ Nob_Proc nob_cmd_run_async_redirect(Nob_Cmd cmd, Nob_Cmd_Redirect redirect) #ifdef _WIN32 // https://docs.microsoft.com/en-us/windows/win32/procthread/creating-a-child-process-with-redirected-input-and-output - +#ifdef UNICODE + STARTUPINFOW siStartInfo; + ZeroMemory(&siStartInfo, sizeof(siStartInfo)); + siStartInfo.cb = sizeof(STARTUPINFOW); +#else STARTUPINFOA siStartInfo; ZeroMemory(&siStartInfo, sizeof(siStartInfo)); siStartInfo.cb = sizeof(STARTUPINFOA); +#endif // NOTE: theoretically setting NULL to std handles should not be a problem // https://docs.microsoft.com/en-us/windows/console/getstdhandle?redirectedfrom=MSDN#attachdetach-behavior // TODO: check for errors in GetStdHandle @@ -935,7 +956,18 @@ Nob_Proc nob_cmd_run_async_redirect(Nob_Cmd cmd, Nob_Cmd_Redirect redirect) // cmd_render is for logging primarily nob_cmd_render(cmd, &sb); nob_sb_append_null(&sb); +#ifdef UNICODE + LPWSTR wCmdLine; + int cchCmdLine = MultiByteToWideChar(CP_UTF8, 0, sb.items, -1, NULL, 0); + // https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw + // per MSDN ref of `lpCommandLine`, "The maximum length of this string is 32,767 characters" + wCmdLine = NOB_REALLOC(NULL, cchCmdLine * sizeof(WCHAR)); + MultiByteToWideChar(CP_UTF8, 0, sb.items, -1, wCmdLine, cchCmdLine); + BOOL bSuccess = CreateProcessW(NULL, wCmdLine, NULL, NULL, TRUE, 0, NULL, NULL, &siStartInfo, &piProcInfo); + NOB_FREE(wCmdLine); +#else BOOL bSuccess = CreateProcessA(NULL, sb.items, NULL, NULL, TRUE, 0, NULL, NULL, &siStartInfo, &piProcInfo); +#endif nob_sb_free(sb); if (!bSuccess) { @@ -1032,7 +1064,18 @@ Nob_Fd nob_fd_open_for_read(const char *path) SECURITY_ATTRIBUTES saAttr = {0}; saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); saAttr.bInheritHandle = TRUE; - +#ifdef UNICODE + WCHAR wPath[MAX_PATH]; + MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH); + Nob_Fd result = CreateFileW( + wPath, + GENERIC_READ, + 0, + &saAttr, + OPEN_EXISTING, + FILE_ATTRIBUTE_READONLY, + NULL); +#else Nob_Fd result = CreateFileA( path, GENERIC_READ, @@ -1041,6 +1084,7 @@ Nob_Fd nob_fd_open_for_read(const char *path) OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); +#endif if (result == INVALID_HANDLE_VALUE) { nob_log(NOB_ERROR, "Could not open file %s: %s", path, nob_win32_error_message(GetLastError())); @@ -1067,6 +1111,19 @@ Nob_Fd nob_fd_open_for_write(const char *path) saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); saAttr.bInheritHandle = TRUE; +#ifdef UNICODE + WCHAR wPath[MAX_PATH]; + MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH); + Nob_Fd result = CreateFileW( + wPath, // name of the write + GENERIC_WRITE, // open for writing + 0, // do not share + &saAttr, // default security + CREATE_ALWAYS, // create always + FILE_ATTRIBUTE_NORMAL, // normal file + NULL // no attr. template + ); +#else# Nob_Fd result = CreateFileA( path, // name of the write GENERIC_WRITE, // open for writing @@ -1076,6 +1133,7 @@ Nob_Fd nob_fd_open_for_write(const char *path) FILE_ATTRIBUTE_NORMAL, // normal file NULL // no attr. template ); +#endif if (result == INVALID_HANDLE_VALUE) { nob_log(NOB_ERROR, "Could not open file %s: %s", path, nob_win32_error_message(GetLastError())); @@ -1316,7 +1374,13 @@ bool nob_write_entire_file(const char *path, const void *data, size_t size) Nob_File_Type nob_get_file_type(const char *path) { #ifdef _WIN32 +#ifdef UNICODE + WCHAR wPath[MAX_PATH]; + MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH); + DWORD attr = GetFileAttributesW(wPath); +#else DWORD attr = GetFileAttributesA(path); +#endif if (attr == INVALID_FILE_ATTRIBUTES) { nob_log(NOB_ERROR, "Could not get file attributes of %s: %s", path, nob_win32_error_message(GetLastError())); return -1; @@ -1343,10 +1407,19 @@ bool nob_delete_file(const char *path) { nob_log(NOB_INFO, "deleting %s", path); #ifdef _WIN32 +#ifdef UNICODE + WCHAR wPath[MAX_PATH]; + MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH); + if (!DeleteFileW(wPath)) { + nob_log(NOB_ERROR, "Could not delete file %s: %s", path, nob_win32_error_message(GetLastError())); + return false; + } +#else if (!DeleteFileA(path)) { nob_log(NOB_ERROR, "Could not delete file %s: %s", path, nob_win32_error_message(GetLastError())); return false; } +#endif return true; #else if (remove(path) < 0) { @@ -1485,8 +1558,13 @@ int nob_needs_rebuild(const char *output_path, const char **input_paths, size_t { #ifdef _WIN32 BOOL bSuccess; - +#ifdef UNICODE + WCHAR wPath[MAX_PATH]; + MultiByteToWideChar(CP_UTF8, 0, output_path, -1, wPath, MAX_PATH); + HANDLE output_path_fd = CreateFileW(wPath, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); +#else HANDLE output_path_fd = CreateFileA(output_path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); +#endif if (output_path_fd == INVALID_HANDLE_VALUE) { // NOTE: if output does not exist it 100% must be rebuilt if (GetLastError() == ERROR_FILE_NOT_FOUND) return 1; @@ -1503,7 +1581,12 @@ int nob_needs_rebuild(const char *output_path, const char **input_paths, size_t for (size_t i = 0; i < input_paths_count; ++i) { const char *input_path = input_paths[i]; +#ifdef UNICODE + MultiByteToWideChar(CP_UTF8, 0, input_path, -1, wPath, MAX_PATH); + HANDLE input_path_fd = CreateFileW(wPath, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); +#else HANDLE input_path_fd = CreateFileA(input_path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); +#endif if (input_path_fd == INVALID_HANDLE_VALUE) { // NOTE: non-existing input is an error cause it is needed for building in the first place nob_log(NOB_ERROR, "Could not open file %s: %s", input_path, nob_win32_error_message(GetLastError())); @@ -1571,10 +1654,21 @@ bool nob_rename(const char *old_path, const char *new_path) { nob_log(NOB_INFO, "renaming %s -> %s", old_path, new_path); #ifdef _WIN32 +#ifdef UNICODE + WCHAR wOldPath[MAX_PATH]; + WCHAR wNewPath[MAX_PATH]; + MultiByteToWideChar(CP_UTF8, 0, old_path, -1, wOldPath, MAX_PATH); + MultiByteToWideChar(CP_UTF8, 0, new_path, -1, wNewPath, MAX_PATH); + if (!MoveFileExW(wOldPath, wNewPath, MOVEFILE_REPLACE_EXISTING)) { + nob_log(NOB_ERROR, "could not rename %s to %s: %s", old_path, new_path, nob_win32_error_message(GetLastError())); + return false; + } +#else if (!MoveFileExA(old_path, new_path, MOVEFILE_REPLACE_EXISTING)) { nob_log(NOB_ERROR, "could not rename %s to %s: %s", old_path, new_path, nob_win32_error_message(GetLastError())); return false; } +#endif #else if (rename(old_path, new_path) < 0) { nob_log(NOB_ERROR, "could not rename %s to %s: %s", old_path, new_path, strerror(errno)); @@ -1751,7 +1845,13 @@ bool nob_sv_starts_with(Nob_String_View sv, Nob_String_View expected_prefix) int nob_file_exists(const char *file_path) { #if _WIN32 +#ifdef UNICODE + WCHAR wPath[MAX_PATH]; + MultiByteToWideChar(CP_UTF8, 0, file_path, -1, wPath, MAX_PATH); + DWORD dwAttrib = GetFileAttributesW(wPath); +#else DWORD dwAttrib = GetFileAttributesA(file_path); +#endif if(dwAttrib == INVALID_FILE_ATTRIBUTES){ DWORD err = GetLastError(); if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) return 0; @@ -1773,17 +1873,32 @@ int nob_file_exists(const char *file_path) const char *nob_get_current_dir_temp(void) { #ifdef _WIN32 +#ifdef UNICODE + DWORD nBufferLength = GetCurrentDirectoryW(0, NULL); +#else DWORD nBufferLength = GetCurrentDirectoryA(0, NULL); +#endif if (nBufferLength == 0) { nob_log(NOB_ERROR, "could not get current directory: %s", nob_win32_error_message(GetLastError())); return NULL; } +#ifdef UNICODE + WCHAR wCwd[MAX_PATH]; + if (GetCurrentDirectoryW(nBufferLength, wCwd) == 0) { + nob_log(NOB_ERROR, "could not get current directory: %s", nob_win32_error_message(GetLastError())); + return NULL; + } + nBufferLength = WideCharToMultiByte(CP_UTF8, 0, wCwd, -1, NULL, 0, NULL, NULL); + char *buffer = (char*) nob_temp_alloc(nBufferLength); + WideCharToMultiByte(CP_UTF8, 0, wCwd, -1, buffer, nBufferLength, NULL, NULL); +#else char *buffer = (char*) nob_temp_alloc(nBufferLength); if (GetCurrentDirectoryA(nBufferLength, buffer) == 0) { nob_log(NOB_ERROR, "could not get current directory: %s", nob_win32_error_message(GetLastError())); return NULL; } +#endif return buffer; #else @@ -1800,10 +1915,19 @@ const char *nob_get_current_dir_temp(void) bool nob_set_current_dir(const char *path) { #ifdef _WIN32 +#ifdef UNICODE + WCHAR wPath[MAX_PATH]; + MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH); + if (!SetCurrentDirectoryW(wPath)) { + nob_log(NOB_ERROR, "could not set current directory to %s: %s", path, nob_win32_error_message(GetLastError())); + return false; + } +#else if (!SetCurrentDirectoryA(path)) { nob_log(NOB_ERROR, "could not set current directory to %s: %s", path, nob_win32_error_message(GetLastError())); return false; } +#endif return true; #else if (chdir(path) < 0) { @@ -1819,7 +1943,11 @@ bool nob_set_current_dir(const char *path) struct DIR { HANDLE hFind; +#ifdef UNICODE + WIN32_FIND_DATAW data; +#else WIN32_FIND_DATAA data; +#endif struct dirent *dirent; }; @@ -1833,7 +1961,13 @@ DIR *opendir(const char *dirpath) DIR *dir = (DIR*)NOB_REALLOC(NULL, sizeof(DIR)); memset(dir, 0, sizeof(DIR)); +#ifdef UNICODE + WCHAR wBuffer[MAX_PATH]; + MultiByteToWideChar(CP_UTF8, 0, buffer, -1, wBuffer, MAX_PATH); + dir->hFind = FindFirstFileW(wBuffer, &dir->data); +#else dir->hFind = FindFirstFileA(buffer, &dir->data); +#endif if (dir->hFind == INVALID_HANDLE_VALUE) { // TODO: opendir should set errno accordingly on FindFirstFile fail // https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror @@ -1859,7 +1993,11 @@ struct dirent *readdir(DIR *dirp) dirp->dirent = (struct dirent*)NOB_REALLOC(NULL, sizeof(struct dirent)); memset(dirp->dirent, 0, sizeof(struct dirent)); } else { +#ifdef UNICODE + if(!FindNextFileW(dirp->hFind, &dirp->data)) { +#else if(!FindNextFileA(dirp->hFind, &dirp->data)) { +#endif if (GetLastError() != ERROR_NO_MORE_FILES) { // TODO: readdir should set errno accordingly on FindNextFile fail // https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror @@ -1872,10 +2010,14 @@ struct dirent *readdir(DIR *dirp) memset(dirp->dirent->d_name, 0, sizeof(dirp->dirent->d_name)); +#ifdef UNICODE + WideCharToMultiByte(CP_UTF8, 0, dirp->data.cFileName, -1, dirp->dirent->d_name, MAX_PATH - 1, NULL, NULL); +#else strncpy( dirp->dirent->d_name, dirp->data.cFileName, sizeof(dirp->dirent->d_name) - 1); +#endif return dirp->dirent; } From 8090d4685d195055ec176feacd5e4e4c468277ae Mon Sep 17 00:00:00 2001 From: yhr0x43 Date: Tue, 6 May 2025 14:07:58 -0500 Subject: [PATCH 09/27] Only use wide Win APIs After some debate with myself. I am convinced that wchar API functions should be used exclusively for the following reasons: - Good Windows programming practice. wchar should be used whenever possible - The rest io interactions uses C standard library, which on windows often can accepts and convert utf-8 string automatically. So I'll argue using ANSI functions is incorrect because library internals (like Nob_Cmd) is assumed to use utf-8 string like what a sane person would do (and also what Linux does). For correctness's sake, the extra memory allocation and conversion is worth it. --- nob.h | 123 ++-------------------------------------------------------- 1 file changed, 3 insertions(+), 120 deletions(-) diff --git a/nob.h b/nob.h index 1ba7cb2..9931eb7 100644 --- a/nob.h +++ b/nob.h @@ -703,15 +703,10 @@ Nob_Log_Level nob_minimal_log_level = NOB_INFO; char *nob_win32_error_message(DWORD err) { static char win32ErrMsg[NOB_WIN32_ERR_MSG_SIZE] = {0}; -#ifdef UNICODE WCHAR lpBuffer[NOB_WIN32_ERR_MSG_SIZE]; - DWORD errMsgSize = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, LANG_USER_DEFAULT, lpBuffer, + DWORD cchBuffer = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, LANG_USER_DEFAULT, lpBuffer, NOB_WIN32_ERR_MSG_SIZE, NULL); - errMsgSize = WideCharToMultiByte(CP_UTF8, 0, lpBuffer, errMsgSize, win32ErrMsg, NOB_WIN32_ERR_MSG_SIZE, NULL, NULL); -#else - DWORD errMsgSize = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, LANG_USER_DEFAULT, win32ErrMsg, - NOB_WIN32_ERR_MSG_SIZE, NULL); -#endif + int errMsgSize = WideCharToMultiByte(CP_UTF8, 0, lpBuffer, cchBuffer, win32ErrMsg, NOB_WIN32_ERR_MSG_SIZE, NULL, NULL); if (errMsgSize == 0) { char *newErrMsg = NULL; @@ -796,13 +791,9 @@ static char nob_temp[NOB_TEMP_CAPACITY] = {0}; bool nob_mkdir_if_not_exists(const char *path) { #ifdef _WIN32 -#ifdef UNICODE WCHAR wPath[MAX_PATH]; MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH * sizeof(WCHAR)); int result = CreateDirectoryW(wPath, NULL); -#else - int result = CreateDirectoryA(path, NULL); -#endif if (result == 0) { DWORD err = GetLastError(); if (err == ERROR_ALREADY_EXISTS) { @@ -832,7 +823,6 @@ bool nob_copy_file(const char *src_path, const char *dst_path) { nob_log(NOB_INFO, "copying %s -> %s", src_path, dst_path); #ifdef _WIN32 -#ifdef UNICODE WCHAR wsrc_path[MAX_PATH], wdst_path[MAX_PATH]; MultiByteToWideChar(CP_UTF8, 0, src_path, -1, wsrc_path, MAX_PATH * sizeof(WCHAR)); MultiByteToWideChar(CP_UTF8, 0, dst_path, -1, wdst_path, MAX_PATH * sizeof(WCHAR)); @@ -840,12 +830,6 @@ bool nob_copy_file(const char *src_path, const char *dst_path) nob_log(NOB_ERROR, "Could not copy file: %s", nob_win32_error_message(GetLastError())); return false; } -#else - if (!CopyFileA(src_path, dst_path, FALSE)) { - nob_log(NOB_ERROR, "Could not copy file: %s", nob_win32_error_message(GetLastError())); - return false; - } -#endif return true; #else int src_fd = -1; @@ -932,15 +916,9 @@ Nob_Proc nob_cmd_run_async_redirect(Nob_Cmd cmd, Nob_Cmd_Redirect redirect) #ifdef _WIN32 // https://docs.microsoft.com/en-us/windows/win32/procthread/creating-a-child-process-with-redirected-input-and-output -#ifdef UNICODE STARTUPINFOW siStartInfo; ZeroMemory(&siStartInfo, sizeof(siStartInfo)); siStartInfo.cb = sizeof(STARTUPINFOW); -#else - STARTUPINFOA siStartInfo; - ZeroMemory(&siStartInfo, sizeof(siStartInfo)); - siStartInfo.cb = sizeof(STARTUPINFOA); -#endif // NOTE: theoretically setting NULL to std handles should not be a problem // https://docs.microsoft.com/en-us/windows/console/getstdhandle?redirectedfrom=MSDN#attachdetach-behavior // TODO: check for errors in GetStdHandle @@ -956,18 +934,15 @@ Nob_Proc nob_cmd_run_async_redirect(Nob_Cmd cmd, Nob_Cmd_Redirect redirect) // cmd_render is for logging primarily nob_cmd_render(cmd, &sb); nob_sb_append_null(&sb); -#ifdef UNICODE LPWSTR wCmdLine; int cchCmdLine = MultiByteToWideChar(CP_UTF8, 0, sb.items, -1, NULL, 0); // https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw // per MSDN ref of `lpCommandLine`, "The maximum length of this string is 32,767 characters" wCmdLine = NOB_REALLOC(NULL, cchCmdLine * sizeof(WCHAR)); + NOB_ASSERT(wCmdLine != NULL && "Buy more RAM"); MultiByteToWideChar(CP_UTF8, 0, sb.items, -1, wCmdLine, cchCmdLine); BOOL bSuccess = CreateProcessW(NULL, wCmdLine, NULL, NULL, TRUE, 0, NULL, NULL, &siStartInfo, &piProcInfo); NOB_FREE(wCmdLine); -#else - BOOL bSuccess = CreateProcessA(NULL, sb.items, NULL, NULL, TRUE, 0, NULL, NULL, &siStartInfo, &piProcInfo); -#endif nob_sb_free(sb); if (!bSuccess) { @@ -1064,7 +1039,6 @@ Nob_Fd nob_fd_open_for_read(const char *path) SECURITY_ATTRIBUTES saAttr = {0}; saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); saAttr.bInheritHandle = TRUE; -#ifdef UNICODE WCHAR wPath[MAX_PATH]; MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH); Nob_Fd result = CreateFileW( @@ -1075,16 +1049,6 @@ Nob_Fd nob_fd_open_for_read(const char *path) OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); -#else - Nob_Fd result = CreateFileA( - path, - GENERIC_READ, - 0, - &saAttr, - OPEN_EXISTING, - FILE_ATTRIBUTE_READONLY, - NULL); -#endif if (result == INVALID_HANDLE_VALUE) { nob_log(NOB_ERROR, "Could not open file %s: %s", path, nob_win32_error_message(GetLastError())); @@ -1111,7 +1075,6 @@ Nob_Fd nob_fd_open_for_write(const char *path) saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); saAttr.bInheritHandle = TRUE; -#ifdef UNICODE WCHAR wPath[MAX_PATH]; MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH); Nob_Fd result = CreateFileW( @@ -1123,17 +1086,6 @@ Nob_Fd nob_fd_open_for_write(const char *path) FILE_ATTRIBUTE_NORMAL, // normal file NULL // no attr. template ); -#else# - Nob_Fd result = CreateFileA( - path, // name of the write - GENERIC_WRITE, // open for writing - 0, // do not share - &saAttr, // default security - CREATE_ALWAYS, // create always - FILE_ATTRIBUTE_NORMAL, // normal file - NULL // no attr. template - ); -#endif if (result == INVALID_HANDLE_VALUE) { nob_log(NOB_ERROR, "Could not open file %s: %s", path, nob_win32_error_message(GetLastError())); @@ -1374,13 +1326,9 @@ bool nob_write_entire_file(const char *path, const void *data, size_t size) Nob_File_Type nob_get_file_type(const char *path) { #ifdef _WIN32 -#ifdef UNICODE WCHAR wPath[MAX_PATH]; MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH); DWORD attr = GetFileAttributesW(wPath); -#else - DWORD attr = GetFileAttributesA(path); -#endif if (attr == INVALID_FILE_ATTRIBUTES) { nob_log(NOB_ERROR, "Could not get file attributes of %s: %s", path, nob_win32_error_message(GetLastError())); return -1; @@ -1407,19 +1355,12 @@ bool nob_delete_file(const char *path) { nob_log(NOB_INFO, "deleting %s", path); #ifdef _WIN32 -#ifdef UNICODE WCHAR wPath[MAX_PATH]; MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH); if (!DeleteFileW(wPath)) { nob_log(NOB_ERROR, "Could not delete file %s: %s", path, nob_win32_error_message(GetLastError())); return false; } -#else - if (!DeleteFileA(path)) { - nob_log(NOB_ERROR, "Could not delete file %s: %s", path, nob_win32_error_message(GetLastError())); - return false; - } -#endif return true; #else if (remove(path) < 0) { @@ -1558,13 +1499,9 @@ int nob_needs_rebuild(const char *output_path, const char **input_paths, size_t { #ifdef _WIN32 BOOL bSuccess; -#ifdef UNICODE WCHAR wPath[MAX_PATH]; MultiByteToWideChar(CP_UTF8, 0, output_path, -1, wPath, MAX_PATH); HANDLE output_path_fd = CreateFileW(wPath, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); -#else - HANDLE output_path_fd = CreateFileA(output_path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); -#endif if (output_path_fd == INVALID_HANDLE_VALUE) { // NOTE: if output does not exist it 100% must be rebuilt if (GetLastError() == ERROR_FILE_NOT_FOUND) return 1; @@ -1581,12 +1518,8 @@ int nob_needs_rebuild(const char *output_path, const char **input_paths, size_t for (size_t i = 0; i < input_paths_count; ++i) { const char *input_path = input_paths[i]; -#ifdef UNICODE MultiByteToWideChar(CP_UTF8, 0, input_path, -1, wPath, MAX_PATH); HANDLE input_path_fd = CreateFileW(wPath, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); -#else - HANDLE input_path_fd = CreateFileA(input_path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); -#endif if (input_path_fd == INVALID_HANDLE_VALUE) { // NOTE: non-existing input is an error cause it is needed for building in the first place nob_log(NOB_ERROR, "Could not open file %s: %s", input_path, nob_win32_error_message(GetLastError())); @@ -1654,7 +1587,6 @@ bool nob_rename(const char *old_path, const char *new_path) { nob_log(NOB_INFO, "renaming %s -> %s", old_path, new_path); #ifdef _WIN32 -#ifdef UNICODE WCHAR wOldPath[MAX_PATH]; WCHAR wNewPath[MAX_PATH]; MultiByteToWideChar(CP_UTF8, 0, old_path, -1, wOldPath, MAX_PATH); @@ -1663,12 +1595,6 @@ bool nob_rename(const char *old_path, const char *new_path) nob_log(NOB_ERROR, "could not rename %s to %s: %s", old_path, new_path, nob_win32_error_message(GetLastError())); return false; } -#else - if (!MoveFileExA(old_path, new_path, MOVEFILE_REPLACE_EXISTING)) { - nob_log(NOB_ERROR, "could not rename %s to %s: %s", old_path, new_path, nob_win32_error_message(GetLastError())); - return false; - } -#endif #else if (rename(old_path, new_path) < 0) { nob_log(NOB_ERROR, "could not rename %s to %s: %s", old_path, new_path, strerror(errno)); @@ -1845,13 +1771,9 @@ bool nob_sv_starts_with(Nob_String_View sv, Nob_String_View expected_prefix) int nob_file_exists(const char *file_path) { #if _WIN32 -#ifdef UNICODE WCHAR wPath[MAX_PATH]; MultiByteToWideChar(CP_UTF8, 0, file_path, -1, wPath, MAX_PATH); DWORD dwAttrib = GetFileAttributesW(wPath); -#else - DWORD dwAttrib = GetFileAttributesA(file_path); -#endif if(dwAttrib == INVALID_FILE_ATTRIBUTES){ DWORD err = GetLastError(); if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) return 0; @@ -1873,17 +1795,12 @@ int nob_file_exists(const char *file_path) const char *nob_get_current_dir_temp(void) { #ifdef _WIN32 -#ifdef UNICODE DWORD nBufferLength = GetCurrentDirectoryW(0, NULL); -#else - DWORD nBufferLength = GetCurrentDirectoryA(0, NULL); -#endif if (nBufferLength == 0) { nob_log(NOB_ERROR, "could not get current directory: %s", nob_win32_error_message(GetLastError())); return NULL; } -#ifdef UNICODE WCHAR wCwd[MAX_PATH]; if (GetCurrentDirectoryW(nBufferLength, wCwd) == 0) { nob_log(NOB_ERROR, "could not get current directory: %s", nob_win32_error_message(GetLastError())); @@ -1892,13 +1809,6 @@ const char *nob_get_current_dir_temp(void) nBufferLength = WideCharToMultiByte(CP_UTF8, 0, wCwd, -1, NULL, 0, NULL, NULL); char *buffer = (char*) nob_temp_alloc(nBufferLength); WideCharToMultiByte(CP_UTF8, 0, wCwd, -1, buffer, nBufferLength, NULL, NULL); -#else - char *buffer = (char*) nob_temp_alloc(nBufferLength); - if (GetCurrentDirectoryA(nBufferLength, buffer) == 0) { - nob_log(NOB_ERROR, "could not get current directory: %s", nob_win32_error_message(GetLastError())); - return NULL; - } -#endif return buffer; #else @@ -1915,19 +1825,12 @@ const char *nob_get_current_dir_temp(void) bool nob_set_current_dir(const char *path) { #ifdef _WIN32 -#ifdef UNICODE WCHAR wPath[MAX_PATH]; MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH); if (!SetCurrentDirectoryW(wPath)) { nob_log(NOB_ERROR, "could not set current directory to %s: %s", path, nob_win32_error_message(GetLastError())); return false; } -#else - if (!SetCurrentDirectoryA(path)) { - nob_log(NOB_ERROR, "could not set current directory to %s: %s", path, nob_win32_error_message(GetLastError())); - return false; - } -#endif return true; #else if (chdir(path) < 0) { @@ -1943,11 +1846,7 @@ bool nob_set_current_dir(const char *path) struct DIR { HANDLE hFind; -#ifdef UNICODE WIN32_FIND_DATAW data; -#else - WIN32_FIND_DATAA data; -#endif struct dirent *dirent; }; @@ -1961,13 +1860,9 @@ DIR *opendir(const char *dirpath) DIR *dir = (DIR*)NOB_REALLOC(NULL, sizeof(DIR)); memset(dir, 0, sizeof(DIR)); -#ifdef UNICODE WCHAR wBuffer[MAX_PATH]; MultiByteToWideChar(CP_UTF8, 0, buffer, -1, wBuffer, MAX_PATH); dir->hFind = FindFirstFileW(wBuffer, &dir->data); -#else - dir->hFind = FindFirstFileA(buffer, &dir->data); -#endif if (dir->hFind == INVALID_HANDLE_VALUE) { // TODO: opendir should set errno accordingly on FindFirstFile fail // https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror @@ -1993,11 +1888,7 @@ struct dirent *readdir(DIR *dirp) dirp->dirent = (struct dirent*)NOB_REALLOC(NULL, sizeof(struct dirent)); memset(dirp->dirent, 0, sizeof(struct dirent)); } else { -#ifdef UNICODE if(!FindNextFileW(dirp->hFind, &dirp->data)) { -#else - if(!FindNextFileA(dirp->hFind, &dirp->data)) { -#endif if (GetLastError() != ERROR_NO_MORE_FILES) { // TODO: readdir should set errno accordingly on FindNextFile fail // https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror @@ -2010,15 +1901,7 @@ struct dirent *readdir(DIR *dirp) memset(dirp->dirent->d_name, 0, sizeof(dirp->dirent->d_name)); -#ifdef UNICODE WideCharToMultiByte(CP_UTF8, 0, dirp->data.cFileName, -1, dirp->dirent->d_name, MAX_PATH - 1, NULL, NULL); -#else - strncpy( - dirp->dirent->d_name, - dirp->data.cFileName, - sizeof(dirp->dirent->d_name) - 1); -#endif - return dirp->dirent; } From cc745e5dff245d611ce758cc4a8315db08b87deb Mon Sep 17 00:00:00 2001 From: yhr0x43 Date: Tue, 6 May 2025 14:12:34 -0500 Subject: [PATCH 10/27] fix the use of MAX_PATH --- nob.h | 56 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/nob.h b/nob.h index 9931eb7..47c1b72 100644 --- a/nob.h +++ b/nob.h @@ -791,8 +791,8 @@ static char nob_temp[NOB_TEMP_CAPACITY] = {0}; bool nob_mkdir_if_not_exists(const char *path) { #ifdef _WIN32 - WCHAR wPath[MAX_PATH]; - MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH * sizeof(WCHAR)); + WCHAR wPath[MAX_PATH+1]; + MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH+1); int result = CreateDirectoryW(wPath, NULL); if (result == 0) { DWORD err = GetLastError(); @@ -823,9 +823,9 @@ bool nob_copy_file(const char *src_path, const char *dst_path) { nob_log(NOB_INFO, "copying %s -> %s", src_path, dst_path); #ifdef _WIN32 - WCHAR wsrc_path[MAX_PATH], wdst_path[MAX_PATH]; - MultiByteToWideChar(CP_UTF8, 0, src_path, -1, wsrc_path, MAX_PATH * sizeof(WCHAR)); - MultiByteToWideChar(CP_UTF8, 0, dst_path, -1, wdst_path, MAX_PATH * sizeof(WCHAR)); + WCHAR wsrc_path[MAX_PATH+1], wdst_path[MAX_PATH+1]; + MultiByteToWideChar(CP_UTF8, 0, src_path, -1, wsrc_path, MAX_PATH+1); + MultiByteToWideChar(CP_UTF8, 0, dst_path, -1, wdst_path, MAX_PATH+1); if (!CopyFileW(wsrc_path, wdst_path, FALSE)) { nob_log(NOB_ERROR, "Could not copy file: %s", nob_win32_error_message(GetLastError())); return false; @@ -1039,8 +1039,8 @@ Nob_Fd nob_fd_open_for_read(const char *path) SECURITY_ATTRIBUTES saAttr = {0}; saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); saAttr.bInheritHandle = TRUE; - WCHAR wPath[MAX_PATH]; - MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH); + WCHAR wPath[MAX_PATH+1]; + MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH+1); Nob_Fd result = CreateFileW( wPath, GENERIC_READ, @@ -1075,8 +1075,8 @@ Nob_Fd nob_fd_open_for_write(const char *path) saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); saAttr.bInheritHandle = TRUE; - WCHAR wPath[MAX_PATH]; - MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH); + WCHAR wPath[MAX_PATH+1]; + MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH+1); Nob_Fd result = CreateFileW( wPath, // name of the write GENERIC_WRITE, // open for writing @@ -1326,8 +1326,8 @@ bool nob_write_entire_file(const char *path, const void *data, size_t size) Nob_File_Type nob_get_file_type(const char *path) { #ifdef _WIN32 - WCHAR wPath[MAX_PATH]; - MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH); + WCHAR wPath[MAX_PATH+1]; + MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH+1); DWORD attr = GetFileAttributesW(wPath); if (attr == INVALID_FILE_ATTRIBUTES) { nob_log(NOB_ERROR, "Could not get file attributes of %s: %s", path, nob_win32_error_message(GetLastError())); @@ -1355,8 +1355,8 @@ bool nob_delete_file(const char *path) { nob_log(NOB_INFO, "deleting %s", path); #ifdef _WIN32 - WCHAR wPath[MAX_PATH]; - MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH); + WCHAR wPath[MAX_PATH+1]; + MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH+1); if (!DeleteFileW(wPath)) { nob_log(NOB_ERROR, "Could not delete file %s: %s", path, nob_win32_error_message(GetLastError())); return false; @@ -1499,8 +1499,8 @@ int nob_needs_rebuild(const char *output_path, const char **input_paths, size_t { #ifdef _WIN32 BOOL bSuccess; - WCHAR wPath[MAX_PATH]; - MultiByteToWideChar(CP_UTF8, 0, output_path, -1, wPath, MAX_PATH); + WCHAR wPath[MAX_PATH+1]; + MultiByteToWideChar(CP_UTF8, 0, output_path, -1, wPath, MAX_PATH+1); HANDLE output_path_fd = CreateFileW(wPath, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); if (output_path_fd == INVALID_HANDLE_VALUE) { // NOTE: if output does not exist it 100% must be rebuilt @@ -1518,7 +1518,7 @@ int nob_needs_rebuild(const char *output_path, const char **input_paths, size_t for (size_t i = 0; i < input_paths_count; ++i) { const char *input_path = input_paths[i]; - MultiByteToWideChar(CP_UTF8, 0, input_path, -1, wPath, MAX_PATH); + MultiByteToWideChar(CP_UTF8, 0, input_path, -1, wPath, MAX_PATH+1); HANDLE input_path_fd = CreateFileW(wPath, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); if (input_path_fd == INVALID_HANDLE_VALUE) { // NOTE: non-existing input is an error cause it is needed for building in the first place @@ -1587,10 +1587,10 @@ bool nob_rename(const char *old_path, const char *new_path) { nob_log(NOB_INFO, "renaming %s -> %s", old_path, new_path); #ifdef _WIN32 - WCHAR wOldPath[MAX_PATH]; - WCHAR wNewPath[MAX_PATH]; - MultiByteToWideChar(CP_UTF8, 0, old_path, -1, wOldPath, MAX_PATH); - MultiByteToWideChar(CP_UTF8, 0, new_path, -1, wNewPath, MAX_PATH); + WCHAR wOldPath[MAX_PATH+1]; + WCHAR wNewPath[MAX_PATH+1]; + MultiByteToWideChar(CP_UTF8, 0, old_path, -1, wOldPath, MAX_PATH+1); + MultiByteToWideChar(CP_UTF8, 0, new_path, -1, wNewPath, MAX_PATH+1); if (!MoveFileExW(wOldPath, wNewPath, MOVEFILE_REPLACE_EXISTING)) { nob_log(NOB_ERROR, "could not rename %s to %s: %s", old_path, new_path, nob_win32_error_message(GetLastError())); return false; @@ -1771,8 +1771,8 @@ bool nob_sv_starts_with(Nob_String_View sv, Nob_String_View expected_prefix) int nob_file_exists(const char *file_path) { #if _WIN32 - WCHAR wPath[MAX_PATH]; - MultiByteToWideChar(CP_UTF8, 0, file_path, -1, wPath, MAX_PATH); + WCHAR wPath[MAX_PATH+1]; + MultiByteToWideChar(CP_UTF8, 0, file_path, -1, wPath, MAX_PATH+1); DWORD dwAttrib = GetFileAttributesW(wPath); if(dwAttrib == INVALID_FILE_ATTRIBUTES){ DWORD err = GetLastError(); @@ -1801,7 +1801,7 @@ const char *nob_get_current_dir_temp(void) return NULL; } - WCHAR wCwd[MAX_PATH]; + WCHAR wCwd[MAX_PATH+1]; if (GetCurrentDirectoryW(nBufferLength, wCwd) == 0) { nob_log(NOB_ERROR, "could not get current directory: %s", nob_win32_error_message(GetLastError())); return NULL; @@ -1825,8 +1825,8 @@ const char *nob_get_current_dir_temp(void) bool nob_set_current_dir(const char *path) { #ifdef _WIN32 - WCHAR wPath[MAX_PATH]; - MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH); + WCHAR wPath[MAX_PATH+1]; + MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH+1); if (!SetCurrentDirectoryW(wPath)) { nob_log(NOB_ERROR, "could not set current directory to %s: %s", path, nob_win32_error_message(GetLastError())); return false; @@ -1860,8 +1860,8 @@ DIR *opendir(const char *dirpath) DIR *dir = (DIR*)NOB_REALLOC(NULL, sizeof(DIR)); memset(dir, 0, sizeof(DIR)); - WCHAR wBuffer[MAX_PATH]; - MultiByteToWideChar(CP_UTF8, 0, buffer, -1, wBuffer, MAX_PATH); + WCHAR wBuffer[MAX_PATH+1]; + MultiByteToWideChar(CP_UTF8, 0, buffer, -1, wBuffer, MAX_PATH+1); dir->hFind = FindFirstFileW(wBuffer, &dir->data); if (dir->hFind == INVALID_HANDLE_VALUE) { // TODO: opendir should set errno accordingly on FindFirstFile fail @@ -1901,7 +1901,7 @@ struct dirent *readdir(DIR *dirp) memset(dirp->dirent->d_name, 0, sizeof(dirp->dirent->d_name)); - WideCharToMultiByte(CP_UTF8, 0, dirp->data.cFileName, -1, dirp->dirent->d_name, MAX_PATH - 1, NULL, NULL); + WideCharToMultiByte(CP_UTF8, 0, dirp->data.cFileName, -1, dirp->dirent->d_name, MAX_PATH+1, NULL, NULL); return dirp->dirent; } From 3e7d0296805a84fcc75672f824e40b55d193b446 Mon Sep 17 00:00:00 2001 From: yhr0x43 Date: Tue, 6 May 2025 15:12:46 -0500 Subject: [PATCH 11/27] Fix errorcode overwrite in nob_win32_error_message --- nob.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/nob.h b/nob.h index 47c1b72..76cceca 100644 --- a/nob.h +++ b/nob.h @@ -706,9 +706,8 @@ char *nob_win32_error_message(DWORD err) { WCHAR lpBuffer[NOB_WIN32_ERR_MSG_SIZE]; DWORD cchBuffer = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, LANG_USER_DEFAULT, lpBuffer, NOB_WIN32_ERR_MSG_SIZE, NULL); - int errMsgSize = WideCharToMultiByte(CP_UTF8, 0, lpBuffer, cchBuffer, win32ErrMsg, NOB_WIN32_ERR_MSG_SIZE, NULL, NULL); - if (errMsgSize == 0) { + if (cchBuffer == 0) { char *newErrMsg = NULL; if (GetLastError() == ERROR_MR_MID_NOT_FOUND) { newErrMsg = "Invalid Win32 error code"; @@ -723,6 +722,10 @@ char *nob_win32_error_message(DWORD err) { } } + int errMsgSize = WideCharToMultiByte(CP_UTF8, 0, lpBuffer, cchBuffer, win32ErrMsg, NOB_WIN32_ERR_MSG_SIZE, NULL, NULL); + if (errMsgSize == 0) { + return "Insufficient error message buffer size"; + } while (errMsgSize > 1 && isspace(win32ErrMsg[errMsgSize - 1])) { win32ErrMsg[--errMsgSize] = '\0'; } From a6bfc7fd1a3acdf4ed04dcab9f7a3f72055696b2 Mon Sep 17 00:00:00 2001 From: yhr0x43 Date: Tue, 6 May 2025 15:46:28 -0500 Subject: [PATCH 12/27] error check wchar conversion --- nob.h | 77 ++++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 60 insertions(+), 17 deletions(-) diff --git a/nob.h b/nob.h index 76cceca..7d0f37c 100644 --- a/nob.h +++ b/nob.h @@ -795,7 +795,10 @@ bool nob_mkdir_if_not_exists(const char *path) { #ifdef _WIN32 WCHAR wPath[MAX_PATH+1]; - MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH+1); + if (MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH+1) == 0) { + nob_log(NOB_ERROR, "Path `%s` too long", path); + return false; + } int result = CreateDirectoryW(wPath, NULL); if (result == 0) { DWORD err = GetLastError(); @@ -826,10 +829,16 @@ bool nob_copy_file(const char *src_path, const char *dst_path) { nob_log(NOB_INFO, "copying %s -> %s", src_path, dst_path); #ifdef _WIN32 - WCHAR wsrc_path[MAX_PATH+1], wdst_path[MAX_PATH+1]; - MultiByteToWideChar(CP_UTF8, 0, src_path, -1, wsrc_path, MAX_PATH+1); - MultiByteToWideChar(CP_UTF8, 0, dst_path, -1, wdst_path, MAX_PATH+1); - if (!CopyFileW(wsrc_path, wdst_path, FALSE)) { + WCHAR wSrcPath[MAX_PATH+1], wDstPath[MAX_PATH+1]; + if (MultiByteToWideChar(CP_UTF8, 0, src_path, -1, wSrcPath, MAX_PATH+1) == 0) { + nob_log(NOB_ERROR, "Path `%s` too long", src_path); + return false; + } + if (MultiByteToWideChar(CP_UTF8, 0, dst_path, -1, wDstPath, MAX_PATH+1) == 0) { + nob_log(NOB_ERROR, "Path `%s` too long", dst_path); + return false; + } + if (!CopyFileW(wSrcPath, wDstPath, FALSE)) { nob_log(NOB_ERROR, "Could not copy file: %s", nob_win32_error_message(GetLastError())); return false; } @@ -943,7 +952,8 @@ Nob_Proc nob_cmd_run_async_redirect(Nob_Cmd cmd, Nob_Cmd_Redirect redirect) // per MSDN ref of `lpCommandLine`, "The maximum length of this string is 32,767 characters" wCmdLine = NOB_REALLOC(NULL, cchCmdLine * sizeof(WCHAR)); NOB_ASSERT(wCmdLine != NULL && "Buy more RAM"); - MultiByteToWideChar(CP_UTF8, 0, sb.items, -1, wCmdLine, cchCmdLine); + // It's impossible to now have a large enough buffer here and user will know if they have invalid character + (void)MultiByteToWideChar(CP_UTF8, 0, sb.items, -1, wCmdLine, cchCmdLine); BOOL bSuccess = CreateProcessW(NULL, wCmdLine, NULL, NULL, TRUE, 0, NULL, NULL, &siStartInfo, &piProcInfo); NOB_FREE(wCmdLine); nob_sb_free(sb); @@ -1043,7 +1053,10 @@ Nob_Fd nob_fd_open_for_read(const char *path) saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); saAttr.bInheritHandle = TRUE; WCHAR wPath[MAX_PATH+1]; - MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH+1); + if (MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH+1) == 0) { + nob_log(NOB_ERROR, "Path `%s` too long", path); + return NOB_INVALID_FD; + } Nob_Fd result = CreateFileW( wPath, GENERIC_READ, @@ -1079,7 +1092,10 @@ Nob_Fd nob_fd_open_for_write(const char *path) saAttr.bInheritHandle = TRUE; WCHAR wPath[MAX_PATH+1]; - MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH+1); + if (MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH+1) == 0) { + nob_log(NOB_ERROR, "Path `%s` too long", path); + return NOB_INVALID_FD; + } Nob_Fd result = CreateFileW( wPath, // name of the write GENERIC_WRITE, // open for writing @@ -1330,7 +1346,10 @@ Nob_File_Type nob_get_file_type(const char *path) { #ifdef _WIN32 WCHAR wPath[MAX_PATH+1]; - MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH+1); + if (MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH+1) == 0) { + nob_log(NOB_ERROR, "Path `%s` too long", path); + return -1; + } DWORD attr = GetFileAttributesW(wPath); if (attr == INVALID_FILE_ATTRIBUTES) { nob_log(NOB_ERROR, "Could not get file attributes of %s: %s", path, nob_win32_error_message(GetLastError())); @@ -1359,7 +1378,10 @@ bool nob_delete_file(const char *path) nob_log(NOB_INFO, "deleting %s", path); #ifdef _WIN32 WCHAR wPath[MAX_PATH+1]; - MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH+1); + if (MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH+1) == 0) { + nob_log(NOB_ERROR, "Path `%s` too long", path); + return false; + } if (!DeleteFileW(wPath)) { nob_log(NOB_ERROR, "Could not delete file %s: %s", path, nob_win32_error_message(GetLastError())); return false; @@ -1503,7 +1525,10 @@ int nob_needs_rebuild(const char *output_path, const char **input_paths, size_t #ifdef _WIN32 BOOL bSuccess; WCHAR wPath[MAX_PATH+1]; - MultiByteToWideChar(CP_UTF8, 0, output_path, -1, wPath, MAX_PATH+1); + if (MultiByteToWideChar(CP_UTF8, 0, output_path, -1, wPath, MAX_PATH+1) == 0) { + nob_log(NOB_ERROR, "Path `%s` too long", path); + return -1; + } HANDLE output_path_fd = CreateFileW(wPath, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); if (output_path_fd == INVALID_HANDLE_VALUE) { // NOTE: if output does not exist it 100% must be rebuilt @@ -1521,7 +1546,10 @@ int nob_needs_rebuild(const char *output_path, const char **input_paths, size_t for (size_t i = 0; i < input_paths_count; ++i) { const char *input_path = input_paths[i]; - MultiByteToWideChar(CP_UTF8, 0, input_path, -1, wPath, MAX_PATH+1); + if (MultiByteToWideChar(CP_UTF8, 0, input_path, -1, wPath, MAX_PATH+1) == 0) { + nob_log(NOB_ERROR, "Path `%s` too long", path); + return -1; + } HANDLE input_path_fd = CreateFileW(wPath, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); if (input_path_fd == INVALID_HANDLE_VALUE) { // NOTE: non-existing input is an error cause it is needed for building in the first place @@ -1592,8 +1620,14 @@ bool nob_rename(const char *old_path, const char *new_path) #ifdef _WIN32 WCHAR wOldPath[MAX_PATH+1]; WCHAR wNewPath[MAX_PATH+1]; - MultiByteToWideChar(CP_UTF8, 0, old_path, -1, wOldPath, MAX_PATH+1); - MultiByteToWideChar(CP_UTF8, 0, new_path, -1, wNewPath, MAX_PATH+1); + if (MultiByteToWideChar(CP_UTF8, 0, old_path, -1, wOldPath, MAX_PATH+1) == 0) { + nob_log(NOB_ERROR, "Path `%s` too long", old_path); + return false; + } + if (MultiByteToWideChar(CP_UTF8, 0, new_path, -1, wNewPath, MAX_PATH+1) == 0) { + nob_log(NOB_ERROR, "Path `%s` too long", new_path); + return false; + } if (!MoveFileExW(wOldPath, wNewPath, MOVEFILE_REPLACE_EXISTING)) { nob_log(NOB_ERROR, "could not rename %s to %s: %s", old_path, new_path, nob_win32_error_message(GetLastError())); return false; @@ -1775,7 +1809,10 @@ int nob_file_exists(const char *file_path) { #if _WIN32 WCHAR wPath[MAX_PATH+1]; - MultiByteToWideChar(CP_UTF8, 0, file_path, -1, wPath, MAX_PATH+1); + if (MultiByteToWideChar(CP_UTF8, 0, file_path, -1, wPath, MAX_PATH+1) == 0) { + nob_log(NOB_ERROR, "Path `%s` too long", file_path); + return -1; + } DWORD dwAttrib = GetFileAttributesW(wPath); if(dwAttrib == INVALID_FILE_ATTRIBUTES){ DWORD err = GetLastError(); @@ -1829,7 +1866,10 @@ bool nob_set_current_dir(const char *path) { #ifdef _WIN32 WCHAR wPath[MAX_PATH+1]; - MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH+1); + if (MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH) == 0) { + nob_log(NOB_ERROR, "Path `%s` too long", path); + return false; + } if (!SetCurrentDirectoryW(wPath)) { nob_log(NOB_ERROR, "could not set current directory to %s: %s", path, nob_win32_error_message(GetLastError())); return false; @@ -1864,7 +1904,10 @@ DIR *opendir(const char *dirpath) memset(dir, 0, sizeof(DIR)); WCHAR wBuffer[MAX_PATH+1]; - MultiByteToWideChar(CP_UTF8, 0, buffer, -1, wBuffer, MAX_PATH+1); + if (MultiByteToWideChar(CP_UTF8, 0, buffer, -1, wBuffer, MAX_PATH+1) == 0) { + errno = ENOSYS; + goto fail; + } dir->hFind = FindFirstFileW(wBuffer, &dir->data); if (dir->hFind == INVALID_HANDLE_VALUE) { // TODO: opendir should set errno accordingly on FindFirstFile fail From 755b1bd5ff1973375fb86b4f9d7a6d54028ca6c4 Mon Sep 17 00:00:00 2001 From: yhr0x43 Date: Tue, 6 May 2025 15:48:07 -0500 Subject: [PATCH 13/27] no need to specify UNICODE anymore --- tests/win32_error.c | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/win32_error.c b/tests/win32_error.c index 2f65218..cc86920 100644 --- a/tests/win32_error.c +++ b/tests/win32_error.c @@ -1,4 +1,3 @@ -#define UNICODE #define NOB_IMPLEMENTATION #include "nob.h" From efa9501cd61e7a1ece801a79ca5024cbd777bdc3 Mon Sep 17 00:00:00 2001 From: yhr0x43 Date: Tue, 6 May 2025 16:45:03 -0500 Subject: [PATCH 14/27] Variable name error --- nob.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nob.h b/nob.h index 7d0f37c..7d9b003 100644 --- a/nob.h +++ b/nob.h @@ -1526,7 +1526,7 @@ int nob_needs_rebuild(const char *output_path, const char **input_paths, size_t BOOL bSuccess; WCHAR wPath[MAX_PATH+1]; if (MultiByteToWideChar(CP_UTF8, 0, output_path, -1, wPath, MAX_PATH+1) == 0) { - nob_log(NOB_ERROR, "Path `%s` too long", path); + nob_log(NOB_ERROR, "Path `%s` too long", output_path); return -1; } HANDLE output_path_fd = CreateFileW(wPath, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); @@ -1547,7 +1547,7 @@ int nob_needs_rebuild(const char *output_path, const char **input_paths, size_t for (size_t i = 0; i < input_paths_count; ++i) { const char *input_path = input_paths[i]; if (MultiByteToWideChar(CP_UTF8, 0, input_path, -1, wPath, MAX_PATH+1) == 0) { - nob_log(NOB_ERROR, "Path `%s` too long", path); + nob_log(NOB_ERROR, "Path `%s` too long", input_path); return -1; } HANDLE input_path_fd = CreateFileW(wPath, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); From 1f20112c92d73ce814c95c78b0eab4efce995ab9 Mon Sep 17 00:00:00 2001 From: yhr0x43 Date: Thu, 15 May 2025 20:25:55 -0500 Subject: [PATCH 15/27] FormatMessage use LANGID 0 for message fallback --- nob.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nob.h b/nob.h index 7d9b003..63ac3f1 100644 --- a/nob.h +++ b/nob.h @@ -704,8 +704,8 @@ Nob_Log_Level nob_minimal_log_level = NOB_INFO; char *nob_win32_error_message(DWORD err) { static char win32ErrMsg[NOB_WIN32_ERR_MSG_SIZE] = {0}; WCHAR lpBuffer[NOB_WIN32_ERR_MSG_SIZE]; - DWORD cchBuffer = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, LANG_USER_DEFAULT, lpBuffer, - NOB_WIN32_ERR_MSG_SIZE, NULL); + DWORD cchBuffer = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, 0, lpBuffer, + NOB_WIN32_ERR_MSG_SIZE, NULL); if (cchBuffer == 0) { char *newErrMsg = NULL; From f55ecf346e209bcfb42c6a121ba34319901d66a5 Mon Sep 17 00:00:00 2001 From: KxD-Work Date: Tue, 20 May 2025 15:01:20 -0400 Subject: [PATCH 16/27] Add support for extended path to opendir function --- nob.h | 207 +++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 153 insertions(+), 54 deletions(-) diff --git a/nob.h b/nob.h index 7b520a7..404d3f4 100644 --- a/nob.h +++ b/nob.h @@ -481,6 +481,13 @@ void nob_temp_reset(void); size_t nob_temp_save(void); void nob_temp_rewind(size_t checkpoint); +#ifdef _WIN32 +const wchar_t *nob_temp_cstr_to_wstr(const char *cstr); +// const wchar_t *nob_win32_temp_wide_path(const char *path); +const wchar_t *nob_win32_temp_wide_path_suff(const char *path, const char *suffix); +#define nob_win32_temp_wide_path(path) nob_win32_temp_wide_path_suff(path, NULL) +#endif // _WIN32 + // Given any path returns the last part of that path. // "/path/to/a/file.c" -> "file.c"; "/path/to/a/directory" -> "directory" const char *nob_path_name(const char *path); @@ -664,12 +671,15 @@ Nob_String_View nob_sv_from_parts(const char *data, size_t count); #include #else // _WIN32 -#define WIN32_LEAN_AND_MEAN -#include +#include +#include +#include +#include +#include struct dirent { - char d_name[MAX_PATH+1]; + char d_name[MAX_PATH]; }; typedef struct DIR DIR; @@ -799,9 +809,9 @@ static char nob_temp[NOB_TEMP_CAPACITY] = {0}; bool nob_mkdir_if_not_exists(const char *path) { #ifdef _WIN32 - WCHAR wPath[MAX_PATH+1]; - if (MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH+1) == 0) { - nob_log(NOB_ERROR, "Path `%s` too long", path); + WCHAR wPath[MAX_PATH]; + if (MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH) == 0) { + nob_log(NOB_ERROR, "Path \"%s\" is invalid: %s", path, nob_win32_error_message(GetLastError())); return false; } int result = CreateDirectoryW(wPath, NULL); @@ -834,13 +844,13 @@ bool nob_copy_file(const char *src_path, const char *dst_path) { nob_log(NOB_INFO, "copying %s -> %s", src_path, dst_path); #ifdef _WIN32 - WCHAR wSrcPath[MAX_PATH+1], wDstPath[MAX_PATH+1]; - if (MultiByteToWideChar(CP_UTF8, 0, src_path, -1, wSrcPath, MAX_PATH+1) == 0) { - nob_log(NOB_ERROR, "Path `%s` too long", src_path); + WCHAR wSrcPath[MAX_PATH], wDstPath[MAX_PATH]; + if (MultiByteToWideChar(CP_UTF8, 0, src_path, -1, wSrcPath, MAX_PATH) == 0) { + nob_log(NOB_ERROR, "Path \"%s\" is invalid: %s", src_path, nob_win32_error_message(GetLastError())); return false; } - if (MultiByteToWideChar(CP_UTF8, 0, dst_path, -1, wDstPath, MAX_PATH+1) == 0) { - nob_log(NOB_ERROR, "Path `%s` too long", dst_path); + if (MultiByteToWideChar(CP_UTF8, 0, dst_path, -1, wDstPath, MAX_PATH) == 0) { + nob_log(NOB_ERROR, "Path \"%s\" is invalid: %s", dst_path, nob_win32_error_message(GetLastError())); return false; } if (!CopyFileW(wSrcPath, wDstPath, FALSE)) { @@ -1098,9 +1108,9 @@ Nob_Fd nob_fd_open_for_read(const char *path) SECURITY_ATTRIBUTES saAttr = {0}; saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); saAttr.bInheritHandle = TRUE; - WCHAR wPath[MAX_PATH+1]; - if (MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH+1) == 0) { - nob_log(NOB_ERROR, "Path `%s` too long", path); + WCHAR wPath[MAX_PATH]; + if (MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH) == 0) { + nob_log(NOB_ERROR, "Path \"%s\" is invalid: %s", path, nob_win32_error_message(GetLastError())); return NOB_INVALID_FD; } Nob_Fd result = CreateFileW( @@ -1137,9 +1147,9 @@ Nob_Fd nob_fd_open_for_write(const char *path) saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); saAttr.bInheritHandle = TRUE; - WCHAR wPath[MAX_PATH+1]; - if (MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH+1) == 0) { - nob_log(NOB_ERROR, "Path `%s` too long", path); + WCHAR wPath[MAX_PATH]; + if (MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH) == 0) { + nob_log(NOB_ERROR, "Path \"%s\" is invalid: %s", path, nob_win32_error_message(GetLastError())); return NOB_INVALID_FD; } Nob_Fd result = CreateFileW( @@ -1391,9 +1401,9 @@ bool nob_write_entire_file(const char *path, const void *data, size_t size) Nob_File_Type nob_get_file_type(const char *path) { #ifdef _WIN32 - WCHAR wPath[MAX_PATH+1]; - if (MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH+1) == 0) { - nob_log(NOB_ERROR, "Path `%s` too long", path); + WCHAR wPath[MAX_PATH]; + if (MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH) == 0) { + nob_log(NOB_ERROR, "Path \"%s\" is invalid: %s", path, nob_win32_error_message(GetLastError())); return -1; } DWORD attr = GetFileAttributesW(wPath); @@ -1423,9 +1433,9 @@ bool nob_delete_file(const char *path) { nob_log(NOB_INFO, "deleting %s", path); #ifdef _WIN32 - WCHAR wPath[MAX_PATH+1]; - if (MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH+1) == 0) { - nob_log(NOB_ERROR, "Path `%s` too long", path); + WCHAR wPath[MAX_PATH]; + if (MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH) == 0) { + nob_log(NOB_ERROR, "Path \"%s\" is invalid: %s", path, nob_win32_error_message(GetLastError())); return false; } if (!DeleteFileW(wPath)) { @@ -1566,13 +1576,53 @@ const char *nob_temp_sv_to_cstr(Nob_String_View sv) return result; } +#ifdef _WIN32 +const wchar_t *nob_temp_cstr_to_wstr(const char *cstr) { + if (cstr == NULL) return NULL; + if (!cstr[0]) return L""; + + size_t len = strlen(cstr) + 1; + wchar_t *result = nob_temp_alloc(len * sizeof(wchar_t)); + NOB_ASSERT(result != NULL && "Extend the size of the temporary allocator"); + + int charCount = MultiByteToWideChar(CP_UTF8, 0, cstr, -1, result, len); + if (!charCount) { + nob_log(NOB_ERROR, "Could not convert String \"%s\" to Wide String: %s", cstr, nob_win32_error_message(GetLastError())); + return NULL; + } + return result; +} + +const wchar_t *nob_win32_temp_wide_path_suff(const char *path, const char *suffix) { + const char extLenPrefix[] = "\\\\?\\"; + + bool needExtLenPref = false; + if (strlen(path) + ((suffix) ? (strlen(suffix)) : (0)) >= MAX_PATH) // Ignore if path is smaller than MAX_PATH + needExtLenPref = memcmp(extLenPrefix, path, sizeof(extLenPrefix) - 1) != 0; + + char *buffer = nob_temp_sprintf("%s%s%s", needExtLenPref ? extLenPrefix : "", path, suffix ? suffix : ""); + + int charCount = (int)strlen(buffer) + 1; + if (charCount > 0x7FFF) { + nob_log(NOB_ERROR, "Path exceeds maximum lenght of 32767 characters"); + return NULL; + } + // nob_log(NOB_INFO, "charCount: %d", charCount); + + return nob_temp_cstr_to_wstr(buffer); +} + +#define nob_win32_temp_wide_path(path) nob_win32_temp_wide_path_suff(path, NULL) +// const wchar_t *nob_win32_temp_wide_path(const char *path) { return nob_win32_temp_wide_path_suff(path, NULL); } +#endif // _WIN32 + int nob_needs_rebuild(const char *output_path, const char **input_paths, size_t input_paths_count) { #ifdef _WIN32 BOOL bSuccess; - WCHAR wPath[MAX_PATH+1]; - if (MultiByteToWideChar(CP_UTF8, 0, output_path, -1, wPath, MAX_PATH+1) == 0) { - nob_log(NOB_ERROR, "Path `%s` too long", output_path); + WCHAR wPath[MAX_PATH]; + if (MultiByteToWideChar(CP_UTF8, 0, output_path, -1, wPath, MAX_PATH) == 0) { + nob_log(NOB_ERROR, "Path \"%s\" is invalid: %s", output_path, nob_win32_error_message(GetLastError())); return -1; } HANDLE output_path_fd = CreateFileW(wPath, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); @@ -1592,8 +1642,8 @@ int nob_needs_rebuild(const char *output_path, const char **input_paths, size_t for (size_t i = 0; i < input_paths_count; ++i) { const char *input_path = input_paths[i]; - if (MultiByteToWideChar(CP_UTF8, 0, input_path, -1, wPath, MAX_PATH+1) == 0) { - nob_log(NOB_ERROR, "Path `%s` too long", input_path); + if (MultiByteToWideChar(CP_UTF8, 0, input_path, -1, wPath, MAX_PATH) == 0) { + nob_log(NOB_ERROR, "Path \"%s\" is invalid: %s", input_path, nob_win32_error_message(GetLastError())); return -1; } HANDLE input_path_fd = CreateFileW(wPath, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); @@ -1664,14 +1714,14 @@ bool nob_rename(const char *old_path, const char *new_path) { nob_log(NOB_INFO, "renaming %s -> %s", old_path, new_path); #ifdef _WIN32 - WCHAR wOldPath[MAX_PATH+1]; - WCHAR wNewPath[MAX_PATH+1]; - if (MultiByteToWideChar(CP_UTF8, 0, old_path, -1, wOldPath, MAX_PATH+1) == 0) { - nob_log(NOB_ERROR, "Path `%s` too long", old_path); + WCHAR wOldPath[MAX_PATH]; + WCHAR wNewPath[MAX_PATH]; + if (MultiByteToWideChar(CP_UTF8, 0, old_path, -1, wOldPath, MAX_PATH) == 0) { + nob_log(NOB_ERROR, "Path \"%s\" is invalid: %s", old_path, nob_win32_error_message(GetLastError())); return false; } - if (MultiByteToWideChar(CP_UTF8, 0, new_path, -1, wNewPath, MAX_PATH+1) == 0) { - nob_log(NOB_ERROR, "Path `%s` too long", new_path); + if (MultiByteToWideChar(CP_UTF8, 0, new_path, -1, wNewPath, MAX_PATH) == 0) { + nob_log(NOB_ERROR, "Path \"%s\" is invalid: %s", new_path, nob_win32_error_message(GetLastError())); return false; } if (!MoveFileExW(wOldPath, wNewPath, MOVEFILE_REPLACE_EXISTING)) { @@ -1854,9 +1904,9 @@ bool nob_sv_starts_with(Nob_String_View sv, Nob_String_View expected_prefix) int nob_file_exists(const char *file_path) { #if _WIN32 - WCHAR wPath[MAX_PATH+1]; - if (MultiByteToWideChar(CP_UTF8, 0, file_path, -1, wPath, MAX_PATH+1) == 0) { - nob_log(NOB_ERROR, "Path `%s` too long", file_path); + WCHAR wPath[MAX_PATH]; + if (MultiByteToWideChar(CP_UTF8, 0, file_path, -1, wPath, MAX_PATH) == 0) { + nob_log(NOB_ERROR, "Path \"%s\" is invalid: %s", file_path, nob_win32_error_message(GetLastError())); return -1; } DWORD dwAttrib = GetFileAttributesW(wPath); @@ -1887,7 +1937,7 @@ const char *nob_get_current_dir_temp(void) return NULL; } - WCHAR wCwd[MAX_PATH+1]; + WCHAR wCwd[MAX_PATH]; if (GetCurrentDirectoryW(nBufferLength, wCwd) == 0) { nob_log(NOB_ERROR, "could not get current directory: %s", nob_win32_error_message(GetLastError())); return NULL; @@ -1911,9 +1961,9 @@ const char *nob_get_current_dir_temp(void) bool nob_set_current_dir(const char *path) { #ifdef _WIN32 - WCHAR wPath[MAX_PATH+1]; + WCHAR wPath[MAX_PATH]; if (MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH) == 0) { - nob_log(NOB_ERROR, "Path `%s` too long", path); + nob_log(NOB_ERROR, "Path \"%s\" is invalid: %s", path, nob_win32_error_message(GetLastError())); return false; } if (!SetCurrentDirectoryW(wPath)) { @@ -1941,30 +1991,55 @@ struct DIR DIR *opendir(const char *dirpath) { - NOB_ASSERT(dirpath); + NOB_ASSERT(dirpath != NULL); + if (!dirpath[0]) { // Set errno & return NULL if string is empty + errno = ENOENT; + return NULL; + } - char buffer[MAX_PATH]; - snprintf(buffer, MAX_PATH, "%s\\*", dirpath); + const size_t cp = nob_temp_save() ; - DIR *dir = (DIR*)NOB_REALLOC(NULL, sizeof(DIR)); + const wchar_t *wBuffer = nob_win32_temp_wide_path_suff(dirpath, "\\*"); + if (!wBuffer) { + switch (GetLastError()) { + case ERROR_INSUFFICIENT_BUFFER: + errno = ENAMETOOLONG; + break; + // case ERROR_INVALID_FLAGS: + // case ERROR_INVALID_PARAMETER: + // errno = EINVAL; // will it ever happens? + // break; + case ERROR_NO_UNICODE_TRANSLATION: + errno = EILSEQ; + break; + default: + errno = ENOSYS; + break; + } + nob_temp_rewind(cp); + return NULL; + } + + DIR *dir = (DIR *)NOB_REALLOC(NULL, sizeof(DIR)); memset(dir, 0, sizeof(DIR)); - WCHAR wBuffer[MAX_PATH+1]; - if (MultiByteToWideChar(CP_UTF8, 0, buffer, -1, wBuffer, MAX_PATH+1) == 0) { - errno = ENOSYS; - goto fail; - } dir->hFind = FindFirstFileW(wBuffer, &dir->data); if (dir->hFind == INVALID_HANDLE_VALUE) { // TODO: opendir should set errno accordingly on FindFirstFile fail // https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror - errno = ENOSYS; + DWORD err = GetLastError(); + if(err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) + errno = ENOENT; + else + errno = ENOSYS; goto fail; } + nob_temp_rewind(cp); return dir; fail: + nob_temp_rewind(cp); if (dir) { NOB_FREE(dir); } @@ -1974,10 +2049,10 @@ DIR *opendir(const char *dirpath) struct dirent *readdir(DIR *dirp) { - NOB_ASSERT(dirp); + NOB_ASSERT(dirp != NULL); if (dirp->dirent == NULL) { - dirp->dirent = (struct dirent*)NOB_REALLOC(NULL, sizeof(struct dirent)); + dirp->dirent = (struct dirent *)NOB_REALLOC(NULL, sizeof(struct dirent)); memset(dirp->dirent, 0, sizeof(struct dirent)); } else { if(!FindNextFileW(dirp->hFind, &dirp->data)) { @@ -1993,13 +2068,37 @@ struct dirent *readdir(DIR *dirp) memset(dirp->dirent->d_name, 0, sizeof(dirp->dirent->d_name)); - WideCharToMultiByte(CP_UTF8, 0, dirp->data.cFileName, -1, dirp->dirent->d_name, MAX_PATH+1, NULL, NULL); + // Set errno or just crash if conversion failed??? +#if 1 + assert(WideCharToMultiByte(CP_UTF8, 0, dirp->data.cFileName, -1, dirp->dirent->d_name, sizeof(dirp->dirent->d_name), NULL, NULL) != 0); +#else + SetLastError(0); + if (WideCharToMultiByte(CP_UTF8, 0, dirp->data.cFileName, -1, dirp->dirent->d_name, sizeof(dirp->dirent->d_name), NULL, NULL) == 0) { + switch (GetLastError()) { + case ERROR_INSUFFICIENT_BUFFER: + errno = ENAMETOOLONG; + break; + // case ERROR_INVALID_FLAGS: + // case ERROR_INVALID_PARAMETER: + // errno = EINVAL; // will it ever happens? + // break; + case ERROR_NO_UNICODE_TRANSLATION: + errno = EILSEQ; + break; + default: + errno = ENOSYS; + break; + } + return NULL; + } +#endif + return dirp->dirent; } int closedir(DIR *dirp) { - NOB_ASSERT(dirp); + NOB_ASSERT(dirp != NULL); if(!FindClose(dirp->hFind)) { // TODO: closedir should set errno accordingly on FindClose fail From c8abc87588e963964859e9a8a31c3d2aa7e5da1c Mon Sep 17 00:00:00 2001 From: KillerxDBr <97485370+KillerxDBr@users.noreply.github.com> Date: Thu, 22 May 2025 18:15:25 -0400 Subject: [PATCH 17/27] Revert "Add support for extended path to opendir function" This reverts commit f55ecf346e209bcfb42c6a121ba34319901d66a5. --- nob.h | 207 +++++++++++++++------------------------------------------- 1 file changed, 54 insertions(+), 153 deletions(-) diff --git a/nob.h b/nob.h index 404d3f4..7b520a7 100644 --- a/nob.h +++ b/nob.h @@ -481,13 +481,6 @@ void nob_temp_reset(void); size_t nob_temp_save(void); void nob_temp_rewind(size_t checkpoint); -#ifdef _WIN32 -const wchar_t *nob_temp_cstr_to_wstr(const char *cstr); -// const wchar_t *nob_win32_temp_wide_path(const char *path); -const wchar_t *nob_win32_temp_wide_path_suff(const char *path, const char *suffix); -#define nob_win32_temp_wide_path(path) nob_win32_temp_wide_path_suff(path, NULL) -#endif // _WIN32 - // Given any path returns the last part of that path. // "/path/to/a/file.c" -> "file.c"; "/path/to/a/directory" -> "directory" const char *nob_path_name(const char *path); @@ -671,15 +664,12 @@ Nob_String_View nob_sv_from_parts(const char *data, size_t count); #include #else // _WIN32 -#include -#include -#include -#include -#include +#define WIN32_LEAN_AND_MEAN +#include struct dirent { - char d_name[MAX_PATH]; + char d_name[MAX_PATH+1]; }; typedef struct DIR DIR; @@ -809,9 +799,9 @@ static char nob_temp[NOB_TEMP_CAPACITY] = {0}; bool nob_mkdir_if_not_exists(const char *path) { #ifdef _WIN32 - WCHAR wPath[MAX_PATH]; - if (MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH) == 0) { - nob_log(NOB_ERROR, "Path \"%s\" is invalid: %s", path, nob_win32_error_message(GetLastError())); + WCHAR wPath[MAX_PATH+1]; + if (MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH+1) == 0) { + nob_log(NOB_ERROR, "Path `%s` too long", path); return false; } int result = CreateDirectoryW(wPath, NULL); @@ -844,13 +834,13 @@ bool nob_copy_file(const char *src_path, const char *dst_path) { nob_log(NOB_INFO, "copying %s -> %s", src_path, dst_path); #ifdef _WIN32 - WCHAR wSrcPath[MAX_PATH], wDstPath[MAX_PATH]; - if (MultiByteToWideChar(CP_UTF8, 0, src_path, -1, wSrcPath, MAX_PATH) == 0) { - nob_log(NOB_ERROR, "Path \"%s\" is invalid: %s", src_path, nob_win32_error_message(GetLastError())); + WCHAR wSrcPath[MAX_PATH+1], wDstPath[MAX_PATH+1]; + if (MultiByteToWideChar(CP_UTF8, 0, src_path, -1, wSrcPath, MAX_PATH+1) == 0) { + nob_log(NOB_ERROR, "Path `%s` too long", src_path); return false; } - if (MultiByteToWideChar(CP_UTF8, 0, dst_path, -1, wDstPath, MAX_PATH) == 0) { - nob_log(NOB_ERROR, "Path \"%s\" is invalid: %s", dst_path, nob_win32_error_message(GetLastError())); + if (MultiByteToWideChar(CP_UTF8, 0, dst_path, -1, wDstPath, MAX_PATH+1) == 0) { + nob_log(NOB_ERROR, "Path `%s` too long", dst_path); return false; } if (!CopyFileW(wSrcPath, wDstPath, FALSE)) { @@ -1108,9 +1098,9 @@ Nob_Fd nob_fd_open_for_read(const char *path) SECURITY_ATTRIBUTES saAttr = {0}; saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); saAttr.bInheritHandle = TRUE; - WCHAR wPath[MAX_PATH]; - if (MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH) == 0) { - nob_log(NOB_ERROR, "Path \"%s\" is invalid: %s", path, nob_win32_error_message(GetLastError())); + WCHAR wPath[MAX_PATH+1]; + if (MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH+1) == 0) { + nob_log(NOB_ERROR, "Path `%s` too long", path); return NOB_INVALID_FD; } Nob_Fd result = CreateFileW( @@ -1147,9 +1137,9 @@ Nob_Fd nob_fd_open_for_write(const char *path) saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); saAttr.bInheritHandle = TRUE; - WCHAR wPath[MAX_PATH]; - if (MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH) == 0) { - nob_log(NOB_ERROR, "Path \"%s\" is invalid: %s", path, nob_win32_error_message(GetLastError())); + WCHAR wPath[MAX_PATH+1]; + if (MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH+1) == 0) { + nob_log(NOB_ERROR, "Path `%s` too long", path); return NOB_INVALID_FD; } Nob_Fd result = CreateFileW( @@ -1401,9 +1391,9 @@ bool nob_write_entire_file(const char *path, const void *data, size_t size) Nob_File_Type nob_get_file_type(const char *path) { #ifdef _WIN32 - WCHAR wPath[MAX_PATH]; - if (MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH) == 0) { - nob_log(NOB_ERROR, "Path \"%s\" is invalid: %s", path, nob_win32_error_message(GetLastError())); + WCHAR wPath[MAX_PATH+1]; + if (MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH+1) == 0) { + nob_log(NOB_ERROR, "Path `%s` too long", path); return -1; } DWORD attr = GetFileAttributesW(wPath); @@ -1433,9 +1423,9 @@ bool nob_delete_file(const char *path) { nob_log(NOB_INFO, "deleting %s", path); #ifdef _WIN32 - WCHAR wPath[MAX_PATH]; - if (MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH) == 0) { - nob_log(NOB_ERROR, "Path \"%s\" is invalid: %s", path, nob_win32_error_message(GetLastError())); + WCHAR wPath[MAX_PATH+1]; + if (MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH+1) == 0) { + nob_log(NOB_ERROR, "Path `%s` too long", path); return false; } if (!DeleteFileW(wPath)) { @@ -1576,53 +1566,13 @@ const char *nob_temp_sv_to_cstr(Nob_String_View sv) return result; } -#ifdef _WIN32 -const wchar_t *nob_temp_cstr_to_wstr(const char *cstr) { - if (cstr == NULL) return NULL; - if (!cstr[0]) return L""; - - size_t len = strlen(cstr) + 1; - wchar_t *result = nob_temp_alloc(len * sizeof(wchar_t)); - NOB_ASSERT(result != NULL && "Extend the size of the temporary allocator"); - - int charCount = MultiByteToWideChar(CP_UTF8, 0, cstr, -1, result, len); - if (!charCount) { - nob_log(NOB_ERROR, "Could not convert String \"%s\" to Wide String: %s", cstr, nob_win32_error_message(GetLastError())); - return NULL; - } - return result; -} - -const wchar_t *nob_win32_temp_wide_path_suff(const char *path, const char *suffix) { - const char extLenPrefix[] = "\\\\?\\"; - - bool needExtLenPref = false; - if (strlen(path) + ((suffix) ? (strlen(suffix)) : (0)) >= MAX_PATH) // Ignore if path is smaller than MAX_PATH - needExtLenPref = memcmp(extLenPrefix, path, sizeof(extLenPrefix) - 1) != 0; - - char *buffer = nob_temp_sprintf("%s%s%s", needExtLenPref ? extLenPrefix : "", path, suffix ? suffix : ""); - - int charCount = (int)strlen(buffer) + 1; - if (charCount > 0x7FFF) { - nob_log(NOB_ERROR, "Path exceeds maximum lenght of 32767 characters"); - return NULL; - } - // nob_log(NOB_INFO, "charCount: %d", charCount); - - return nob_temp_cstr_to_wstr(buffer); -} - -#define nob_win32_temp_wide_path(path) nob_win32_temp_wide_path_suff(path, NULL) -// const wchar_t *nob_win32_temp_wide_path(const char *path) { return nob_win32_temp_wide_path_suff(path, NULL); } -#endif // _WIN32 - int nob_needs_rebuild(const char *output_path, const char **input_paths, size_t input_paths_count) { #ifdef _WIN32 BOOL bSuccess; - WCHAR wPath[MAX_PATH]; - if (MultiByteToWideChar(CP_UTF8, 0, output_path, -1, wPath, MAX_PATH) == 0) { - nob_log(NOB_ERROR, "Path \"%s\" is invalid: %s", output_path, nob_win32_error_message(GetLastError())); + WCHAR wPath[MAX_PATH+1]; + if (MultiByteToWideChar(CP_UTF8, 0, output_path, -1, wPath, MAX_PATH+1) == 0) { + nob_log(NOB_ERROR, "Path `%s` too long", output_path); return -1; } HANDLE output_path_fd = CreateFileW(wPath, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); @@ -1642,8 +1592,8 @@ int nob_needs_rebuild(const char *output_path, const char **input_paths, size_t for (size_t i = 0; i < input_paths_count; ++i) { const char *input_path = input_paths[i]; - if (MultiByteToWideChar(CP_UTF8, 0, input_path, -1, wPath, MAX_PATH) == 0) { - nob_log(NOB_ERROR, "Path \"%s\" is invalid: %s", input_path, nob_win32_error_message(GetLastError())); + if (MultiByteToWideChar(CP_UTF8, 0, input_path, -1, wPath, MAX_PATH+1) == 0) { + nob_log(NOB_ERROR, "Path `%s` too long", input_path); return -1; } HANDLE input_path_fd = CreateFileW(wPath, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); @@ -1714,14 +1664,14 @@ bool nob_rename(const char *old_path, const char *new_path) { nob_log(NOB_INFO, "renaming %s -> %s", old_path, new_path); #ifdef _WIN32 - WCHAR wOldPath[MAX_PATH]; - WCHAR wNewPath[MAX_PATH]; - if (MultiByteToWideChar(CP_UTF8, 0, old_path, -1, wOldPath, MAX_PATH) == 0) { - nob_log(NOB_ERROR, "Path \"%s\" is invalid: %s", old_path, nob_win32_error_message(GetLastError())); + WCHAR wOldPath[MAX_PATH+1]; + WCHAR wNewPath[MAX_PATH+1]; + if (MultiByteToWideChar(CP_UTF8, 0, old_path, -1, wOldPath, MAX_PATH+1) == 0) { + nob_log(NOB_ERROR, "Path `%s` too long", old_path); return false; } - if (MultiByteToWideChar(CP_UTF8, 0, new_path, -1, wNewPath, MAX_PATH) == 0) { - nob_log(NOB_ERROR, "Path \"%s\" is invalid: %s", new_path, nob_win32_error_message(GetLastError())); + if (MultiByteToWideChar(CP_UTF8, 0, new_path, -1, wNewPath, MAX_PATH+1) == 0) { + nob_log(NOB_ERROR, "Path `%s` too long", new_path); return false; } if (!MoveFileExW(wOldPath, wNewPath, MOVEFILE_REPLACE_EXISTING)) { @@ -1904,9 +1854,9 @@ bool nob_sv_starts_with(Nob_String_View sv, Nob_String_View expected_prefix) int nob_file_exists(const char *file_path) { #if _WIN32 - WCHAR wPath[MAX_PATH]; - if (MultiByteToWideChar(CP_UTF8, 0, file_path, -1, wPath, MAX_PATH) == 0) { - nob_log(NOB_ERROR, "Path \"%s\" is invalid: %s", file_path, nob_win32_error_message(GetLastError())); + WCHAR wPath[MAX_PATH+1]; + if (MultiByteToWideChar(CP_UTF8, 0, file_path, -1, wPath, MAX_PATH+1) == 0) { + nob_log(NOB_ERROR, "Path `%s` too long", file_path); return -1; } DWORD dwAttrib = GetFileAttributesW(wPath); @@ -1937,7 +1887,7 @@ const char *nob_get_current_dir_temp(void) return NULL; } - WCHAR wCwd[MAX_PATH]; + WCHAR wCwd[MAX_PATH+1]; if (GetCurrentDirectoryW(nBufferLength, wCwd) == 0) { nob_log(NOB_ERROR, "could not get current directory: %s", nob_win32_error_message(GetLastError())); return NULL; @@ -1961,9 +1911,9 @@ const char *nob_get_current_dir_temp(void) bool nob_set_current_dir(const char *path) { #ifdef _WIN32 - WCHAR wPath[MAX_PATH]; + WCHAR wPath[MAX_PATH+1]; if (MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH) == 0) { - nob_log(NOB_ERROR, "Path \"%s\" is invalid: %s", path, nob_win32_error_message(GetLastError())); + nob_log(NOB_ERROR, "Path `%s` too long", path); return false; } if (!SetCurrentDirectoryW(wPath)) { @@ -1991,55 +1941,30 @@ struct DIR DIR *opendir(const char *dirpath) { - NOB_ASSERT(dirpath != NULL); - if (!dirpath[0]) { // Set errno & return NULL if string is empty - errno = ENOENT; - return NULL; - } + NOB_ASSERT(dirpath); - const size_t cp = nob_temp_save() ; + char buffer[MAX_PATH]; + snprintf(buffer, MAX_PATH, "%s\\*", dirpath); - const wchar_t *wBuffer = nob_win32_temp_wide_path_suff(dirpath, "\\*"); - if (!wBuffer) { - switch (GetLastError()) { - case ERROR_INSUFFICIENT_BUFFER: - errno = ENAMETOOLONG; - break; - // case ERROR_INVALID_FLAGS: - // case ERROR_INVALID_PARAMETER: - // errno = EINVAL; // will it ever happens? - // break; - case ERROR_NO_UNICODE_TRANSLATION: - errno = EILSEQ; - break; - default: - errno = ENOSYS; - break; - } - nob_temp_rewind(cp); - return NULL; - } - - DIR *dir = (DIR *)NOB_REALLOC(NULL, sizeof(DIR)); + DIR *dir = (DIR*)NOB_REALLOC(NULL, sizeof(DIR)); memset(dir, 0, sizeof(DIR)); + WCHAR wBuffer[MAX_PATH+1]; + if (MultiByteToWideChar(CP_UTF8, 0, buffer, -1, wBuffer, MAX_PATH+1) == 0) { + errno = ENOSYS; + goto fail; + } dir->hFind = FindFirstFileW(wBuffer, &dir->data); if (dir->hFind == INVALID_HANDLE_VALUE) { // TODO: opendir should set errno accordingly on FindFirstFile fail // https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror - DWORD err = GetLastError(); - if(err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) - errno = ENOENT; - else - errno = ENOSYS; + errno = ENOSYS; goto fail; } - nob_temp_rewind(cp); return dir; fail: - nob_temp_rewind(cp); if (dir) { NOB_FREE(dir); } @@ -2049,10 +1974,10 @@ DIR *opendir(const char *dirpath) struct dirent *readdir(DIR *dirp) { - NOB_ASSERT(dirp != NULL); + NOB_ASSERT(dirp); if (dirp->dirent == NULL) { - dirp->dirent = (struct dirent *)NOB_REALLOC(NULL, sizeof(struct dirent)); + dirp->dirent = (struct dirent*)NOB_REALLOC(NULL, sizeof(struct dirent)); memset(dirp->dirent, 0, sizeof(struct dirent)); } else { if(!FindNextFileW(dirp->hFind, &dirp->data)) { @@ -2068,37 +1993,13 @@ struct dirent *readdir(DIR *dirp) memset(dirp->dirent->d_name, 0, sizeof(dirp->dirent->d_name)); - // Set errno or just crash if conversion failed??? -#if 1 - assert(WideCharToMultiByte(CP_UTF8, 0, dirp->data.cFileName, -1, dirp->dirent->d_name, sizeof(dirp->dirent->d_name), NULL, NULL) != 0); -#else - SetLastError(0); - if (WideCharToMultiByte(CP_UTF8, 0, dirp->data.cFileName, -1, dirp->dirent->d_name, sizeof(dirp->dirent->d_name), NULL, NULL) == 0) { - switch (GetLastError()) { - case ERROR_INSUFFICIENT_BUFFER: - errno = ENAMETOOLONG; - break; - // case ERROR_INVALID_FLAGS: - // case ERROR_INVALID_PARAMETER: - // errno = EINVAL; // will it ever happens? - // break; - case ERROR_NO_UNICODE_TRANSLATION: - errno = EILSEQ; - break; - default: - errno = ENOSYS; - break; - } - return NULL; - } -#endif - + WideCharToMultiByte(CP_UTF8, 0, dirp->data.cFileName, -1, dirp->dirent->d_name, MAX_PATH+1, NULL, NULL); return dirp->dirent; } int closedir(DIR *dirp) { - NOB_ASSERT(dirp != NULL); + NOB_ASSERT(dirp); if(!FindClose(dirp->hFind)) { // TODO: closedir should set errno accordingly on FindClose fail From 08527db9f3b304f8b374a9dae16b2f4376ba44aa Mon Sep 17 00:00:00 2001 From: KillerxDBr <97485370+KillerxDBr@users.noreply.github.com> Date: Thu, 22 May 2025 20:21:49 -0400 Subject: [PATCH 18/27] fix for warning in win32_error.c --- tests/win32_error.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/win32_error.c b/tests/win32_error.c index cc86920..3b0c78b 100644 --- a/tests/win32_error.c +++ b/tests/win32_error.c @@ -4,6 +4,6 @@ int main(void) { nob_log(NOB_ERROR, "First 100 Win32 API Errors:"); for (DWORD i = 0; i < 100; i++) - nob_log(NOB_ERROR, "%lu (0x%X): \"%s\"", i, i, nob_win32_error_message(i)); + nob_log(NOB_ERROR, "%lu (0x%lX): \"%s\"", i, i, nob_win32_error_message(i)); return 0; } From d38a6b156f8439de7bd10bf152a50e0e82988da7 Mon Sep 17 00:00:00 2001 From: KillerxDBr <97485370+KillerxDBr@users.noreply.github.com> Date: Thu, 22 May 2025 20:22:51 -0400 Subject: [PATCH 19/27] Add MSVC Flags --- shared.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared.h b/shared.h index 32f8229..f66f23e 100644 --- a/shared.h +++ b/shared.h @@ -9,7 +9,7 @@ #define TESTS_FOLDER "tests/" #if defined(_MSC_VER) -# define nob_cc_flags(cmd) cmd_append(cmd, "-I.") +# define nob_cc_flags(cmd) cmd_append(cmd, "/I.", "/D_CRT_SECURE_NO_WARNINGS", "/W4", "/nologo") #elif defined(__APPLE__) || defined(__MACH__) // TODO: "-std=c99", "-D_POSIX_SOURCE" didn't work for MacOS, don't know why, don't really care that much at the moment. // Anybody who does feel free to investigate. From 2d05f7acf33e490a278477a0581ff6075a59a3c3 Mon Sep 17 00:00:00 2001 From: KillerxDBr <97485370+KillerxDBr@users.noreply.github.com> Date: Thu, 22 May 2025 20:34:11 -0400 Subject: [PATCH 20/27] Fixing Wide String conversion --- nob.h | 255 +++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 182 insertions(+), 73 deletions(-) diff --git a/nob.h b/nob.h index 7b520a7..d1df4c3 100644 --- a/nob.h +++ b/nob.h @@ -669,7 +669,7 @@ Nob_String_View nob_sv_from_parts(const char *data, size_t count); struct dirent { - char d_name[MAX_PATH+1]; + char d_name[MAX_PATH]; }; typedef struct DIR DIR; @@ -707,7 +707,8 @@ Nob_Log_Level nob_minimal_log_level = NOB_INFO; #endif // NOB_WIN32_ERR_MSG_SIZE char *nob_win32_error_message(DWORD err) { - static char win32ErrMsg[NOB_WIN32_ERR_MSG_SIZE] = {0}; + int errMsgSize; + static char win32ErrMsg[NOB_WIN32_ERR_MSG_SIZE]; WCHAR lpBuffer[NOB_WIN32_ERR_MSG_SIZE]; DWORD cchBuffer = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, 0, lpBuffer, NOB_WIN32_ERR_MSG_SIZE, NULL); @@ -720,15 +721,16 @@ char *nob_win32_error_message(DWORD err) { newErrMsg = "Could not get error message"; } - if (snprintf(win32ErrMsg, sizeof(win32ErrMsg), "%s for 0x%lX", newErrMsg, err) > 0) { - return (char *)&win32ErrMsg; - } else { + // snprintf return DONT count the NULL terminator + errMsgSize = snprintf(win32ErrMsg, NOB_WIN32_ERR_MSG_SIZE, "%s for 0x%lX", newErrMsg, err); + if(errMsgSize < 1 || errMsgSize >= NOB_WIN32_ERR_MSG_SIZE) return newErrMsg; - } + + return (char *)&win32ErrMsg; } - int errMsgSize = WideCharToMultiByte(CP_UTF8, 0, lpBuffer, cchBuffer, win32ErrMsg, NOB_WIN32_ERR_MSG_SIZE, NULL, NULL); - if (errMsgSize == 0) { + errMsgSize = WideCharToMultiByte(CP_UTF8, 0, lpBuffer, -1, win32ErrMsg, NOB_WIN32_ERR_MSG_SIZE, NULL, NULL) -1; + if (errMsgSize < 1 || errMsgSize > NOB_WIN32_ERR_MSG_SIZE) { return "Insufficient error message buffer size"; } while (errMsgSize > 1 && isspace(win32ErrMsg[errMsgSize - 1])) { @@ -799,11 +801,14 @@ static char nob_temp[NOB_TEMP_CAPACITY] = {0}; bool nob_mkdir_if_not_exists(const char *path) { #ifdef _WIN32 - WCHAR wPath[MAX_PATH+1]; - if (MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH+1) == 0) { - nob_log(NOB_ERROR, "Path `%s` too long", path); + WCHAR wPath[MAX_PATH]; + + int charCount = MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH); + if (charCount < 1 || charCount > MAX_PATH) { + nob_log(NOB_ERROR, "Path `%s` is invalid: %s", path, nob_win32_error_message(GetLastError())); return false; } + int result = CreateDirectoryW(wPath, NULL); if (result == 0) { DWORD err = GetLastError(); @@ -834,15 +839,22 @@ bool nob_copy_file(const char *src_path, const char *dst_path) { nob_log(NOB_INFO, "copying %s -> %s", src_path, dst_path); #ifdef _WIN32 - WCHAR wSrcPath[MAX_PATH+1], wDstPath[MAX_PATH+1]; - if (MultiByteToWideChar(CP_UTF8, 0, src_path, -1, wSrcPath, MAX_PATH+1) == 0) { - nob_log(NOB_ERROR, "Path `%s` too long", src_path); + int charCount; + WCHAR wSrcPath[MAX_PATH]; + WCHAR wDstPath[MAX_PATH]; + + charCount = MultiByteToWideChar(CP_UTF8, 0, src_path, -1, wSrcPath, MAX_PATH); + if (charCount < 1 || charCount > MAX_PATH) { + nob_log(NOB_ERROR, "Path `%s` is invalid: %s", src_path, nob_win32_error_message(GetLastError())); return false; } - if (MultiByteToWideChar(CP_UTF8, 0, dst_path, -1, wDstPath, MAX_PATH+1) == 0) { - nob_log(NOB_ERROR, "Path `%s` too long", dst_path); + + charCount = MultiByteToWideChar(CP_UTF8, 0, dst_path, -1, wDstPath, MAX_PATH); + if (charCount < 1 || charCount > MAX_PATH) { + nob_log(NOB_ERROR, "Path `%s` is invalid: %s", dst_path, nob_win32_error_message(GetLastError())); return false; } + if (!CopyFileW(wSrcPath, wDstPath, FALSE)) { nob_log(NOB_ERROR, "Could not copy file: %s", nob_win32_error_message(GetLastError())); return false; @@ -971,10 +983,10 @@ Nob_Proc nob_cmd_run_async_redirect(Nob_Cmd cmd, Nob_Cmd_Redirect redirect) nob_cmd_render(cmd, &sb); nob_sb_append_null(&sb); nob_log(NOB_INFO, "CMD: %s", sb.items); - nob_sb_free(sb); - memset(&sb, 0, sizeof(sb)); #ifdef _WIN32 + const size_t cp = nob_temp_save(); + // https://docs.microsoft.com/en-us/windows/win32/procthread/creating-a-child-process-with-redirected-input-and-output STARTUPINFOW siStartInfo; ZeroMemory(&siStartInfo, sizeof(siStartInfo)); @@ -990,18 +1002,28 @@ Nob_Proc nob_cmd_run_async_redirect(Nob_Cmd cmd, Nob_Cmd_Redirect redirect) PROCESS_INFORMATION piProcInfo; ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION)); + sb.count = 0; nob__win32_cmd_quote(cmd, &sb); nob_sb_append_null(&sb); - LPWSTR wCmdLine; + + WCHAR *wCmdLine; int cchCmdLine = MultiByteToWideChar(CP_UTF8, 0, sb.items, -1, NULL, 0); // https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw // per MSDN ref of `lpCommandLine`, "The maximum length of this string is 32,767 characters" - wCmdLine = NOB_REALLOC(NULL, cchCmdLine * sizeof(WCHAR)); - NOB_ASSERT(wCmdLine != NULL && "Buy more RAM"); + wCmdLine = nob_temp_alloc(cchCmdLine * sizeof(WCHAR)); + NOB_ASSERT(wCmdLine != NULL && "Increase NOB_TEMP_CAPACITY"); + // It's impossible to now have a large enough buffer here and user will know if they have invalid character - (void)MultiByteToWideChar(CP_UTF8, 0, sb.items, -1, wCmdLine, cchCmdLine); + cchCmdLine = MultiByteToWideChar(CP_UTF8, 0, sb.items, -1, wCmdLine, cchCmdLine); + if (cchCmdLine < 1 || cchCmdLine > 0x7FFF) { + nob_log(NOB_ERROR, "Could not convert Command Line to Wide String: %s", nob_win32_error_message(GetLastError())); + nob_temp_rewind(cp); + return NOB_INVALID_PROC; + } + BOOL bSuccess = CreateProcessW(NULL, wCmdLine, NULL, NULL, TRUE, 0, NULL, NULL, &siStartInfo, &piProcInfo); - NOB_FREE(wCmdLine); + // NOB_FREE(wCmdLine); + nob_temp_rewind(cp); nob_sb_free(sb); if (!bSuccess) { @@ -1013,6 +1035,9 @@ Nob_Proc nob_cmd_run_async_redirect(Nob_Cmd cmd, Nob_Cmd_Redirect redirect) return piProcInfo.hProcess; #else + nob_sb_free(sb); + memset(&sb, 0, sizeof(sb)); + pid_t cpid = fork(); if (cpid < 0) { nob_log(NOB_ERROR, "Could not fork child process: %s", strerror(errno)); @@ -1098,9 +1123,11 @@ Nob_Fd nob_fd_open_for_read(const char *path) SECURITY_ATTRIBUTES saAttr = {0}; saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); saAttr.bInheritHandle = TRUE; - WCHAR wPath[MAX_PATH+1]; - if (MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH+1) == 0) { - nob_log(NOB_ERROR, "Path `%s` too long", path); + WCHAR wPath[MAX_PATH]; + + int charCount = MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH); + if (charCount < 1 || charCount > MAX_PATH) { + nob_log(NOB_ERROR, "Path `%s` is invalid: %s", path, nob_win32_error_message(GetLastError())); return NOB_INVALID_FD; } Nob_Fd result = CreateFileW( @@ -1136,10 +1163,11 @@ Nob_Fd nob_fd_open_for_write(const char *path) SECURITY_ATTRIBUTES saAttr = {0}; saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); saAttr.bInheritHandle = TRUE; + WCHAR wPath[MAX_PATH]; - WCHAR wPath[MAX_PATH+1]; - if (MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH+1) == 0) { - nob_log(NOB_ERROR, "Path `%s` too long", path); + int charCount = MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH); + if (charCount < 1 || charCount > MAX_PATH) { + nob_log(NOB_ERROR, "Path `%s` is invalid: %s", path, nob_win32_error_message(GetLastError())); return NOB_INVALID_FD; } Nob_Fd result = CreateFileW( @@ -1391,14 +1419,17 @@ bool nob_write_entire_file(const char *path, const void *data, size_t size) Nob_File_Type nob_get_file_type(const char *path) { #ifdef _WIN32 - WCHAR wPath[MAX_PATH+1]; - if (MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH+1) == 0) { - nob_log(NOB_ERROR, "Path `%s` too long", path); + WCHAR wPath[MAX_PATH]; + + int charCount = MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH); + if (charCount < 1 || charCount > MAX_PATH) { + nob_log(NOB_ERROR, "Path `%s` is invalid: %s", path, nob_win32_error_message(GetLastError())); return -1; } + DWORD attr = GetFileAttributesW(wPath); if (attr == INVALID_FILE_ATTRIBUTES) { - nob_log(NOB_ERROR, "Could not get file attributes of %s: %s", path, nob_win32_error_message(GetLastError())); + nob_log(NOB_ERROR, "Could not get file attributes of `%s`: %s", path, nob_win32_error_message(GetLastError())); return -1; } @@ -1423,11 +1454,14 @@ bool nob_delete_file(const char *path) { nob_log(NOB_INFO, "deleting %s", path); #ifdef _WIN32 - WCHAR wPath[MAX_PATH+1]; - if (MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH+1) == 0) { - nob_log(NOB_ERROR, "Path `%s` too long", path); + WCHAR wPath[MAX_PATH]; + + int charCount = MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH); + if (charCount < 1 || charCount > MAX_PATH) { + nob_log(NOB_ERROR, "Path `%s` is invalid: %s", path, nob_win32_error_message(GetLastError())); return false; } + if (!DeleteFileW(wPath)) { nob_log(NOB_ERROR, "Could not delete file %s: %s", path, nob_win32_error_message(GetLastError())); return false; @@ -1570,11 +1604,14 @@ int nob_needs_rebuild(const char *output_path, const char **input_paths, size_t { #ifdef _WIN32 BOOL bSuccess; - WCHAR wPath[MAX_PATH+1]; - if (MultiByteToWideChar(CP_UTF8, 0, output_path, -1, wPath, MAX_PATH+1) == 0) { - nob_log(NOB_ERROR, "Path `%s` too long", output_path); + WCHAR wPath[MAX_PATH]; + + int charCount = MultiByteToWideChar(CP_UTF8, 0, output_path, -1, wPath, MAX_PATH); + if (charCount < 1 || charCount > MAX_PATH) { + nob_log(NOB_ERROR, "Path `%s` is invalid: %s", output_path, nob_win32_error_message(GetLastError())); return -1; } + HANDLE output_path_fd = CreateFileW(wPath, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); if (output_path_fd == INVALID_HANDLE_VALUE) { // NOTE: if output does not exist it 100% must be rebuilt @@ -1592,8 +1629,10 @@ int nob_needs_rebuild(const char *output_path, const char **input_paths, size_t for (size_t i = 0; i < input_paths_count; ++i) { const char *input_path = input_paths[i]; - if (MultiByteToWideChar(CP_UTF8, 0, input_path, -1, wPath, MAX_PATH+1) == 0) { - nob_log(NOB_ERROR, "Path `%s` too long", input_path); + + charCount = MultiByteToWideChar(CP_UTF8, 0, input_path, -1, wPath, MAX_PATH); + if (charCount < 1 || charCount > MAX_PATH) { + nob_log(NOB_ERROR, "Path `%s` is invalid: %s", input_path, nob_win32_error_message(GetLastError())); return -1; } HANDLE input_path_fd = CreateFileW(wPath, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL); @@ -1664,16 +1703,22 @@ bool nob_rename(const char *old_path, const char *new_path) { nob_log(NOB_INFO, "renaming %s -> %s", old_path, new_path); #ifdef _WIN32 - WCHAR wOldPath[MAX_PATH+1]; - WCHAR wNewPath[MAX_PATH+1]; - if (MultiByteToWideChar(CP_UTF8, 0, old_path, -1, wOldPath, MAX_PATH+1) == 0) { - nob_log(NOB_ERROR, "Path `%s` too long", old_path); + int charCount; + WCHAR wOldPath[MAX_PATH]; + WCHAR wNewPath[MAX_PATH]; + + charCount = MultiByteToWideChar(CP_UTF8, 0, old_path, -1, wOldPath, MAX_PATH); + if (charCount < 1 || charCount > MAX_PATH) { + nob_log(NOB_ERROR, "Path `%s` is invalid: %s", old_path, nob_win32_error_message(GetLastError())); return false; } - if (MultiByteToWideChar(CP_UTF8, 0, new_path, -1, wNewPath, MAX_PATH+1) == 0) { - nob_log(NOB_ERROR, "Path `%s` too long", new_path); + + charCount = MultiByteToWideChar(CP_UTF8, 0, new_path, -1, wNewPath, MAX_PATH); + if (charCount < 1 || charCount > MAX_PATH) { + nob_log(NOB_ERROR, "Path `%s` is invalid: %s", new_path, nob_win32_error_message(GetLastError())); return false; } + if (!MoveFileExW(wOldPath, wNewPath, MOVEFILE_REPLACE_EXISTING)) { nob_log(NOB_ERROR, "could not rename %s to %s: %s", old_path, new_path, nob_win32_error_message(GetLastError())); return false; @@ -1854,11 +1899,14 @@ bool nob_sv_starts_with(Nob_String_View sv, Nob_String_View expected_prefix) int nob_file_exists(const char *file_path) { #if _WIN32 - WCHAR wPath[MAX_PATH+1]; - if (MultiByteToWideChar(CP_UTF8, 0, file_path, -1, wPath, MAX_PATH+1) == 0) { - nob_log(NOB_ERROR, "Path `%s` too long", file_path); + WCHAR wPath[MAX_PATH]; + + int charCount = MultiByteToWideChar(CP_UTF8, 0, file_path, -1, wPath, MAX_PATH); + if (charCount < 1 || charCount > MAX_PATH) { + nob_log(NOB_ERROR, "Path `%s` is invalid: %s", file_path, nob_win32_error_message(GetLastError())); return -1; } + DWORD dwAttrib = GetFileAttributesW(wPath); if(dwAttrib == INVALID_FILE_ATTRIBUTES){ DWORD err = GetLastError(); @@ -1883,24 +1931,29 @@ const char *nob_get_current_dir_temp(void) #ifdef _WIN32 DWORD nBufferLength = GetCurrentDirectoryW(0, NULL); if (nBufferLength == 0) { - nob_log(NOB_ERROR, "could not get current directory: %s", nob_win32_error_message(GetLastError())); + nob_log(NOB_ERROR, "Could not get current directory: %s", nob_win32_error_message(GetLastError())); return NULL; } - WCHAR wCwd[MAX_PATH+1]; + WCHAR wCwd[MAX_PATH]; if (GetCurrentDirectoryW(nBufferLength, wCwd) == 0) { - nob_log(NOB_ERROR, "could not get current directory: %s", nob_win32_error_message(GetLastError())); + nob_log(NOB_ERROR, "Could not get current directory: %s", nob_win32_error_message(GetLastError())); return NULL; } + nBufferLength = WideCharToMultiByte(CP_UTF8, 0, wCwd, -1, NULL, 0, NULL, NULL); - char *buffer = (char*) nob_temp_alloc(nBufferLength); - WideCharToMultiByte(CP_UTF8, 0, wCwd, -1, buffer, nBufferLength, NULL, NULL); + char *buffer = (char *)nob_temp_alloc(nBufferLength); + int charCount = WideCharToMultiByte(CP_UTF8, 0, wCwd, -1, buffer, nBufferLength, NULL, NULL); + if (charCount < 1 || charCount > (int)nBufferLength) { + nob_log(NOB_ERROR, "Could not get current directory: %s", nob_win32_error_message(GetLastError())); + return NULL; + } return buffer; #else char *buffer = (char*) nob_temp_alloc(PATH_MAX); if (getcwd(buffer, PATH_MAX) == NULL) { - nob_log(NOB_ERROR, "could not get current directory: %s", strerror(errno)); + nob_log(NOB_ERROR, "Could not get current directory: %s", strerror(errno)); return NULL; } @@ -1911,10 +1964,12 @@ const char *nob_get_current_dir_temp(void) bool nob_set_current_dir(const char *path) { #ifdef _WIN32 - WCHAR wPath[MAX_PATH+1]; - if (MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH) == 0) { - nob_log(NOB_ERROR, "Path `%s` too long", path); - return false; + WCHAR wPath[MAX_PATH]; + + int charCount = MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH); + if (charCount < 1 || charCount > MAX_PATH) { + nob_log(NOB_ERROR, "Path `%s` is invalid: %s", path, nob_win32_error_message(GetLastError())); + return -1; } if (!SetCurrentDirectoryW(wPath)) { nob_log(NOB_ERROR, "could not set current directory to %s: %s", path, nob_win32_error_message(GetLastError())); @@ -1941,24 +1996,53 @@ struct DIR DIR *opendir(const char *dirpath) { - NOB_ASSERT(dirpath); + NOB_ASSERT(dirpath != NULL); + if (!(*dirpath)) { // Checking for empty string + errno = ENOENT; + return NULL; + } char buffer[MAX_PATH]; - snprintf(buffer, MAX_PATH, "%s\\*", dirpath); + int charCount = snprintf(buffer, MAX_PATH, "%s\\*", dirpath); + if(charCount < 1 || charCount >= MAX_PATH) { // snprintf return DONT count the NULL terminator + errno = ENAMETOOLONG; + return NULL; + } - DIR *dir = (DIR*)NOB_REALLOC(NULL, sizeof(DIR)); + DIR *dir = (DIR *)NOB_REALLOC(NULL, sizeof(DIR)); memset(dir, 0, sizeof(DIR)); - WCHAR wBuffer[MAX_PATH+1]; - if (MultiByteToWideChar(CP_UTF8, 0, buffer, -1, wBuffer, MAX_PATH+1) == 0) { - errno = ENOSYS; + WCHAR wBuffer[MAX_PATH]; + + SetLastError(0); + charCount = MultiByteToWideChar(CP_UTF8, 0, buffer, -1, wBuffer, MAX_PATH); + if (charCount < 1 || charCount > MAX_PATH) { // MultiByteToWideChar return count the NULL terminator + switch (GetLastError()) { + case ERROR_INSUFFICIENT_BUFFER: + errno = ENAMETOOLONG; + break; + case ERROR_INVALID_FLAGS: + case ERROR_INVALID_PARAMETER: + errno = EINVAL; + break; + case ERROR_NO_UNICODE_TRANSLATION: + errno = EILSEQ; + break; + default: + errno = ENOSYS; + break; + } goto fail; } + dir->hFind = FindFirstFileW(wBuffer, &dir->data); if (dir->hFind == INVALID_HANDLE_VALUE) { // TODO: opendir should set errno accordingly on FindFirstFile fail - // https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror - errno = ENOSYS; + DWORD err = GetLastError(); + if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) + errno = ENOENT; + else + errno = ENOSYS; goto fail; } @@ -1974,10 +2058,10 @@ DIR *opendir(const char *dirpath) struct dirent *readdir(DIR *dirp) { - NOB_ASSERT(dirp); + NOB_ASSERT(dirp != NULL); if (dirp->dirent == NULL) { - dirp->dirent = (struct dirent*)NOB_REALLOC(NULL, sizeof(struct dirent)); + dirp->dirent = (struct dirent *)NOB_REALLOC(NULL, sizeof(struct dirent)); memset(dirp->dirent, 0, sizeof(struct dirent)); } else { if(!FindNextFileW(dirp->hFind, &dirp->data)) { @@ -1991,15 +2075,40 @@ struct dirent *readdir(DIR *dirp) } } - memset(dirp->dirent->d_name, 0, sizeof(dirp->dirent->d_name)); + memset(dirp->dirent->d_name, 0, MAX_PATH); + + // WideCharToMultiByte return count the NULL terminator + int charCount = WideCharToMultiByte(CP_UTF8, 0, dirp->data.cFileName, -1, dirp->dirent->d_name, MAX_PATH, NULL, NULL); - WideCharToMultiByte(CP_UTF8, 0, dirp->data.cFileName, -1, dirp->dirent->d_name, MAX_PATH+1, NULL, NULL); +#if 1 // Set errno or just crash if conversion failed??? + NOB_ASSERT((charCount > 0 && charCount <= MAX_PATH)); +#else + if (charCount < 1 || charCount > MAX_PATH) { + switch (GetLastError()) { + case ERROR_INSUFFICIENT_BUFFER: + errno = ENAMETOOLONG; + break; + case ERROR_INVALID_FLAGS: + case ERROR_INVALID_PARAMETER: + errno = EINVAL; + break; + case ERROR_NO_UNICODE_TRANSLATION: + errno = EILSEQ; + break; + default: + errno = ENOSYS; + break; + } + return NULL; + } +#endif + return dirp->dirent; } int closedir(DIR *dirp) { - NOB_ASSERT(dirp); + NOB_ASSERT(dirp != NULL); if(!FindClose(dirp->hFind)) { // TODO: closedir should set errno accordingly on FindClose fail From 78371779120450138eaf0a07b292c7907b917a05 Mon Sep 17 00:00:00 2001 From: KillerxDBr <97485370+KillerxDBr@users.noreply.github.com> Date: Thu, 22 May 2025 21:48:35 -0400 Subject: [PATCH 21/27] small fixes --- nob.h | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/nob.h b/nob.h index d1df4c3..30a436b 100644 --- a/nob.h +++ b/nob.h @@ -713,7 +713,7 @@ char *nob_win32_error_message(DWORD err) { DWORD cchBuffer = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, 0, lpBuffer, NOB_WIN32_ERR_MSG_SIZE, NULL); - if (cchBuffer == 0) { + if (cchBuffer < 1 || cchBuffer > NOB_WIN32_ERR_MSG_SIZE) { char *newErrMsg = NULL; if (GetLastError() == ERROR_MR_MID_NOT_FOUND) { newErrMsg = "Invalid Win32 error code"; @@ -1429,7 +1429,7 @@ Nob_File_Type nob_get_file_type(const char *path) DWORD attr = GetFileAttributesW(wPath); if (attr == INVALID_FILE_ATTRIBUTES) { - nob_log(NOB_ERROR, "Could not get file attributes of `%s`: %s", path, nob_win32_error_message(GetLastError())); + nob_log(NOB_ERROR, "Could not get file attributes of %s: %s", path, nob_win32_error_message(GetLastError())); return -1; } @@ -1929,23 +1929,18 @@ int nob_file_exists(const char *file_path) const char *nob_get_current_dir_temp(void) { #ifdef _WIN32 - DWORD nBufferLength = GetCurrentDirectoryW(0, NULL); - if (nBufferLength == 0) { - nob_log(NOB_ERROR, "Could not get current directory: %s", nob_win32_error_message(GetLastError())); - return NULL; - } - WCHAR wCwd[MAX_PATH]; - if (GetCurrentDirectoryW(nBufferLength, wCwd) == 0) { + DWORD WCharCount = GetCurrentDirectoryW(MAX_PATH, wCwd); + if (WCharCount < 1 || WCharCount >= MAX_PATH) { nob_log(NOB_ERROR, "Could not get current directory: %s", nob_win32_error_message(GetLastError())); return NULL; } - nBufferLength = WideCharToMultiByte(CP_UTF8, 0, wCwd, -1, NULL, 0, NULL, NULL); - char *buffer = (char *)nob_temp_alloc(nBufferLength); + int buffSize = WideCharToMultiByte(CP_UTF8, 0, wCwd, -1, NULL, 0, NULL, NULL); + char *buffer = (char *)nob_temp_alloc(buffSize); - int charCount = WideCharToMultiByte(CP_UTF8, 0, wCwd, -1, buffer, nBufferLength, NULL, NULL); - if (charCount < 1 || charCount > (int)nBufferLength) { + int charCount = WideCharToMultiByte(CP_UTF8, 0, wCwd, -1, buffer, buffSize, NULL, NULL); + if (charCount < 1 || charCount > buffSize) { nob_log(NOB_ERROR, "Could not get current directory: %s", nob_win32_error_message(GetLastError())); return NULL; } From de844c8902d8276c2fa39362025508027caf5cc1 Mon Sep 17 00:00:00 2001 From: KillerxDBr <97485370+KillerxDBr@users.noreply.github.com> Date: Thu, 22 May 2025 22:29:03 -0400 Subject: [PATCH 22/27] UTF8 support to nob_(read/write)_entire_file --- nob.h | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/nob.h b/nob.h index 30a436b..b489b62 100644 --- a/nob.h +++ b/nob.h @@ -1387,8 +1387,21 @@ bool nob_read_entire_dir(const char *parent, Nob_File_Paths *children) bool nob_write_entire_file(const char *path, const void *data, size_t size) { bool result = true; + FILE *f = NULL; - FILE *f = fopen(path, "wb"); +#ifndef _WIN32 + f = fopen(path, "wb"); +#else + WCHAR wPath[MAX_PATH]; + + int charCount = MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH); + if (charCount < 1 || charCount > MAX_PATH) { + nob_log(NOB_ERROR, "Path `%s` is invalid: %s", path, nob_win32_error_message(GetLastError())); + nob_return_defer(false); + } + + f = _wfopen(wPath, L"wb"); +#endif if (f == NULL) { nob_log(NOB_ERROR, "Could not open file %s for writing: %s\n", path, strerror(errno)); nob_return_defer(false); @@ -1735,8 +1748,21 @@ bool nob_rename(const char *old_path, const char *new_path) bool nob_read_entire_file(const char *path, Nob_String_Builder *sb) { bool result = true; + FILE *f = NULL; - FILE *f = fopen(path, "rb"); +#ifndef _WIN32 + f = fopen(path, "rb"); +#else + WCHAR wPath[MAX_PATH]; + + int charCount = MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH); + if (charCount < 1 || charCount > MAX_PATH) { + nob_log(NOB_ERROR, "Path `%s` is invalid: %s", path, nob_win32_error_message(GetLastError())); + nob_return_defer(false); + } + + f = _wfopen(wPath, L"rb"); +#endif if (f == NULL) nob_return_defer(false); if (fseek(f, 0, SEEK_END) < 0) nob_return_defer(false); #ifndef _WIN32 From dbebb36296be1600d9c382342340e87b77147292 Mon Sep 17 00:00:00 2001 From: KillerxDBr <97485370+KillerxDBr@users.noreply.github.com> Date: Thu, 22 May 2025 22:29:31 -0400 Subject: [PATCH 23/27] flush stderr before abort --- nob.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nob.h b/nob.h index b489b62..6fda427 100644 --- a/nob.h +++ b/nob.h @@ -225,8 +225,8 @@ #endif #define NOB_UNUSED(value) (void)(value) -#define NOB_TODO(message) do { fprintf(stderr, "%s:%d: TODO: %s\n", __FILE__, __LINE__, message); abort(); } while(0) -#define NOB_UNREACHABLE(message) do { fprintf(stderr, "%s:%d: UNREACHABLE: %s\n", __FILE__, __LINE__, message); abort(); } while(0) +#define NOB_TODO(message) do { fprintf(stderr, "%s:%d: TODO: %s\n", __FILE__, __LINE__, message); fflush(stderr); abort(); } while(0) +#define NOB_UNREACHABLE(message) do { fprintf(stderr, "%s:%d: UNREACHABLE: %s\n", __FILE__, __LINE__, message); fflush(stderr); abort(); } while(0) #define NOB_ARRAY_LEN(array) (sizeof(array)/sizeof(array[0])) #define NOB_ARRAY_GET(array, index) \ From 061bf7d2d8f69042d8f7269664cab4e470b57940 Mon Sep 17 00:00:00 2001 From: KillerxDBr <97485370+KillerxDBr@users.noreply.github.com> Date: Sat, 31 May 2025 13:36:12 -0400 Subject: [PATCH 24/27] Removing useless error checks --- nob.h | 97 +++++++++++++++++++++++++++-------------------------------- 1 file changed, 44 insertions(+), 53 deletions(-) diff --git a/nob.h b/nob.h index 6fda427..d88a173 100644 --- a/nob.h +++ b/nob.h @@ -729,9 +729,8 @@ char *nob_win32_error_message(DWORD err) { return (char *)&win32ErrMsg; } - errMsgSize = WideCharToMultiByte(CP_UTF8, 0, lpBuffer, -1, win32ErrMsg, NOB_WIN32_ERR_MSG_SIZE, NULL, NULL) -1; - if (errMsgSize < 1 || errMsgSize > NOB_WIN32_ERR_MSG_SIZE) { - return "Insufficient error message buffer size"; + if (WideCharToMultiByte(CP_UTF8, 0, lpBuffer, -1, win32ErrMsg, NOB_WIN32_ERR_MSG_SIZE, NULL, NULL) == 0) { + return "Could not get error message"; } while (errMsgSize > 1 && isspace(win32ErrMsg[errMsgSize - 1])) { win32ErrMsg[--errMsgSize] = '\0'; @@ -803,8 +802,7 @@ bool nob_mkdir_if_not_exists(const char *path) #ifdef _WIN32 WCHAR wPath[MAX_PATH]; - int charCount = MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH); - if (charCount < 1 || charCount > MAX_PATH) { + if (MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH) == 0) { nob_log(NOB_ERROR, "Path `%s` is invalid: %s", path, nob_win32_error_message(GetLastError())); return false; } @@ -839,19 +837,16 @@ bool nob_copy_file(const char *src_path, const char *dst_path) { nob_log(NOB_INFO, "copying %s -> %s", src_path, dst_path); #ifdef _WIN32 - int charCount; WCHAR wSrcPath[MAX_PATH]; WCHAR wDstPath[MAX_PATH]; - charCount = MultiByteToWideChar(CP_UTF8, 0, src_path, -1, wSrcPath, MAX_PATH); - if (charCount < 1 || charCount > MAX_PATH) { - nob_log(NOB_ERROR, "Path `%s` is invalid: %s", src_path, nob_win32_error_message(GetLastError())); + if (MultiByteToWideChar(CP_UTF8, 0, src_path, -1, wSrcPath, MAX_PATH) == 0) { + nob_log(NOB_ERROR, "Source Path `%s` is invalid: %s", src_path, nob_win32_error_message(GetLastError())); return false; } - charCount = MultiByteToWideChar(CP_UTF8, 0, dst_path, -1, wDstPath, MAX_PATH); - if (charCount < 1 || charCount > MAX_PATH) { - nob_log(NOB_ERROR, "Path `%s` is invalid: %s", dst_path, nob_win32_error_message(GetLastError())); + if (MultiByteToWideChar(CP_UTF8, 0, dst_path, -1, wDstPath, MAX_PATH) == 0) { + nob_log(NOB_ERROR, "Destination Path `%s` is invalid: %s", dst_path, nob_win32_error_message(GetLastError())); return false; } @@ -1007,15 +1002,18 @@ Nob_Proc nob_cmd_run_async_redirect(Nob_Cmd cmd, Nob_Cmd_Redirect redirect) nob_sb_append_null(&sb); WCHAR *wCmdLine; - int cchCmdLine = MultiByteToWideChar(CP_UTF8, 0, sb.items, -1, NULL, 0); // https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw // per MSDN ref of `lpCommandLine`, "The maximum length of this string is 32,767 characters" + int cchCmdLine = MultiByteToWideChar(CP_UTF8, 0, sb.items, -1, NULL, 0); + if (cchCmdLine == 0 || cchCmdLine > 0x7FFF) { + DWORD err = GetLastError(); + nob_log(NOB_ERROR, "Could not convert Command Line to Wide String: %s", nob_win32_error_message(err ? err : ERROR_INSUFFICIENT_BUFFER)); + return NOB_INVALID_PROC; + } wCmdLine = nob_temp_alloc(cchCmdLine * sizeof(WCHAR)); NOB_ASSERT(wCmdLine != NULL && "Increase NOB_TEMP_CAPACITY"); - // It's impossible to now have a large enough buffer here and user will know if they have invalid character - cchCmdLine = MultiByteToWideChar(CP_UTF8, 0, sb.items, -1, wCmdLine, cchCmdLine); - if (cchCmdLine < 1 || cchCmdLine > 0x7FFF) { + if (MultiByteToWideChar(CP_UTF8, 0, sb.items, -1, wCmdLine, cchCmdLine) == 0) { nob_log(NOB_ERROR, "Could not convert Command Line to Wide String: %s", nob_win32_error_message(GetLastError())); nob_temp_rewind(cp); return NOB_INVALID_PROC; @@ -1125,8 +1123,7 @@ Nob_Fd nob_fd_open_for_read(const char *path) saAttr.bInheritHandle = TRUE; WCHAR wPath[MAX_PATH]; - int charCount = MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH); - if (charCount < 1 || charCount > MAX_PATH) { + if (MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH) == 0) { nob_log(NOB_ERROR, "Path `%s` is invalid: %s", path, nob_win32_error_message(GetLastError())); return NOB_INVALID_FD; } @@ -1165,8 +1162,7 @@ Nob_Fd nob_fd_open_for_write(const char *path) saAttr.bInheritHandle = TRUE; WCHAR wPath[MAX_PATH]; - int charCount = MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH); - if (charCount < 1 || charCount > MAX_PATH) { + if (MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH) == 0) { nob_log(NOB_ERROR, "Path `%s` is invalid: %s", path, nob_win32_error_message(GetLastError())); return NOB_INVALID_FD; } @@ -1394,8 +1390,7 @@ bool nob_write_entire_file(const char *path, const void *data, size_t size) #else WCHAR wPath[MAX_PATH]; - int charCount = MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH); - if (charCount < 1 || charCount > MAX_PATH) { + if (MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH) == 0) { nob_log(NOB_ERROR, "Path `%s` is invalid: %s", path, nob_win32_error_message(GetLastError())); nob_return_defer(false); } @@ -1434,8 +1429,7 @@ Nob_File_Type nob_get_file_type(const char *path) #ifdef _WIN32 WCHAR wPath[MAX_PATH]; - int charCount = MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH); - if (charCount < 1 || charCount > MAX_PATH) { + if (MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH) == 0) { nob_log(NOB_ERROR, "Path `%s` is invalid: %s", path, nob_win32_error_message(GetLastError())); return -1; } @@ -1469,8 +1463,7 @@ bool nob_delete_file(const char *path) #ifdef _WIN32 WCHAR wPath[MAX_PATH]; - int charCount = MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH); - if (charCount < 1 || charCount > MAX_PATH) { + if (MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH) == 0) { nob_log(NOB_ERROR, "Path `%s` is invalid: %s", path, nob_win32_error_message(GetLastError())); return false; } @@ -1619,8 +1612,7 @@ int nob_needs_rebuild(const char *output_path, const char **input_paths, size_t BOOL bSuccess; WCHAR wPath[MAX_PATH]; - int charCount = MultiByteToWideChar(CP_UTF8, 0, output_path, -1, wPath, MAX_PATH); - if (charCount < 1 || charCount > MAX_PATH) { + if (MultiByteToWideChar(CP_UTF8, 0, output_path, -1, wPath, MAX_PATH) == 0) { nob_log(NOB_ERROR, "Path `%s` is invalid: %s", output_path, nob_win32_error_message(GetLastError())); return -1; } @@ -1643,8 +1635,7 @@ int nob_needs_rebuild(const char *output_path, const char **input_paths, size_t for (size_t i = 0; i < input_paths_count; ++i) { const char *input_path = input_paths[i]; - charCount = MultiByteToWideChar(CP_UTF8, 0, input_path, -1, wPath, MAX_PATH); - if (charCount < 1 || charCount > MAX_PATH) { + if (MultiByteToWideChar(CP_UTF8, 0, input_path, -1, wPath, MAX_PATH) == 0) { nob_log(NOB_ERROR, "Path `%s` is invalid: %s", input_path, nob_win32_error_message(GetLastError())); return -1; } @@ -1716,19 +1707,16 @@ bool nob_rename(const char *old_path, const char *new_path) { nob_log(NOB_INFO, "renaming %s -> %s", old_path, new_path); #ifdef _WIN32 - int charCount; WCHAR wOldPath[MAX_PATH]; WCHAR wNewPath[MAX_PATH]; - charCount = MultiByteToWideChar(CP_UTF8, 0, old_path, -1, wOldPath, MAX_PATH); - if (charCount < 1 || charCount > MAX_PATH) { - nob_log(NOB_ERROR, "Path `%s` is invalid: %s", old_path, nob_win32_error_message(GetLastError())); + if (MultiByteToWideChar(CP_UTF8, 0, old_path, -1, wOldPath, MAX_PATH) == 0) { + nob_log(NOB_ERROR, "Source Path `%s` is invalid: %s", old_path, nob_win32_error_message(GetLastError())); return false; } - charCount = MultiByteToWideChar(CP_UTF8, 0, new_path, -1, wNewPath, MAX_PATH); - if (charCount < 1 || charCount > MAX_PATH) { - nob_log(NOB_ERROR, "Path `%s` is invalid: %s", new_path, nob_win32_error_message(GetLastError())); + if (MultiByteToWideChar(CP_UTF8, 0, new_path, -1, wNewPath, MAX_PATH) == 0) { + nob_log(NOB_ERROR, "Destination Path `%s` is invalid: %s", new_path, nob_win32_error_message(GetLastError())); return false; } @@ -1755,8 +1743,7 @@ bool nob_read_entire_file(const char *path, Nob_String_Builder *sb) #else WCHAR wPath[MAX_PATH]; - int charCount = MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH); - if (charCount < 1 || charCount > MAX_PATH) { + if (MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH) == 0) { nob_log(NOB_ERROR, "Path `%s` is invalid: %s", path, nob_win32_error_message(GetLastError())); nob_return_defer(false); } @@ -1927,8 +1914,7 @@ int nob_file_exists(const char *file_path) #if _WIN32 WCHAR wPath[MAX_PATH]; - int charCount = MultiByteToWideChar(CP_UTF8, 0, file_path, -1, wPath, MAX_PATH); - if (charCount < 1 || charCount > MAX_PATH) { + if (MultiByteToWideChar(CP_UTF8, 0, file_path, -1, wPath, MAX_PATH) == 0) { nob_log(NOB_ERROR, "Path `%s` is invalid: %s", file_path, nob_win32_error_message(GetLastError())); return -1; } @@ -1957,16 +1943,25 @@ const char *nob_get_current_dir_temp(void) #ifdef _WIN32 WCHAR wCwd[MAX_PATH]; DWORD WCharCount = GetCurrentDirectoryW(MAX_PATH, wCwd); - if (WCharCount < 1 || WCharCount >= MAX_PATH) { + + if (WCharCount == 0 || WCharCount >= MAX_PATH) { nob_log(NOB_ERROR, "Could not get current directory: %s", nob_win32_error_message(GetLastError())); return NULL; } int buffSize = WideCharToMultiByte(CP_UTF8, 0, wCwd, -1, NULL, 0, NULL, NULL); + if (buffSize == 0) { + nob_log(NOB_ERROR, "Could not get current directory: %s", nob_win32_error_message(GetLastError())); + return NULL; + } + char *buffer = (char *)nob_temp_alloc(buffSize); + if (buffer == NULL) { + nob_log(NOB_ERROR, "Could not get current directory: %s", "Increase NOB_TEMP_CAPACITY"); + return NULL; + } - int charCount = WideCharToMultiByte(CP_UTF8, 0, wCwd, -1, buffer, buffSize, NULL, NULL); - if (charCount < 1 || charCount > buffSize) { + if (WideCharToMultiByte(CP_UTF8, 0, wCwd, -1, buffer, buffSize, NULL, NULL) == 0) { nob_log(NOB_ERROR, "Could not get current directory: %s", nob_win32_error_message(GetLastError())); return NULL; } @@ -1987,10 +1982,9 @@ bool nob_set_current_dir(const char *path) #ifdef _WIN32 WCHAR wPath[MAX_PATH]; - int charCount = MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH); - if (charCount < 1 || charCount > MAX_PATH) { + if (MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH) == 0) { nob_log(NOB_ERROR, "Path `%s` is invalid: %s", path, nob_win32_error_message(GetLastError())); - return -1; + return false; } if (!SetCurrentDirectoryW(wPath)) { nob_log(NOB_ERROR, "could not set current directory to %s: %s", path, nob_win32_error_message(GetLastError())); @@ -2035,9 +2029,7 @@ DIR *opendir(const char *dirpath) WCHAR wBuffer[MAX_PATH]; - SetLastError(0); - charCount = MultiByteToWideChar(CP_UTF8, 0, buffer, -1, wBuffer, MAX_PATH); - if (charCount < 1 || charCount > MAX_PATH) { // MultiByteToWideChar return count the NULL terminator + if (MultiByteToWideChar(CP_UTF8, 0, buffer, -1, wBuffer, MAX_PATH) == 0) { switch (GetLastError()) { case ERROR_INSUFFICIENT_BUFFER: errno = ENAMETOOLONG; @@ -2098,13 +2090,12 @@ struct dirent *readdir(DIR *dirp) memset(dirp->dirent->d_name, 0, MAX_PATH); - // WideCharToMultiByte return count the NULL terminator int charCount = WideCharToMultiByte(CP_UTF8, 0, dirp->data.cFileName, -1, dirp->dirent->d_name, MAX_PATH, NULL, NULL); #if 1 // Set errno or just crash if conversion failed??? - NOB_ASSERT((charCount > 0 && charCount <= MAX_PATH)); + NOB_ASSERT(charCount != 0); #else - if (charCount < 1 || charCount > MAX_PATH) { + if (charCount == 0) { switch (GetLastError()) { case ERROR_INSUFFICIENT_BUFFER: errno = ENAMETOOLONG; From 8693a90fca01a56ab1ffc59da4ec631a0e2a1d9b Mon Sep 17 00:00:00 2001 From: KxD-Work Date: Tue, 3 Jun 2025 16:13:13 -0400 Subject: [PATCH 25/27] Make nob_win32_error_message return const char * and using SV to trim error message --- nob.h | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/nob.h b/nob.h index d88a173..cff9554 100644 --- a/nob.h +++ b/nob.h @@ -685,7 +685,7 @@ static int closedir(DIR *dirp); #ifdef _WIN32 -char *nob_win32_error_message(DWORD err); +const char *nob_win32_error_message(DWORD err); #endif // _WIN32 @@ -706,15 +706,14 @@ Nob_Log_Level nob_minimal_log_level = NOB_INFO; #define NOB_WIN32_ERR_MSG_SIZE (4 * 1024) #endif // NOB_WIN32_ERR_MSG_SIZE -char *nob_win32_error_message(DWORD err) { - int errMsgSize; +const char *nob_win32_error_message(DWORD err) { static char win32ErrMsg[NOB_WIN32_ERR_MSG_SIZE]; WCHAR lpBuffer[NOB_WIN32_ERR_MSG_SIZE]; DWORD cchBuffer = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, 0, lpBuffer, NOB_WIN32_ERR_MSG_SIZE, NULL); - if (cchBuffer < 1 || cchBuffer > NOB_WIN32_ERR_MSG_SIZE) { - char *newErrMsg = NULL; + if (cchBuffer == 0) { + const char *newErrMsg = NULL; if (GetLastError() == ERROR_MR_MID_NOT_FOUND) { newErrMsg = "Invalid Win32 error code"; } else { @@ -722,21 +721,21 @@ char *nob_win32_error_message(DWORD err) { } // snprintf return DONT count the NULL terminator - errMsgSize = snprintf(win32ErrMsg, NOB_WIN32_ERR_MSG_SIZE, "%s for 0x%lX", newErrMsg, err); - if(errMsgSize < 1 || errMsgSize >= NOB_WIN32_ERR_MSG_SIZE) + int errMsgSize = snprintf(win32ErrMsg, NOB_WIN32_ERR_MSG_SIZE, "%s for 0x%lX", newErrMsg, err); + if (errMsgSize < 1 || errMsgSize >= NOB_WIN32_ERR_MSG_SIZE) return newErrMsg; - return (char *)&win32ErrMsg; + return win32ErrMsg; } if (WideCharToMultiByte(CP_UTF8, 0, lpBuffer, -1, win32ErrMsg, NOB_WIN32_ERR_MSG_SIZE, NULL, NULL) == 0) { return "Could not get error message"; } - while (errMsgSize > 1 && isspace(win32ErrMsg[errMsgSize - 1])) { - win32ErrMsg[--errMsgSize] = '\0'; - } - return win32ErrMsg; + Nob_String_View sv = nob_sv_trim(nob_sv_from_cstr(win32ErrMsg)); + *(char *)&sv.data[sv.count] = '\0'; + + return sv.data; } #endif // _WIN32 From b8f8eaffa526928bcc223c3ba0ef9124f6b65137 Mon Sep 17 00:00:00 2001 From: KillerxDBr <97485370+KillerxDBr@users.noreply.github.com> Date: Fri, 6 Jun 2025 13:35:34 -0400 Subject: [PATCH 26/27] Fix String Builder memory leak --- nob.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nob.h b/nob.h index cff9554..27675ef 100644 --- a/nob.h +++ b/nob.h @@ -1007,6 +1007,7 @@ Nob_Proc nob_cmd_run_async_redirect(Nob_Cmd cmd, Nob_Cmd_Redirect redirect) if (cchCmdLine == 0 || cchCmdLine > 0x7FFF) { DWORD err = GetLastError(); nob_log(NOB_ERROR, "Could not convert Command Line to Wide String: %s", nob_win32_error_message(err ? err : ERROR_INSUFFICIENT_BUFFER)); + nob_sb_free(sb); return NOB_INVALID_PROC; } wCmdLine = nob_temp_alloc(cchCmdLine * sizeof(WCHAR)); @@ -1015,6 +1016,7 @@ Nob_Proc nob_cmd_run_async_redirect(Nob_Cmd cmd, Nob_Cmd_Redirect redirect) if (MultiByteToWideChar(CP_UTF8, 0, sb.items, -1, wCmdLine, cchCmdLine) == 0) { nob_log(NOB_ERROR, "Could not convert Command Line to Wide String: %s", nob_win32_error_message(GetLastError())); nob_temp_rewind(cp); + nob_sb_free(sb); return NOB_INVALID_PROC; } From c811ec48954104d8d9a1dee94e0405450e24c2d6 Mon Sep 17 00:00:00 2001 From: KillerxDBr <97485370+KillerxDBr@users.noreply.github.com> Date: Fri, 20 Jun 2025 18:37:22 -0400 Subject: [PATCH 27/27] Reverting use of String View to trim line breaks --- nob.h | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/nob.h b/nob.h index 27675ef..30d0e9e 100644 --- a/nob.h +++ b/nob.h @@ -196,8 +196,8 @@ # define _IMM_ # define _WINCON_ # include -# include # include +# include #else # include # include @@ -707,35 +707,37 @@ Nob_Log_Level nob_minimal_log_level = NOB_INFO; #endif // NOB_WIN32_ERR_MSG_SIZE const char *nob_win32_error_message(DWORD err) { + int errMsgSize; static char win32ErrMsg[NOB_WIN32_ERR_MSG_SIZE]; WCHAR lpBuffer[NOB_WIN32_ERR_MSG_SIZE]; DWORD cchBuffer = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, 0, lpBuffer, NOB_WIN32_ERR_MSG_SIZE, NULL); + const char *newErrMsg = "Could not get error message"; if (cchBuffer == 0) { - const char *newErrMsg = NULL; if (GetLastError() == ERROR_MR_MID_NOT_FOUND) { newErrMsg = "Invalid Win32 error code"; - } else { - newErrMsg = "Could not get error message"; } // snprintf return DONT count the NULL terminator - int errMsgSize = snprintf(win32ErrMsg, NOB_WIN32_ERR_MSG_SIZE, "%s for 0x%lX", newErrMsg, err); + errMsgSize = snprintf(win32ErrMsg, NOB_WIN32_ERR_MSG_SIZE, "%s for 0x%lX", newErrMsg, err); if (errMsgSize < 1 || errMsgSize >= NOB_WIN32_ERR_MSG_SIZE) return newErrMsg; return win32ErrMsg; } - if (WideCharToMultiByte(CP_UTF8, 0, lpBuffer, -1, win32ErrMsg, NOB_WIN32_ERR_MSG_SIZE, NULL, NULL) == 0) { - return "Could not get error message"; + errMsgSize = WideCharToMultiByte(CP_UTF8, 0, lpBuffer, -1, win32ErrMsg, NOB_WIN32_ERR_MSG_SIZE, NULL, NULL); + if (errMsgSize == 0) { + return newErrMsg; } - Nob_String_View sv = nob_sv_trim(nob_sv_from_cstr(win32ErrMsg)); - *(char *)&sv.data[sv.count] = '\0'; + errMsgSize--; // Remove null terminator from count + while (errMsgSize > 1 && isspace(win32ErrMsg[errMsgSize - 1])) { + win32ErrMsg[--errMsgSize] = '\0'; + } - return sv.data; + return win32ErrMsg; } #endif // _WIN32