diff options
Diffstat (limited to 'src/win32')
-rw-r--r-- | src/win32/environment_win32.c | 21 | ||||
-rw-r--r-- | src/win32/filesystem_win32.c | 183 | ||||
-rw-r--r-- | src/win32/win32_util.c | 73 | ||||
-rw-r--r-- | src/win32/win32_util.h | 31 |
4 files changed, 267 insertions, 41 deletions
diff --git a/src/win32/environment_win32.c b/src/win32/environment_win32.c index 64feee5..ce5f00e 100644 --- a/src/win32/environment_win32.c +++ b/src/win32/environment_win32.c @@ -1,6 +1,8 @@ // Copyright 2012-2024 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC +#include "win32_util.h" + #include <zix/environment.h> #include <windows.h> @@ -9,14 +11,27 @@ char* zix_expand_environment_strings(ZixAllocator* const allocator, const char* const string) { - const DWORD size = ExpandEnvironmentStrings(string, NULL, 0U); + ArgPathChar* const wstring = arg_path_new(allocator, string); + + const DWORD size = ExpandEnvironmentStrings(wstring, NULL, 0U); if (!size) { + arg_path_free(allocator, wstring); return NULL; } - char* const out = (char*)zix_calloc(allocator, (size_t)size + 1U, 1U); + TCHAR* const out = + (TCHAR*)zix_calloc(allocator, (size_t)size + 1U, sizeof(TCHAR)); if (out) { - ExpandEnvironmentStrings(string, out, size + 1U); + ExpandEnvironmentStrings(wstring, out, size + 1U); } + + arg_path_free(allocator, wstring); + +#ifdef UNICODE + char* const result = zix_wchar_to_utf8(allocator, out); + zix_free(allocator, out); + return result; +#else return out; +#endif } diff --git a/src/win32/filesystem_win32.c b/src/win32/filesystem_win32.c index d311bf8..c88b1ff 100644 --- a/src/win32/filesystem_win32.c +++ b/src/win32/filesystem_win32.c @@ -5,6 +5,7 @@ #include "../errno_status.h" #include "../zix_config.h" +#include "win32_util.h" #include <zix/allocator.h> #include <zix/bump_allocator.h> @@ -16,6 +17,7 @@ #include <io.h> #include <limits.h> #include <sys/stat.h> +#include <tchar.h> #include <windows.h> #include <errno.h> @@ -25,6 +27,34 @@ #include <stdlib.h> #include <string.h> +#ifdef UNICODE + +static char* +path_result(ZixAllocator* const allocator, wchar_t* const path) +{ + if (!path) { + return NULL; + } + + static const wchar_t* const long_prefix = L"\\\\?\\"; + + const size_t p = !wcsncmp(path, long_prefix, 4U) ? 4U : 0U; + char* const result = zix_wchar_to_utf8(allocator, path + p); + zix_free(allocator, path); + return result; +} + +#else // !defined(UNICODE) + +static char* +path_result(ZixAllocator* const allocator, char* const path) +{ + (void)allocator; + return path; +} + +#endif + static inline ZixStatus zix_winerror_status(const DWORD e) { @@ -67,8 +97,15 @@ zix_copy_file(ZixAllocator* const allocator, { (void)allocator; - return zix_windows_status( - CopyFile(src, dst, !(options & ZIX_COPY_OPTION_OVERWRITE_EXISTING))); + ArgPathChar* const wsrc = arg_path_new(allocator, src); + ArgPathChar* const wdst = arg_path_new(allocator, dst); + + const BOOL ret = + CopyFile(wsrc, wdst, !(options & ZIX_COPY_OPTION_OVERWRITE_EXISTING)); + + arg_path_free(allocator, wdst); + arg_path_free(allocator, wsrc); + return zix_windows_status(ret); } /// Linear Congruential Generator for making random 32-bit integers @@ -123,9 +160,15 @@ zix_create_temporary_directory(ZixAllocator* const allocator, ZixStatus zix_remove(const char* const path) { - return zix_windows_status((zix_file_type(path) == ZIX_FILE_TYPE_DIRECTORY) - ? RemoveDirectory(path) - : DeleteFile(path)); + ArgPathChar* const wpath = arg_path_new(NULL, path); + const DWORD attrs = GetFileAttributes(wpath); + + const BOOL success = + ((attrs & FILE_ATTRIBUTE_DIRECTORY) ? RemoveDirectory(wpath) + : DeleteFile(wpath)); + + arg_path_free(NULL, wpath); + return zix_windows_status(success); } void @@ -135,9 +178,28 @@ zix_dir_for_each(const char* const path, const char* name, void* data)) { + static const TCHAR* const dot = TEXT("."); + static const TCHAR* const dotdot = TEXT(".."); + +#ifdef UNICODE + const int path_size = MultiByteToWideChar(CP_UTF8, 0, path, -1, NULL, 0); + if (path_size < 1) { + return; + } + + const size_t path_len = (size_t)path_size - 1U; + TCHAR* const pat = (TCHAR*)zix_calloc(NULL, path_len + 4U, sizeof(TCHAR)); + if (!pat) { + return; + } + + MultiByteToWideChar(CP_UTF8, 0, path, -1, pat, path_size); +#else const size_t path_len = strlen(path); - char pat[MAX_PATH + 2U]; + TCHAR* const pat = (TCHAR*)zix_calloc(NULL, path_len + 2U, sizeof(TCHAR)); memcpy(pat, path, path_len + 1U); +#endif + pat[path_len] = '\\'; pat[path_len + 1U] = '*'; pat[path_len + 2U] = '\0'; @@ -146,8 +208,14 @@ zix_dir_for_each(const char* const path, HANDLE fh = FindFirstFile(pat, &fd); if (fh != INVALID_HANDLE_VALUE) { do { - if (!!strcmp(fd.cFileName, ".") && !!strcmp(fd.cFileName, "..")) { + if (!!_tcscmp(fd.cFileName, dot) && !!_tcscmp(fd.cFileName, dotdot)) { +#ifdef UNICODE + char* const name = zix_wchar_to_utf8(NULL, fd.cFileName); + f(path, name, data); + zix_free(NULL, name); +#else f(path, fd.cFileName, data); +#endif } } while (FindNextFile(fh, &fd)); } @@ -187,10 +255,12 @@ zix_canonical_path(ZixAllocator* const allocator, const char* const path) return NULL; } + ArgPathChar* const wpath = arg_path_new(allocator, path); + #if USE_GETFINALPATHNAMEBYHANDLE // Vista+ const HANDLE h = - CreateFile(path, + CreateFile(wpath, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, @@ -198,37 +268,37 @@ zix_canonical_path(ZixAllocator* const allocator, const char* const path) FILE_FLAG_BACKUP_SEMANTICS, NULL); - if (h == INVALID_HANDLE_VALUE) { - return NULL; - } - - const DWORD flags = FILE_NAME_NORMALIZED | VOLUME_NAME_DOS; - const DWORD length = GetFinalPathNameByHandle(h, NULL, 0U, flags); - TCHAR* final = NULL; - if (length) { - final = (TCHAR*)zix_calloc(allocator, (size_t)length + 1U, sizeof(TCHAR)); - if (final) { - GetFinalPathNameByHandle(h, final, length + 1U, flags); + TCHAR* final = NULL; + if (h != INVALID_HANDLE_VALUE) { + const DWORD flags = FILE_NAME_NORMALIZED | VOLUME_NAME_DOS; + const DWORD length = GetFinalPathNameByHandle(h, NULL, 0U, flags); + if (length) { + final = (TCHAR*)zix_calloc(allocator, (size_t)length + 1U, sizeof(TCHAR)); + if (final) { + GetFinalPathNameByHandle(h, final, length + 1U, flags); + } } } CloseHandle(h); - return final; + arg_path_free(allocator, wpath); + return path_result(allocator, final); #else // Fall back to "full path iff it exists" for older Windows TCHAR* full = NULL; - if (GetFileAttributes(path) != INVALID_FILE_ATTRIBUTES) { - const DWORD length = GetFullPathName(path, 0U, NULL, NULL); + if (GetFileAttributes(wpath) != INVALID_FILE_ATTRIBUTES) { + const DWORD length = GetFullPathName(wpath, 0U, NULL, NULL); if (length) { full = (TCHAR*)zix_calloc(allocator, (size_t)length + 1U, sizeof(TCHAR)); if (full) { - GetFullPathName(path, length + 1U, full, NULL); + GetFullPathName(wpath, length + 1U, full, NULL); } } } - return full; + arg_path_free(allocator, wpath); + return path_result(allocator, full); #endif } @@ -258,7 +328,7 @@ attrs_file_type(const DWORD attrs) ZixFileType zix_file_type(const char* const path) { - const ZixFileType type = attrs_file_type(GetFileAttributes(path)); + const ZixFileType type = zix_symlink_type(path); if (type != ZIX_FILE_TYPE_SYMLINK) { return type; } @@ -267,20 +337,33 @@ zix_file_type(const char* const path) char buf[MAX_PATH]; ZixBumpAllocator allocator = zix_bump_allocator(sizeof(buf), buf); char* const canonical = zix_canonical_path(&allocator.base, path); - return zix_file_type(canonical); + return zix_symlink_type(canonical); } ZixFileType zix_symlink_type(const char* const path) { - return attrs_file_type(GetFileAttributes(path)); + ArgPathChar* const wpath = arg_path_new(NULL, path); + if (!wpath) { + return ZIX_FILE_TYPE_NONE; + } + + const ZixFileType type = attrs_file_type(GetFileAttributes(wpath)); + arg_path_free(NULL, wpath); + return type; } ZixStatus zix_create_directory(const char* const dir_path) { - return !dir_path[0] ? ZIX_STATUS_BAD_ARG - : zix_windows_status(CreateDirectory(dir_path, NULL)); + if (!dir_path[0]) { + return ZIX_STATUS_BAD_ARG; + } + + ArgPathChar* const wpath = arg_path_new(NULL, dir_path); + const ZixStatus st = zix_windows_status(CreateDirectory(wpath, NULL)); + arg_path_free(NULL, wpath); + return st; } ZixStatus @@ -298,7 +381,15 @@ zix_create_symlink(const char* const target_path, const char* const link_path) #if USE_CREATESYMBOLICLINK static const DWORD flags = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE; - return zix_windows_status(CreateSymbolicLink(link_path, target_path, flags)); + ArgPathChar* const wtarget = arg_path_new(NULL, target_path); + ArgPathChar* const wlink = arg_path_new(NULL, link_path); + + const BOOL success = CreateSymbolicLink(wlink, wtarget, flags); + + arg_path_free(NULL, wlink); + arg_path_free(NULL, wtarget); + return zix_windows_status(success); + #else (void)target_path; (void)link_path; @@ -314,7 +405,15 @@ zix_create_directory_symlink(const char* const target_path, static const DWORD flags = SYMBOLIC_LINK_FLAG_DIRECTORY | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE; - return zix_windows_status(CreateSymbolicLink(link_path, target_path, flags)); + ArgPathChar* const wtarget = arg_path_new(NULL, target_path); + ArgPathChar* const wlink = arg_path_new(NULL, link_path); + + const BOOL success = CreateSymbolicLink(wlink, wtarget, flags); + + arg_path_free(NULL, wlink); + arg_path_free(NULL, wtarget); + return zix_windows_status(success); + #else (void)target_path; (void)link_path; @@ -326,7 +425,15 @@ ZixStatus zix_create_hard_link(const char* const target_path, const char* const link_path) { #if USE_CREATEHARDLINK - return zix_windows_status(CreateHardLink(link_path, target_path, NULL)); + ArgPathChar* const wtarget = arg_path_new(NULL, target_path); + ArgPathChar* const wlink = arg_path_new(NULL, link_path); + + const BOOL success = CreateHardLink(wlink, wtarget, NULL); + + arg_path_free(NULL, wlink); + arg_path_free(NULL, wtarget); + return zix_windows_status(success); + #else (void)target_path; (void)link_path; @@ -337,25 +444,25 @@ zix_create_hard_link(const char* const target_path, const char* const link_path) char* zix_temp_directory_path(ZixAllocator* const allocator) { - const DWORD size = GetTempPath(0U, NULL); - char* const buf = (char*)zix_calloc(allocator, size, 1); + const DWORD size = GetTempPath(0U, NULL); + TCHAR* const buf = (TCHAR*)zix_calloc(allocator, size, sizeof(TCHAR)); if (buf && (GetTempPath(size, buf) != size - 1U)) { zix_free(allocator, buf); return NULL; } - return buf; + return path_result(allocator, buf); } char* zix_current_path(ZixAllocator* const allocator) { - const DWORD size = GetCurrentDirectory(0U, NULL); - char* const buf = (char*)zix_calloc(allocator, size, 1); + const DWORD size = GetCurrentDirectory(0U, NULL); + TCHAR* const buf = (TCHAR*)zix_calloc(allocator, size, sizeof(TCHAR)); if (buf && (GetCurrentDirectory(size, buf) != size - 1U)) { zix_free(allocator, buf); return NULL; } - return buf; + return path_result(allocator, buf); } diff --git a/src/win32/win32_util.c b/src/win32/win32_util.c new file mode 100644 index 0000000..959867c --- /dev/null +++ b/src/win32/win32_util.c @@ -0,0 +1,73 @@ +// Copyright 2019-2024 David Robillard <d@drobilla.net> +// SPDX-License-Identifier: ISC + +#include "win32_util.h" + +#include <limits.h> +#include <windows.h> + +#ifdef UNICODE + +ArgPathChar* +arg_path_new(ZixAllocator* const allocator, const char* const path) +{ + return zix_utf8_to_wchar(allocator, path); +} + +void +arg_path_free(ZixAllocator* const allocator, ArgPathChar* const path) +{ + zix_free(allocator, path); +} + +#else // !defined(UNICODE) + +ArgPathChar* +arg_path_new(ZixAllocator* const allocator, const char* const path) +{ + (void)allocator; + return path; +} + +void +arg_path_free(ZixAllocator* const allocator, ArgPathChar* const path) +{ + (void)allocator; + (void)path; +} + +#endif + +wchar_t* +zix_utf8_to_wchar(ZixAllocator* const allocator, const char* const utf8) +{ + const int rc = utf8 ? MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0) : 0; + if (rc <= 0 || rc == INT_MAX) { + return NULL; + } + + wchar_t* const result = + (wchar_t*)zix_calloc(allocator, (size_t)rc, sizeof(wchar_t)); + if (result) { + MultiByteToWideChar(CP_UTF8, 0, utf8, -1, result, rc); + } + + return result; +} + +char* +zix_wchar_to_utf8(ZixAllocator* const allocator, const wchar_t* const wstr) +{ + const int rc = + wstr ? WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL) : 0; + if (rc <= 0 || rc == INT_MAX) { + return NULL; + } + + char* const result = (char*)zix_calloc(allocator, (size_t)rc, sizeof(char)); + if (result) { + WideCharToMultiByte(CP_UTF8, 0, wstr, -1, result, rc, NULL, NULL); + } + + return result; +} diff --git a/src/win32/win32_util.h b/src/win32/win32_util.h new file mode 100644 index 0000000..3123ef4 --- /dev/null +++ b/src/win32/win32_util.h @@ -0,0 +1,31 @@ +// Copyright 2019-2024 David Robillard <d@drobilla.net> +// SPDX-License-Identifier: ISC + +#ifndef ZIX_WIN32_UTIL_H +#define ZIX_WIN32_UTIL_H + +#include <zix/allocator.h> + +#ifdef UNICODE +typedef wchar_t ArgPathChar; +#else +typedef const char ArgPathChar; +#endif + +/// Copy and convert a path argument if necessary +ArgPathChar* +arg_path_new(ZixAllocator* const allocator, const char* const path); + +/// Free a path from arg_path_new() if necessary +void +arg_path_free(ZixAllocator* const allocator, ArgPathChar* const path); + +/// Convert from (user) UTF-8 to (Windows) UTF-16 +wchar_t* +zix_utf8_to_wchar(ZixAllocator* allocator, const char* utf8); + +/// Convert from (Windows) UTF-16 to (user) UTF-8 +char* +zix_wchar_to_utf8(ZixAllocator* allocator, const wchar_t* wstr); + +#endif // ZIX_WIN32_UTIL_H |