From 3f6c3bfd1bf2baebb49a68d3b463f6b414555d73 Mon Sep 17 00:00:00 2001 From: pommicket Date: Fri, 29 Jul 2022 16:45:46 -0400 Subject: fixed non-ascii path handling on windows --- filesystem-win.c | 51 ++++++++++++++++++++++++++++++++------------------- main.c | 20 +++++++++++++------- 2 files changed, 45 insertions(+), 26 deletions(-) diff --git a/filesystem-win.c b/filesystem-win.c index a16dc6b..dc17eaf 100644 --- a/filesystem-win.c +++ b/filesystem-win.c @@ -13,7 +13,11 @@ static FsType windows_file_attributes_to_type(DWORD attrs) { } FsType fs_path_type(char const *path) { - return windows_file_attributes_to_type(GetFileAttributesA(path)); + WCHAR wide_path[4100]; + if (MultiByteToWideChar(CP_UTF8, 0, path, -1, wide_path, sizeof wide_path) == 0) { + return FS_NON_EXISTENT; + } + return windows_file_attributes_to_type(GetFileAttributesW(wide_path)); } FsPermission fs_path_permission(char const *path) { @@ -28,39 +32,46 @@ bool fs_file_exists(char const *path) { } FsDirectoryEntry **fs_list_directory(char const *dirname) { - char file_pattern[256] = {0}; + char file_pattern[1000] = {0}; FsDirectoryEntry **files = NULL; - WIN32_FIND_DATA find_data; + WIN32_FIND_DATAW find_data; HANDLE fhandle; assert(*dirname); sprintf_s(file_pattern, sizeof file_pattern, "%s%s*", dirname, dirname[strlen(dirname) - 1] == PATH_SEPARATOR ? "" : PATH_SEPARATOR_STR); - fhandle = FindFirstFileA(file_pattern, &find_data); + wchar_t wide_pattern[1024] = {0}; + MultiByteToWideChar(CP_UTF8, 0, file_pattern, -1, wide_pattern, sizeof wide_pattern - 1); + + fhandle = FindFirstFileW(wide_pattern, &find_data); if (fhandle != INVALID_HANDLE_VALUE) { // first, figure out number of files int nfiles = 1, idx = 0; - while (FindNextFile(fhandle, &find_data)) { + while (FindNextFileW(fhandle, &find_data)) { ++nfiles; } FindClose(fhandle); // now, fill out files array files = calloc(nfiles + 1, sizeof *files); if (files) { - fhandle = FindFirstFileA(file_pattern, &find_data); + fhandle = FindFirstFileW(wide_pattern, &find_data); if (fhandle != INVALID_HANDLE_VALUE) { do { if (idx < nfiles) { - const char *filename = find_data.cFileName; - size_t len = strlen(filename); - FsDirectoryEntry *entry = calloc(1, sizeof *entry + len + 1); + LPWSTR wide_filename = find_data.cFileName; + size_t wide_len = wcslen(wide_filename); + size_t cap = 4 * wide_len + 4; + FsDirectoryEntry *entry = calloc(1, sizeof *entry + cap); + + if (entry) { + if (WideCharToMultiByte(CP_UTF8, 0, wide_filename, (int)wide_len, entry->name, (int)cap - 1, NULL, NULL) == 0) + break; DWORD attrs = find_data.dwFileAttributes; entry->type = windows_file_attributes_to_type(attrs); - memcpy(entry->name, filename, len); files[idx++] = entry; } else break; // stop now } - } while (FindNextFile(fhandle, &find_data)); + } while (FindNextFileW(fhandle, &find_data)); FindClose(fhandle); } } @@ -69,7 +80,11 @@ FsDirectoryEntry **fs_list_directory(char const *dirname) { } int fs_mkdir(char const *path) { - if (CreateDirectoryA(path, NULL)) { + WCHAR wide_path[4100]; + if (MultiByteToWideChar(CP_UTF8, 0, path, -1, wide_path, sizeof wide_path) == 0) + return -1; + + if (CreateDirectoryW(wide_path, NULL)) { // directory created successfully return 1; } else { @@ -82,12 +97,10 @@ int fs_mkdir(char const *path) { int fs_get_cwd(char *buf, size_t buflen) { assert(buf && buflen); - DWORD pathlen = GetCurrentDirectory((DWORD)buflen, buf); - if (pathlen == 0) { - return -1; - } else if (pathlen < buflen) { // it's confusing, but this is < and not <= - return 1; - } else { + wchar_t wide_path[4100]; + DWORD wide_pathlen = GetCurrentDirectoryW(sizeof wide_path - 1, wide_path); + if (wide_pathlen == 0) return -1; + if (WideCharToMultiByte(CP_UTF8, 0, wide_path, (int)wide_pathlen, buf, (int)buflen, NULL, NULL) == 0) return 0; - } + return 1; } diff --git a/main.c b/main.c index 2857869..791af49 100644 --- a/main.c +++ b/main.c @@ -245,17 +245,17 @@ INT WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, (void)hInstance; (void)hPrevInstance; (void)lpCmdLine; (void)nCmdShow; int argc = 0; LPWSTR* wide_argv = CommandLineToArgvW(GetCommandLineW(), &argc); - char** argv = malloc(argc * sizeof *argv); + char** argv = calloc(argc + 1, sizeof *argv); if (!argv) { die("Out of memory."); } for (int i = 0; i < argc; i++) { LPWSTR wide_arg = wide_argv[i]; int len = (int)wcslen(wide_arg); - argv[i] = malloc(len + 1); - if (!argv[i]) die("Out of memory."); - for (int j = 0; j <= len; j++) - argv[i][j] = (char)wide_arg[j]; + int bufsz = len * 4 + 8; + argv[i] = calloc((size_t)bufsz, 1); + if (!argv[i]) die("Out of memory."); + WideCharToMultiByte(CP_UTF8, 0, wide_arg, len, argv[i], bufsz - 1, NULL, NULL); } LocalFree(wide_argv); #else @@ -281,7 +281,11 @@ int main(int argc, char **argv) { #error "Unrecognized operating system." #endif - setlocale(LC_ALL, ""); // allow unicode + #if _WIN32 + setlocale(LC_ALL, ".65001"); + #else + setlocale(LC_ALL, "C.UTF-8"); + #endif // read command-line arguments char const *starting_filename = NULL; @@ -335,8 +339,10 @@ int main(int argc, char **argv) { } // on Windows, the global data directory is just the directory where the executable is. + WCHAR executable_wide_path[TED_PATH_MAX] = {0}; char executable_path[TED_PATH_MAX] = {0}; - if (GetModuleFileNameA(NULL, executable_path, sizeof executable_path) > 0) { + if (GetModuleFileNameW(NULL, executable_wide_path, sizeof executable_wide_path - 1) > 0) { + WideCharToMultiByte(CP_UTF8, 0, executable_wide_path, -1, executable_path, sizeof executable_path, NULL, NULL); char *last_backslash = strrchr(executable_path, '\\'); if (last_backslash) { *last_backslash = '\0'; -- cgit v1.2.3