diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/.clang-tidy | 1 | ||||
-rw-r--r-- | src/allocator.c | 16 | ||||
-rw-r--r-- | src/btree.c | 90 | ||||
-rw-r--r-- | src/bump_allocator.c | 21 | ||||
-rw-r--r-- | src/darwin/sem_darwin.c | 4 | ||||
-rw-r--r-- | src/digest.c | 2 | ||||
-rw-r--r-- | src/errno_status.c | 4 | ||||
-rw-r--r-- | src/errno_status.h | 10 | ||||
-rw-r--r-- | src/filesystem.c | 16 | ||||
-rw-r--r-- | src/hash.c | 9 | ||||
-rw-r--r-- | src/path.c | 35 | ||||
-rw-r--r-- | src/path_iter.h | 8 | ||||
-rw-r--r-- | src/posix/environment_posix.c | 134 | ||||
-rw-r--r-- | src/posix/filesystem_posix.c | 35 | ||||
-rw-r--r-- | src/posix/sem_posix.c | 7 | ||||
-rw-r--r-- | src/posix/thread_posix.c | 4 | ||||
-rw-r--r-- | src/ring.c | 15 | ||||
-rw-r--r-- | src/status.c | 2 | ||||
-rw-r--r-- | src/string_view.c | 26 | ||||
-rw-r--r-- | src/system.c | 2 | ||||
-rw-r--r-- | src/system.h | 2 | ||||
-rw-r--r-- | src/tree.c | 44 | ||||
-rw-r--r-- | src/tree_debug.h | 139 | ||||
-rw-r--r-- | src/win32/environment_win32.c | 37 | ||||
-rw-r--r-- | src/win32/filesystem_win32.c | 298 | ||||
-rw-r--r-- | src/win32/sem_win32.c | 4 | ||||
-rw-r--r-- | src/win32/thread_win32.c | 4 | ||||
-rw-r--r-- | src/win32/win32_util.c | 73 | ||||
-rw-r--r-- | src/win32/win32_util.h | 31 | ||||
-rw-r--r-- | src/zix_config.h | 68 |
30 files changed, 757 insertions, 384 deletions
diff --git a/src/.clang-tidy b/src/.clang-tidy index 5647a8f..b3b1960 100644 --- a/src/.clang-tidy +++ b/src/.clang-tidy @@ -4,6 +4,7 @@ Checks: > -*-magic-numbers, -bugprone-easily-swappable-parameters, + -bugprone-multi-level-implicit-pointer-conversion, -clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling, -llvm-header-guard, -misc-no-recursion, diff --git a/src/allocator.c b/src/allocator.c index 6c18804..b1453b4 100644 --- a/src/allocator.c +++ b/src/allocator.c @@ -1,30 +1,27 @@ // Copyright 2011-2021 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#include "zix/allocator.h" - -#include "zix/attributes.h" +#include <zix/allocator.h> #include "zix_config.h" +#include <zix/attributes.h> + #ifdef _WIN32 -# define WIN32_LEAN_AND_MEAN # include <malloc.h> # include <windows.h> #endif #include <stdlib.h> -ZIX_MALLOC_FUNC -static void* +ZIX_MALLOC_FUNC static void* zix_default_malloc(ZixAllocator* const allocator, const size_t size) { (void)allocator; return malloc(size); } -ZIX_MALLOC_FUNC -static void* +ZIX_MALLOC_FUNC static void* zix_default_calloc(ZixAllocator* const allocator, const size_t nmemb, const size_t size) @@ -49,8 +46,7 @@ zix_default_free(ZixAllocator* const allocator, void* const ptr) free(ptr); } -ZIX_MALLOC_FUNC -static void* +ZIX_MALLOC_FUNC static void* zix_default_aligned_alloc(ZixAllocator* const allocator, const size_t alignment, const size_t size) diff --git a/src/btree.c b/src/btree.c index 7970796..dabd406 100644 --- a/src/btree.c +++ b/src/btree.c @@ -1,7 +1,11 @@ -// Copyright 2011-2021 David Robillard <d@drobilla.net> +// Copyright 2011-2024 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#include "zix/btree.h" +#include <zix/btree.h> + +#include <zix/allocator.h> +#include <zix/attributes.h> +#include <zix/status.h> #include <assert.h> #include <stdint.h> @@ -10,9 +14,9 @@ // #define ZIX_BTREE_SORTED_CHECK 1 // Define ZixShort as an integer type half the size of a pointer -#if UINTPTR_MAX >= UINT32_MAX +#if UINTPTR_MAX > UINT32_MAX // 64-bit typedef uint32_t ZixShort; -#else +#else // 32-bit typedef uint16_t ZixShort; #endif @@ -78,8 +82,7 @@ zix_btree_node_new(ZixAllocator* const allocator, const bool leaf) return node; } -ZIX_PURE_FUNC -static ZixBTreeNode* +ZIX_PURE_FUNC static ZixBTreeNode* zix_btree_child(const ZixBTreeNode* const node, const unsigned i) { assert(!node->is_leaf); @@ -99,23 +102,15 @@ zix_btree_new(ZixAllocator* const allocator, assert(cmp); - ZixBTree* const t = (ZixBTree*)zix_aligned_alloc( - allocator, ZIX_BTREE_PAGE_SIZE, ZIX_BTREE_PAGE_SIZE); - - if (!t) { - return NULL; - } - - if (!(t->root = zix_btree_node_new(allocator, true))) { - zix_aligned_free(allocator, t); - return NULL; + ZixBTree* const t = (ZixBTree*)zix_malloc(allocator, sizeof(ZixBTree)); + if (t) { + t->allocator = allocator; + t->root = NULL; + t->cmp = cmp; + t->cmp_data = cmp_data; + t->size = 0U; } - t->allocator = allocator; - t->cmp = cmp; - t->cmp_data = cmp_data; - t->size = 0U; - return t; } @@ -154,20 +149,22 @@ zix_btree_free(ZixBTree* const t, if (t) { zix_btree_clear(t, destroy, destroy_user_data); zix_aligned_free(t->allocator, t->root); - zix_aligned_free(t->allocator, t); + zix_free(t->allocator, t); } } void -zix_btree_clear(ZixBTree* const t, - ZixBTreeDestroyFunc destroy, - const void* destroy_user_data) +zix_btree_clear(ZixBTree* const t, + const ZixBTreeDestroyFunc destroy, + const void* const destroy_user_data) { - zix_btree_free_children(t, t->root, destroy, destroy_user_data); + if (t->root) { + zix_btree_free_children(t, t->root, destroy, destroy_user_data); + zix_aligned_free(t->allocator, t->root); + t->root = NULL; + } - memset(t->root, 0U, sizeof(ZixBTreeNode)); - t->root->is_leaf = true; - t->size = 0U; + t->size = 0U; } size_t @@ -196,7 +193,7 @@ zix_btree_ainsert(void** const array, const unsigned i, void* const e) { - memmove(array + i + 1U, array + i, (n - i) * sizeof(e)); + memmove(array + i + 1U, array + i, ((size_t)n - i) * sizeof(e)); array[i] = e; } @@ -205,7 +202,7 @@ static void* zix_btree_aerase(void** const array, const unsigned n, const unsigned i) { void* const ret = array[i]; - memmove(array + i, array + i + 1U, (n - i) * sizeof(ret)); + memmove(array + i, array + i + 1U, ((size_t)n - i) * sizeof(ret)); return ret; } @@ -221,7 +218,7 @@ zix_btree_split_child(ZixAllocator* const allocator, assert(i < n->n_vals + 1U); assert(zix_btree_child(n, i) == lhs); - const ZixShort max_n_vals = zix_btree_max_vals(lhs); + const unsigned max_n_vals = zix_btree_max_vals(lhs); ZixBTreeNode* rhs = zix_btree_node_new(allocator, lhs->is_leaf); if (!rhs) { return NULL; @@ -247,7 +244,7 @@ zix_btree_split_child(ZixAllocator* const allocator, rhs->n_vals * sizeof(void*)); memcpy(rhs->data.inode.children, lhs->data.inode.children + lhs->n_vals + 1U, - (rhs->n_vals + 1U) * sizeof(ZixBTreeNode*)); + ((size_t)rhs->n_vals + 1U) * sizeof(ZixBTreeNode*)); // Move middle value up to parent zix_btree_ainsert( @@ -394,14 +391,14 @@ zix_btree_leaf_find(const ZixBTree* const t, t->cmp, t->cmp_data, n->data.leaf.vals, n->n_vals, e, equal); } -static inline bool +ZIX_PURE_FUNC static inline bool zix_btree_can_remove_from(const ZixBTreeNode* const n) { assert(n->n_vals >= zix_btree_min_vals(n)); return n->n_vals > zix_btree_min_vals(n); } -static inline bool +ZIX_PURE_FUNC static inline bool zix_btree_is_full(const ZixBTreeNode* const n) { assert(n->n_vals <= zix_btree_max_vals(n)); @@ -433,8 +430,13 @@ zix_btree_insert(ZixBTree* const t, void* const e) ZixStatus st = ZIX_STATUS_SUCCESS; - // Grow up if necessary to ensure the root is not full - if (zix_btree_is_full(t->root)) { + if (!t->root) { + // Empty tree, create a new leaf root + if (!(t->root = zix_btree_node_new(t->allocator, true))) { + return ZIX_STATUS_NO_MEM; + } + } else if (zix_btree_is_full(t->root)) { + // Grow up if necessary to ensure the root is not full if ((st = zix_btree_grow_up(t))) { return st; } @@ -490,8 +492,9 @@ zix_btree_insert(ZixBTree* const t, void* const e) static void zix_btree_iter_set_frame(ZixBTreeIter* const ti, ZixBTreeNode* const n, - const ZixShort i) + const unsigned i) { + assert(i <= UINT16_MAX); ti->nodes[ti->level] = n; ti->indexes[ti->level] = (uint16_t)i; } @@ -617,7 +620,7 @@ zix_btree_merge(ZixBTree* const t, ZixBTreeNode* const n, const unsigned i) rhs->n_vals * sizeof(void*)); memcpy(lhs->data.inode.children + lhs->n_vals, rhs->data.inode.children, - (rhs->n_vals + 1U) * sizeof(void*)); + ((size_t)rhs->n_vals + 1U) * sizeof(void*)); } lhs->n_vals += rhs->n_vals; @@ -677,6 +680,7 @@ zix_btree_fatten_child(ZixBTree* const t, ZixBTreeIter* const iter) assert(n); assert(!n->is_leaf); + assert(n->n_vals); ZixBTreeNode* const* const children = n->data.inode.children; if (i > 0U && zix_btree_can_remove_from(children[i - 1U])) { @@ -825,6 +829,9 @@ zix_btree_find(const ZixBTree* const t, ZixBTreeNode* n = t->root; *ti = zix_btree_end_iter; + if (!n) { + return ZIX_STATUS_NOT_FOUND; + } while (!n->is_leaf) { bool equal = false; @@ -866,6 +873,9 @@ zix_btree_lower_bound(const ZixBTree* const t, ZixBTreeNode* n = t->root; // Current node uint16_t found_level = 0U; // Lowest level a match was found at bool found = false; // True if a match was ever found + if (!n) { + return ZIX_STATUS_SUCCESS; + } // Search down until we reach a leaf while (!n->is_leaf) { @@ -959,7 +969,7 @@ zix_btree_end(const ZixBTree* const t) bool zix_btree_iter_equals(const ZixBTreeIter lhs, const ZixBTreeIter rhs) { - const size_t indexes_size = (lhs.level + 1U) * sizeof(uint16_t); + const size_t indexes_size = ((size_t)lhs.level + 1U) * sizeof(uint16_t); return (lhs.level == rhs.level) && (lhs.nodes[0U] == rhs.nodes[0U]) && (!lhs.nodes[0U] || !memcmp(lhs.indexes, rhs.indexes, indexes_size)); diff --git a/src/bump_allocator.c b/src/bump_allocator.c index 924a1c8..01c1652 100644 --- a/src/bump_allocator.c +++ b/src/bump_allocator.c @@ -1,10 +1,10 @@ // Copyright 2021 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#include "zix/bump_allocator.h" +#include <zix/bump_allocator.h> -#include "zix/allocator.h" -#include "zix/attributes.h" +#include <zix/allocator.h> +#include <zix/attributes.h> #include <assert.h> #include <stddef.h> @@ -13,8 +13,7 @@ static const size_t min_alignment = sizeof(uintmax_t); -ZIX_PURE_FUNC -static size_t +ZIX_PURE_FUNC static size_t round_up_multiple(const size_t number, const size_t factor) { assert(factor); // Factor must be non-zero @@ -23,8 +22,7 @@ round_up_multiple(const size_t number, const size_t factor) return (number + factor - 1U) & ~(factor - 1U); } -ZIX_MALLOC_FUNC -static void* +ZIX_MALLOC_FUNC static void* zix_bump_malloc(ZixAllocator* const allocator, const size_t size) { ZixBumpAllocator* const state = (ZixBumpAllocator*)allocator; @@ -46,8 +44,7 @@ zix_bump_malloc(ZixAllocator* const allocator, const size_t size) return (void*)((char*)state->buffer + state->last); } -ZIX_MALLOC_FUNC -static void* +ZIX_MALLOC_FUNC static void* zix_bump_calloc(ZixAllocator* const allocator, const size_t nmemb, const size_t size) @@ -91,8 +88,7 @@ zix_bump_free(ZixAllocator* const allocator, void* const ptr) } } -ZIX_MALLOC_FUNC -static void* +ZIX_MALLOC_FUNC static void* zix_bump_aligned_alloc(ZixAllocator* const allocator, const size_t alignment, const size_t size) @@ -138,8 +134,7 @@ zix_bump_aligned_free(ZixAllocator* const allocator, void* const ptr) zix_bump_free(allocator, ptr); } -ZIX_CONST_FUNC -ZixBumpAllocator +ZIX_CONST_FUNC ZixBumpAllocator zix_bump_allocator(const size_t capacity, void* buffer) { const size_t aligned_top = (uintptr_t)buffer % min_alignment; diff --git a/src/darwin/sem_darwin.c b/src/darwin/sem_darwin.c index 0ace564..dd5750b 100644 --- a/src/darwin/sem_darwin.c +++ b/src/darwin/sem_darwin.c @@ -1,9 +1,9 @@ // Copyright 2012-2022 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#include "zix/sem.h" +#include <zix/sem.h> -#include "zix/status.h" +#include <zix/status.h> #include <mach/mach.h> diff --git a/src/digest.c b/src/digest.c index bc65f90..c40b1cc 100644 --- a/src/digest.c +++ b/src/digest.c @@ -1,7 +1,7 @@ // Copyright 2012-2021 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#include "zix/digest.h" +#include <zix/digest.h> #include <assert.h> #include <stdint.h> diff --git a/src/errno_status.c b/src/errno_status.c index 887158f..ed09b24 100644 --- a/src/errno_status.c +++ b/src/errno_status.c @@ -3,7 +3,7 @@ #include "errno_status.h" -#include "zix/status.h" +#include <zix/status.h> #include <errno.h> #include <stddef.h> @@ -41,7 +41,7 @@ zix_errno_status(const int e) {0, ZIX_STATUS_ERROR}, // Fallback mapping }; - static const size_t n_mappings = sizeof(map) / sizeof(Mapping) - 1U; + static const size_t n_mappings = (sizeof(map) / sizeof(Mapping)) - 1U; // Find the index of the matching mapping (or leave it at the fallback entry) size_t m = 0; diff --git a/src/errno_status.h b/src/errno_status.h index 2f81e4e..969d0dc 100644 --- a/src/errno_status.h +++ b/src/errno_status.h @@ -4,17 +4,15 @@ #ifndef ZIX_ERRNO_STATUS_H #define ZIX_ERRNO_STATUS_H -#include "zix/attributes.h" -#include "zix/status.h" +#include <zix/attributes.h> +#include <zix/status.h> /// Return an errno value converted to a status code -ZIX_CONST_FUNC -ZixStatus +ZIX_CONST_FUNC ZixStatus zix_errno_status(int e); /// Return success if `r` is non-zero, or `errno` as a status code otherwise -ZIX_PURE_FUNC -ZixStatus +ZIX_PURE_FUNC ZixStatus zix_errno_status_if(int r); #endif // ZIX_ERRNO_STATUS_H diff --git a/src/filesystem.c b/src/filesystem.c index 95005ae..a142e32 100644 --- a/src/filesystem.c +++ b/src/filesystem.c @@ -1,13 +1,13 @@ // Copyright 2007-2022 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#include "zix/filesystem.h" +#include <zix/filesystem.h> #include "path_iter.h" #include "system.h" -#include "zix/allocator.h" -#include "zix/status.h" +#include <zix/allocator.h> +#include <zix/status.h> #ifdef _WIN32 # include <direct.h> @@ -18,6 +18,7 @@ #include <fcntl.h> #include <sys/stat.h> +#include <sys/types.h> #include <errno.h> #include <stdbool.h> @@ -49,17 +50,18 @@ zix_create_directories(ZixAllocator* const allocator, // Create each directory down the path while (p.state != ZIX_PATH_END) { - const char old_end = path[p.range.end]; + char* const end = &path[p.range.end]; + const char old_last = *end; - path[p.range.end] = '\0'; + *end = '\0'; if (zix_file_type(path) != ZIX_FILE_TYPE_DIRECTORY) { if ((st = zix_create_directory(path))) { break; } } - path[p.range.end] = old_end; - p = zix_path_next(path, p); + *end = old_last; + p = zix_path_next(path, p); } zix_free(allocator, path); @@ -1,7 +1,10 @@ // Copyright 2011-2021 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#include "zix/hash.h" +#include <zix/hash.h> + +#include <zix/allocator.h> +#include <zix/status.h> #include <assert.h> #include <stdbool.h> @@ -175,7 +178,7 @@ rehash(ZixHash* const hash, const size_t old_n_entries) // Reinsert every element into the new array for (size_t i = 0U; i < old_n_entries; ++i) { - ZixHashEntry* const entry = &old_entries[i]; + const ZixHashEntry* const entry = &old_entries[i]; if (entry->value) { assert(hash->mask == hash->n_entries - 1U); @@ -327,7 +330,7 @@ zix_hash_insert_at(ZixHash* const hash, entry->value = record; // Update size and rehash if we exceeded the maximum load - const size_t max_load = hash->n_entries / 2U + hash->n_entries / 8U; + const size_t max_load = (hash->n_entries / 2U) + (hash->n_entries / 8U); const size_t new_count = hash->count + 1U; if (new_count >= max_load) { const ZixStatus st = grow(hash); @@ -1,12 +1,14 @@ -// Copyright 2007-2022 David Robillard <d@drobilla.net> +// Copyright 2007-2024 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#include "zix/path.h" +#include <zix/path.h> #include "index_range.h" #include "path_iter.h" -#include "zix/string_view.h" +#include <zix/allocator.h> +#include <zix/attributes.h> +#include <zix/string_view.h> #include <stdbool.h> #include <stddef.h> @@ -92,8 +94,7 @@ typedef struct { ZixIndexRange dir; } ZixRootSlices; -ZIX_PURE_FUNC -static ZixRootSlices +ZIX_PURE_FUNC static ZixRootSlices zix_path_root_slices(const char* const path) { // A root name not trailed by a separator has no root directory @@ -128,8 +129,7 @@ zix_string_ranges_equal(const char* const lhs, !strncmp(lhs + lhs_range.begin, rhs + rhs_range.begin, lhs_len)); } -ZIX_PURE_FUNC -static ZixIndexRange +ZIX_PURE_FUNC static ZixIndexRange zix_path_root_path_range(const char* const path) { const ZixRootSlices root = zix_path_root_slices(path); @@ -140,8 +140,7 @@ zix_path_root_path_range(const char* const path) : zix_make_range(root.name.begin, root.name.end + dir_len); } -ZIX_PURE_FUNC -static ZixIndexRange +ZIX_PURE_FUNC static ZixIndexRange zix_path_parent_path_range(const ZixStringView path) { if (!path.length) { @@ -178,8 +177,7 @@ zix_path_parent_path_range(const ZixStringView path) return zix_make_range(root.begin, root.begin + l + 1U - p); } -ZIX_PURE_FUNC -static ZixIndexRange +ZIX_PURE_FUNC static ZixIndexRange zix_path_filename_range(const ZixStringView path) { if (!path.length) { @@ -201,8 +199,7 @@ zix_path_filename_range(const ZixStringView path) return zix_make_range(f, path.length); } -ZIX_PURE_FUNC -static ZixIndexRange +ZIX_PURE_FUNC static ZixIndexRange zix_path_stem_range(const ZixStringView path) { const ZixIndexRange name = zix_path_filename_range(path); @@ -221,8 +218,7 @@ zix_path_stem_range(const ZixStringView path) return zix_is_empty_range(stem) ? name : stem; } -ZIX_PURE_FUNC -static ZixIndexRange +ZIX_PURE_FUNC static ZixIndexRange zix_path_extension_range(const ZixStringView path) { const ZixIndexRange stem = zix_path_stem_range(path); @@ -356,10 +352,9 @@ zix_path_lexically_normal(ZixAllocator* const allocator, const char* const path) size_t last = r; size_t next = 0; for (size_t i = root_len; i < r;) { - if (last < r && i > 2 && i + 1 <= r && result[i - 2] == sep && - result[i - 1] == '.' && result[i] == '.' && - (!result[i + 1] || is_dir_sep(result[i + 1]))) { - if (i < r && result[i + 1] == sep) { + if (last < r && i > 2U && result[i - 2U] == sep && result[i - 1U] == '.' && + result[i] == '.' && (!result[i + 1U] || is_dir_sep(result[i + 1U]))) { + if (result[i + 1] == sep) { ++i; } @@ -702,7 +697,7 @@ zix_path_is_absolute(const char* const path) { #ifdef _WIN32 const ZixRootSlices root = zix_path_root_slices(path); - return (!zix_is_empty_range(root.name) && + return (path && !zix_is_empty_range(root.name) && (!zix_is_empty_range(root.dir) || (is_dir_sep(path[0]) && is_dir_sep(path[1])))); diff --git a/src/path_iter.h b/src/path_iter.h index b3b23e8..43eb8c9 100644 --- a/src/path_iter.h +++ b/src/path_iter.h @@ -6,7 +6,7 @@ #include "index_range.h" -#include "zix/attributes.h" +#include <zix/attributes.h> typedef enum { ZIX_PATH_ROOT_NAME, @@ -20,12 +20,10 @@ typedef struct { ZixPathIterState state; } ZixPathIter; -ZIX_PURE_FUNC -ZixPathIter +ZIX_PURE_FUNC ZixPathIter zix_path_begin(const char* ZIX_NULLABLE path); -ZIX_PURE_FUNC -ZixPathIter +ZIX_PURE_FUNC ZixPathIter zix_path_next(const char* ZIX_NONNULL path, ZixPathIter iter); #endif // ZIX_PATH_ITER_H diff --git a/src/posix/environment_posix.c b/src/posix/environment_posix.c new file mode 100644 index 0000000..8447753 --- /dev/null +++ b/src/posix/environment_posix.c @@ -0,0 +1,134 @@ +// Copyright 2012-2024 David Robillard <d@drobilla.net> +// SPDX-License-Identifier: ISC + +#include <zix/environment.h> + +#include <zix/allocator.h> + +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> + +static bool +is_path_delim(const char c) +{ + return c == '/' || c == ':' || c == '\0'; +} + +static bool +is_var_name_char(const char c) +{ + return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c == '_'); +} + +// Append suffix to dst, update dst_len, and return the realloc'd result +static char* +append_str(ZixAllocator* const allocator, + size_t* const dst_len, + char* const dst, + const size_t suffix_len, + const char* const suffix) +{ + const size_t out_len = *dst_len + suffix_len; + char* const out = (char*)zix_realloc(allocator, dst, out_len + 1U); + if (!out) { + zix_free(allocator, dst); + return NULL; + } + + memcpy(out + *dst_len, suffix, suffix_len); + out[(*dst_len += suffix_len)] = '\0'; + return out; +} + +// Append the value of an environment variable to dst if it's set +static char* +append_var(ZixAllocator* const allocator, + size_t* const dst_len, + char* const dst, + const size_t ref_len, + const char* const ref) +{ + // Get value from environment + const char* const val = getenv(ref + 1U); // NOLINT(concurrency-mt-unsafe) + if (val) { + return append_str(allocator, dst_len, dst, strlen(val), val); + } + + // No value found, append variable reference as-is + return append_str(allocator, dst_len, dst, ref_len, ref); +} + +// Copy a variable reference string like $NAME to a null-terminated string +static char* +set_ref(ZixAllocator* const allocator, + char** const buffer, + const size_t ref_len, + const char* const ref) +{ + char* const out = (char*)zix_realloc(allocator, *buffer, ref_len + 1U); + + if (out) { + memcpy(out, ref, ref_len); + out[ref_len] = '\0'; + } else { + zix_free(allocator, *buffer); + } + + return out; +} + +char* +zix_expand_environment_strings(ZixAllocator* const allocator, + const char* const string) +{ + char* ref = NULL; // Reference string like $NAME + char* out = NULL; // Expanded result + size_t len = 0U; // Length of expanded result + + size_t start = 0U; // Start of current chunk to copy + for (size_t s = 0U; string[s];) { + const char c = string[s]; + if (c == '$' && is_var_name_char(string[s + 1U])) { + // Hit $ (variable reference like $VAR_NAME) + for (size_t t = 1U;; ++t) { + if (!is_var_name_char(string[s + t])) { + const char* const prefix = string + start; + const size_t prefix_len = s - start; + if ((prefix_len && + !(out = append_str(allocator, &len, out, prefix_len, prefix))) || + !(ref = set_ref(allocator, &ref, t, string + s)) || + !(out = append_var(allocator, &len, out, t, ref))) { + zix_free(allocator, ref); + zix_free(allocator, out); + return NULL; + } + start = s = t; + break; + } + } + } else if (c == '~' && is_path_delim(string[s + 1U])) { + // Hit ~ before delimiter or end of string (home directory reference) + const char* const prefix = string + start; + const size_t prefix_len = s - start; + if ((prefix_len && + !(out = append_str(allocator, &len, out, prefix_len, prefix))) || + !(out = append_var(allocator, &len, out, 5U, "$HOME"))) { + zix_free(allocator, ref); + return NULL; + } + start = ++s; + } else { + ++s; + } + } + + if (string[start]) { + const char* const tail = string + start; + const size_t tail_len = strlen(tail); + out = append_str(allocator, &len, out, tail_len, tail); + } + + zix_free(allocator, ref); + return out; +} diff --git a/src/posix/filesystem_posix.c b/src/posix/filesystem_posix.c index c82cdf7..ea091e0 100644 --- a/src/posix/filesystem_posix.c +++ b/src/posix/filesystem_posix.c @@ -1,15 +1,15 @@ // Copyright 2007-2022 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#include "zix/filesystem.h" +#include <zix/filesystem.h> #include "../errno_status.h" #include "../system.h" #include "../zix_config.h" -#include "zix/allocator.h" -#include "zix/attributes.h" -#include "zix/status.h" +#include <zix/allocator.h> +#include <zix/attributes.h> +#include <zix/status.h> #if USE_FLOCK && USE_FILENO # include <sys/file.h> @@ -90,7 +90,7 @@ copy_path(ZixAllocator* const allocator, #if !defined(PATH_MAX) && USE_PATHCONF static size_t -max_path_size(void) +max_path_size(const char* const path) { const long path_max = pathconf(path, _PC_PATH_MAX); return (path_max > 0) ? (size_t)path_max : zix_system_page_size(); @@ -99,8 +99,10 @@ max_path_size(void) #elif !defined(PATH_MAX) static size_t -max_path_size(void) +max_path_size(const char* const path) { + (void)path; + return zix_system_page_size(); } @@ -298,11 +300,9 @@ zix_remove(const char* const path) } void -zix_dir_for_each(const char* const path, - void* const data, - void (*const f)(const char* path, - const char* name, - void* data)) +zix_dir_for_each(const char* const path, + void* const data, + const ZixDirEntryVisitFunc f) { DIR* dir = opendir(path); if (dir) { @@ -341,6 +341,9 @@ zix_canonical_path(ZixAllocator* const allocator, const char* const path) } zix_free(allocator, buffer); + +#else + (void)allocator; #endif return NULL; @@ -349,7 +352,7 @@ zix_canonical_path(ZixAllocator* const allocator, const char* const path) ZixStatus zix_file_lock(FILE* const file, const ZixFileLockMode mode) { -#if !defined(__EMSCRIPTEN__) && USE_FLOCK && USE_FILENO +#if USE_FLOCK && USE_FILENO return zix_posix_status( flock(fileno(file), (mode == ZIX_FILE_LOCK_BLOCK) ? LOCK_EX : (LOCK_EX | LOCK_NB))); @@ -364,7 +367,7 @@ zix_file_lock(FILE* const file, const ZixFileLockMode mode) ZixStatus zix_file_unlock(FILE* const file, const ZixFileLockMode mode) { -#if !defined(__EMSCRIPTEN__) && USE_FLOCK && USE_FILENO +#if USE_FLOCK && USE_FILENO return zix_posix_status( flock(fileno(file), (mode == ZIX_FILE_LOCK_BLOCK) ? LOCK_UN : (LOCK_UN | LOCK_NB))); @@ -376,8 +379,7 @@ zix_file_unlock(FILE* const file, const ZixFileLockMode mode) #endif } -ZIX_CONST_FUNC -static ZixFileType +ZIX_CONST_FUNC static ZixFileType stat_file_type(const struct stat* sb) { typedef struct { @@ -440,7 +442,7 @@ zix_current_path(ZixAllocator* const allocator) #elif USE_PATHCONF // Others don't so we have to query PATH_MAX at runtime to allocate the result - const size_t size = max_path_size(); + const size_t size = max_path_size("."); char* const buffer = (char*)zix_calloc(allocator, size, 1); char* const current = getcwd(buffer, size); if (!current) { @@ -450,6 +452,7 @@ zix_current_path(ZixAllocator* const allocator) return current; #else + (void)allocator; return NULL; #endif diff --git a/src/posix/sem_posix.c b/src/posix/sem_posix.c index 452830d..5b14e37 100644 --- a/src/posix/sem_posix.c +++ b/src/posix/sem_posix.c @@ -1,12 +1,12 @@ // Copyright 2012-2022 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#include "zix/sem.h" +#include <zix/sem.h> #include "../errno_status.h" #include "../zix_config.h" -#include "zix/status.h" +#include <zix/status.h> #include <semaphore.h> @@ -61,6 +61,9 @@ zix_sem_timed_wait(ZixSem* sem, { #if !USE_CLOCK_GETTIME || !USE_SEM_TIMEDWAIT + (void)sem; + (void)seconds; + (void)nanoseconds; return ZIX_STATUS_NOT_SUPPORTED; #else diff --git a/src/posix/thread_posix.c b/src/posix/thread_posix.c index 2073448..468fe38 100644 --- a/src/posix/thread_posix.c +++ b/src/posix/thread_posix.c @@ -1,11 +1,11 @@ // Copyright 2012-2020 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#include "zix/thread.h" +#include <zix/thread.h> #include "../errno_status.h" -#include "zix/status.h" +#include <zix/status.h> #include <pthread.h> @@ -1,15 +1,15 @@ -// Copyright 2011-2022 David Robillard <d@drobilla.net> +// Copyright 2011-2024 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#include "zix/ring.h" +#include <zix/ring.h> #include "errno_status.h" #include "zix_config.h" -#include "zix/allocator.h" -#include "zix/status.h" +#include <zix/allocator.h> +#include <zix/status.h> -#if defined(_WIN32) +#if USE_VIRTUALLOCK # include <windows.h> #elif USE_MLOCK # include <sys/mman.h> @@ -108,7 +108,7 @@ zix_ring_free(ZixRing* const ring) ZixStatus zix_ring_mlock(ZixRing* const ring) { -#if defined(_WIN32) +#if USE_VIRTUALLOCK return (VirtualLock(ring, sizeof(ZixRing)) && VirtualLock(ring->buf, ring->size)) ? ZIX_STATUS_SUCCESS @@ -119,6 +119,7 @@ zix_ring_mlock(ZixRing* const ring) mlock(ring->buf, ring->size)); #else + (void)ring; return ZIX_STATUS_NOT_SUPPORTED; #endif } @@ -190,7 +191,7 @@ peek_internal(const ZixRing* const ring, } else { const uint32_t first_size = ring->size - r; memcpy(dst, &ring->buf[r], first_size); - memcpy((char*)dst + first_size, &ring->buf[0], size - first_size); + memcpy((char*)dst + first_size, &ring->buf[0], (size_t)size - first_size); } return size; diff --git a/src/status.c b/src/status.c index e3d8b99..52057b8 100644 --- a/src/status.c +++ b/src/status.c @@ -1,7 +1,7 @@ // Copyright 2014-2022 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#include "zix/status.h" +#include <zix/status.h> const char* zix_strerror(const ZixStatus status) diff --git a/src/string_view.c b/src/string_view.c index 9d98258..b02e537 100644 --- a/src/string_view.c +++ b/src/string_view.c @@ -1,9 +1,11 @@ -// Copyright 2007-2022 David Robillard <d@drobilla.net> +// Copyright 2007-2024 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#include "zix/string_view.h" -#include "zix/allocator.h" +#include <zix/string_view.h> +#include <zix/allocator.h> + +#include <stdbool.h> #include <string.h> char* @@ -16,3 +18,21 @@ zix_string_view_copy(ZixAllocator* const allocator, const ZixStringView view) } return copy; } + +bool +zix_string_view_equals(const ZixStringView lhs, const ZixStringView rhs) +{ + if (lhs.length != rhs.length) { + return false; + } + + if (lhs.data != rhs.data) { + for (size_t i = 0U; i < lhs.length; ++i) { + if (lhs.data[i] != rhs.data[i]) { + return false; + } + } + } + + return true; +} diff --git a/src/system.c b/src/system.c index eab568b..f672d76 100644 --- a/src/system.c +++ b/src/system.c @@ -5,7 +5,7 @@ #include "errno_status.h" -#include "zix/status.h" +#include <zix/status.h> #ifdef _WIN32 # include <io.h> diff --git a/src/system.h b/src/system.h index ca55161..45a0490 100644 --- a/src/system.h +++ b/src/system.h @@ -4,7 +4,7 @@ #ifndef ZIX_SYSTEM_H #define ZIX_SYSTEM_H -#include "zix/status.h" +#include <zix/status.h> #include <stdint.h> #include <sys/types.h> @@ -1,9 +1,11 @@ // Copyright 2011-2020 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#include "zix/tree.h" +#include <zix/tree.h> -#include "zix/status.h" +#include <zix/allocator.h> +#include <zix/attributes.h> +#include <zix/status.h> #include <assert.h> @@ -31,13 +33,12 @@ struct ZixTreeNodeImpl { #define MIN(a, b) (((a) < (b)) ? (a) : (b)) #define MAX(a, b) (((a) > (b)) ? (a) : (b)) -// Uncomment these for debugging features -// #define ZIX_TREE_VERIFY 1 -// #define ZIX_TREE_HYPER_VERIFY 1 - -#if defined(ZIX_TREE_VERIFY) || defined(ZIX_TREE_HYPER_VERIFY) -# include "tree_debug.h" -# define ASSERT_BALANCE(n) assert(verify_balance(n)) +#ifndef NDEBUG +# define ASSERT_BALANCE(n) \ + assert(!(((n)->balance < -2 || (n)->balance > 2) || \ + ((n)->balance < 0 && !(n)->left) || \ + ((n)->balance > 0 && !(n)->right) || \ + ((n)->balance != 0 && !(n)->left && !(n)->right))) #else # define ASSERT_BALANCE(n) #endif @@ -282,10 +283,6 @@ rotate_right_left(ZixTreeNode* p, int* height_change) static ZixTreeNode* zix_tree_rebalance(ZixTree* t, ZixTreeNode* node, int* height_change) { -#ifdef ZIX_TREE_HYPER_VERIFY - const size_t old_height = height(node); -#endif - *height_change = 0; const bool is_root = !node->parent; @@ -294,6 +291,7 @@ zix_tree_rebalance(ZixTree* t, ZixTreeNode* node, int* height_change) ZixTreeNode* replacement = node; if (node->balance == -2) { assert(node->left); + // NOLINTNEXTLINE(clang-analyzer-core.NullDereference) if (node->left->balance == 1) { replacement = rotate_left_right(node, height_change); } else { @@ -301,6 +299,7 @@ zix_tree_rebalance(ZixTree* t, ZixTreeNode* node, int* height_change) } } else if (node->balance == 2) { assert(node->right); + // NOLINTNEXTLINE(clang-analyzer-core.NullDereference) if (node->right->balance == -1) { replacement = rotate_right_left(node, height_change); } else { @@ -312,10 +311,6 @@ zix_tree_rebalance(ZixTree* t, ZixTreeNode* node, int* height_change) t->root = replacement; } -#ifdef ZIX_TREE_HYPER_VERIFY - assert(old_height + *height_change == height(replacement)); -#endif - return replacement; } @@ -395,12 +390,6 @@ zix_tree_insert(ZixTree* t, void* e, ZixTreeIter** ti) ++t->size; -#ifdef ZIX_TREE_VERIFY - if (!verify(t, t->root)) { - return ZIX_STATUS_ERROR; - } -#endif - return ZIX_STATUS_SUCCESS; } @@ -507,6 +496,8 @@ zix_tree_remove(ZixTree* t, ZixTreeIter* ti) t->root = replace; } + assert(n->left); + replace->parent = n->parent; replace->left = n->left; n->left->parent = replace; @@ -546,13 +537,6 @@ zix_tree_remove(ZixTree* t, ZixTreeIter* ti) zix_free(t->allocator, n); --t->size; - -#ifdef ZIX_TREE_VERIFY - if (!verify(t, t->root)) { - return ZIX_STATUS_ERROR; - } -#endif - return ZIX_STATUS_SUCCESS; } diff --git a/src/tree_debug.h b/src/tree_debug.h deleted file mode 100644 index b1a2d2e..0000000 --- a/src/tree_debug.h +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright 2011-2020 David Robillard <d@drobilla.net> -// SPDX-License-Identifier: ISC - -#ifndef ZIX_TREE_DEBUG_H -#define ZIX_TREE_DEBUG_H - -#include <inttypes.h> - -#ifdef ZIX_TREE_HYPER_VERIFY -static size_t -height(ZixTreeNode* n) -{ - if (!n) { - return 0; - } else { - return 1 + MAX(height(n->left), height(n->right)); - } -} -#endif - -#if defined(ZIX_TREE_VERIFY) || !defined(NDEBUG) -static bool -verify_balance(ZixTreeNode* n) -{ - if (!n) { - return true; - } - - if (n->balance < -2 || n->balance > 2) { - fprintf(stderr, - "Balance out of range : %ld (balance %d)\n", - (intptr_t)n->data, - n->balance); - return false; - } - - if (n->balance < 0 && !n->left) { - fprintf(stderr, - "Bad balance : %ld (balance %d) has no left child\n", - (intptr_t)n->data, - n->balance); - return false; - } - - if (n->balance > 0 && !n->right) { - fprintf(stderr, - "Bad balance : %ld (balance %d) has no right child\n", - (intptr_t)n->data, - n->balance); - return false; - } - - if (n->balance != 0 && !n->left && !n->right) { - fprintf(stderr, - "Bad balance : %ld (balance %d) has no children\n", - (intptr_t)n->data, - n->balance); - return false; - } - -# ifdef ZIX_TREE_HYPER_VERIFY - const intptr_t left_height = (intptr_t)height(n->left); - const intptr_t right_height = (intptr_t)height(n->right); - if (n->balance != right_height - left_height) { - fprintf(stderr, - "Bad balance at %ld: h_r (%" PRIdPTR ")" - "- l_h (%" PRIdPTR ") != %d\n", - (intptr_t)n->data, - right_height, - left_height, - n->balance); - assert(false); - return false; - } -# endif - - return true; -} -#endif - -#ifdef ZIX_TREE_VERIFY -static bool -verify(ZixTree* t, ZixTreeNode* n) -{ - if (!n) { - return true; - } - - if (n->parent) { - if ((n->parent->left != n) && (n->parent->right != n)) { - fprintf(stderr, "Corrupt child/parent pointers\n"); - return false; - } - } - - if (n->left) { - if (t->cmp(n->left->data, n->data, t->cmp_data) > 0) { - fprintf( - stderr, "Bad order: %" PRIdPTR " with left child\n", (intptr_t)n->data); - fprintf(stderr, "%p ? %p\n", (void*)n, (void*)n->left); - fprintf(stderr, - "%" PRIdPTR " ? %" PRIdPTR "\n", - (intptr_t)n->data, - (intptr_t)n->left->data); - return false; - } - - if (!verify(t, n->left)) { - return false; - } - } - - if (n->right) { - if (t->cmp(n->right->data, n->data, t->cmp_data) < 0) { - fprintf(stderr, - "Bad order: %" PRIdPTR " with right child\n", - (intptr_t)n->data); - return false; - } - - if (!verify(t, n->right)) { - return false; - } - } - - if (!verify_balance(n)) { - return false; - } - - if (n->balance <= -2 || n->balance >= 2) { - fprintf(stderr, "Imbalance: %p (balance %d)\n", (void*)n, n->balance); - return false; - } - - return true; -} -#endif - -#endif // ZIX_TREE_DEBUG_H diff --git a/src/win32/environment_win32.c b/src/win32/environment_win32.c new file mode 100644 index 0000000..ce5f00e --- /dev/null +++ b/src/win32/environment_win32.c @@ -0,0 +1,37 @@ +// Copyright 2012-2024 David Robillard <d@drobilla.net> +// SPDX-License-Identifier: ISC + +#include "win32_util.h" + +#include <zix/environment.h> + +#include <windows.h> + +char* +zix_expand_environment_strings(ZixAllocator* const allocator, + const char* const string) +{ + ArgPathChar* const wstring = arg_path_new(allocator, string); + + const DWORD size = ExpandEnvironmentStrings(wstring, NULL, 0U); + if (!size) { + arg_path_free(allocator, wstring); + return NULL; + } + + TCHAR* const out = + (TCHAR*)zix_calloc(allocator, (size_t)size + 1U, sizeof(TCHAR)); + if (out) { + 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 5dc36a8..fa40cde 100644 --- a/src/win32/filesystem_win32.c +++ b/src/win32/filesystem_win32.c @@ -1,21 +1,22 @@ -// Copyright 2007-2022 David Robillard <d@drobilla.net> +// Copyright 2007-2024 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#include "zix/bump_allocator.h" -#include "zix/filesystem.h" +#include <zix/filesystem.h> #include "../errno_status.h" #include "../zix_config.h" +#include "win32_util.h" -#include "zix/allocator.h" -#include "zix/path.h" -#include "zix/status.h" +#include <zix/allocator.h> +#include <zix/path.h> +#include <zix/status.h> #include <direct.h> #include <fcntl.h> #include <io.h> #include <limits.h> #include <sys/stat.h> +#include <tchar.h> #include <windows.h> #include <errno.h> @@ -25,6 +26,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 +96,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 @@ -111,7 +147,7 @@ zix_create_temporary_directory(ZixAllocator* const allocator, suffix[i] = chars[seed % n_chars]; } - if (!_mkdir(result)) { + if (!zix_create_directory(result)) { return result; } } @@ -123,22 +159,44 @@ zix_create_temporary_directory(ZixAllocator* const allocator, ZixStatus zix_remove(const char* const path) { - return ((zix_file_type(path) == ZIX_FILE_TYPE_DIRECTORY) - ? zix_windows_status(RemoveDirectory(path)) - : remove(path) ? zix_errno_status(errno) - : ZIX_STATUS_SUCCESS); + 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 -zix_dir_for_each(const char* const path, - void* const data, - void (*const f)(const char* path, - const char* name, - void* data)) +zix_dir_for_each(const char* const path, + void* const data, + const ZixDirEntryVisitFunc f) { + 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'; @@ -147,8 +205,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)); } @@ -158,6 +222,12 @@ zix_dir_for_each(const char* const path, ZixStatus zix_file_lock(FILE* const file, const ZixFileLockMode mode) { +#ifdef __clang__ + (void)file; + (void)mode; + return ZIX_STATUS_NOT_SUPPORTED; +#else + HANDLE handle = (HANDLE)_get_osfhandle(fileno(file)); OVERLAPPED overlapped = {0}; @@ -167,58 +237,112 @@ zix_file_lock(FILE* const file, const ZixFileLockMode mode) return zix_windows_status( LockFileEx(handle, flags, 0, UINT32_MAX, UINT32_MAX, &overlapped)); +#endif } ZixStatus zix_file_unlock(FILE* const file, const ZixFileLockMode mode) { (void)mode; +#ifdef __clang__ + (void)file; + return ZIX_STATUS_NOT_SUPPORTED; +#else HANDLE handle = (HANDLE)_get_osfhandle(fileno(file)); OVERLAPPED overlapped = {0}; return zix_windows_status( UnlockFileEx(handle, 0, UINT32_MAX, UINT32_MAX, &overlapped)); +#endif } +#if USE_GETFINALPATHNAMEBYHANDLE && USE_CREATEFILE2 + +static HANDLE +open_attribute_handle(const char* const path) +{ + wchar_t* const wpath = zix_utf8_to_wchar(NULL, path); + + CREATEFILE2_EXTENDED_PARAMETERS params = { + sizeof(CREATEFILE2_EXTENDED_PARAMETERS), + 0U, + FILE_FLAG_BACKUP_SEMANTICS, + 0U, + NULL, + NULL}; + + const HANDLE handle = + CreateFile2(wpath, FILE_READ_ATTRIBUTES, 0U, OPEN_EXISTING, ¶ms); + + zix_free(NULL, wpath); + return handle; +} + +#elif USE_GETFINALPATHNAMEBYHANDLE + +static HANDLE +open_attribute_handle(const char* const path) +{ + ArgPathChar* const wpath = arg_path_new(NULL, path); + + const HANDLE handle = CreateFile(wpath, + FILE_READ_ATTRIBUTES, + 0U, + NULL, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, + NULL); + + arg_path_free(NULL, wpath); + return handle; +} + +#endif + char* zix_canonical_path(ZixAllocator* const allocator, const char* const path) { - char full[MAX_PATH] = {0}; - if (!path || !GetFullPathName(path, MAX_PATH, full, NULL)) { + if (!path) { return NULL; } - const HANDLE h = - CreateFile(full, - FILE_READ_ATTRIBUTES, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - NULL, - OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS, - NULL); - - const DWORD flags = FILE_NAME_NORMALIZED | VOLUME_NAME_DOS; - const DWORD final_size = GetFinalPathNameByHandle(h, NULL, 0U, flags); - if (!final_size) { - CloseHandle(h); - return NULL; +#if USE_GETFINALPATHNAMEBYHANDLE // Vista+ + + const HANDLE h = open_attribute_handle(path); + 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); + } + } } - char* const final = (char*)zix_calloc(allocator, final_size + 1U, 1U); - if (!final || !GetFinalPathNameByHandle(h, final, final_size + 1U, flags)) { - zix_free(allocator, final); - CloseHandle(h); - return NULL; + CloseHandle(h); + return path_result(allocator, final); + +#else // Fall back to "full path iff it exists" for older Windows + + ArgPathChar* const wpath = arg_path_new(allocator, path); + TCHAR* full = 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(wpath, length + 1U, full, NULL); + } + } } - if (final_size > 4U && !strncmp(final, "\\\\?\\", 4)) { - memmove(final, final + 4U, final_size - 4U); - final[final_size - 4U] = '\0'; - } + arg_path_free(allocator, wpath); + return path_result(allocator, full); - CloseHandle(h); - return final; +#endif } static ZixFileType @@ -246,30 +370,44 @@ 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; } // Resolve symlink to find the canonical type - 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); + char* const canonical = zix_canonical_path(NULL, path); + const ZixFileType real_type = + canonical ? zix_symlink_type(canonical) : ZIX_FILE_TYPE_NONE; + + zix_free(NULL, canonical); + return real_type; } 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 - : _mkdir(dir_path) ? zix_errno_status(errno) - : ZIX_STATUS_SUCCESS; + 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 @@ -287,7 +425,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; @@ -303,7 +449,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; @@ -314,31 +468,45 @@ zix_create_directory_symlink(const char* const target_path, ZixStatus zix_create_hard_link(const char* const target_path, const char* const link_path) { - return zix_windows_status(CreateHardLink(link_path, target_path, NULL)); +#if USE_CREATEHARDLINK + 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; + return ZIX_STATUS_NOT_SUPPORTED; +#endif } 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/sem_win32.c b/src/win32/sem_win32.c index 979390e..d28f39f 100644 --- a/src/win32/sem_win32.c +++ b/src/win32/sem_win32.c @@ -1,9 +1,9 @@ // Copyright 2012-2022 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#include "zix/sem.h" +#include <zix/sem.h> -#include "zix/status.h" +#include <zix/status.h> #include <windows.h> diff --git a/src/win32/thread_win32.c b/src/win32/thread_win32.c index e4411b1..dbfe026 100644 --- a/src/win32/thread_win32.c +++ b/src/win32/thread_win32.c @@ -1,9 +1,9 @@ // Copyright 2012-2022 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#include "zix/thread.h" +#include <zix/thread.h> -#include "zix/status.h" +#include <zix/status.h> #include <windows.h> 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..3ae2e2b --- /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* allocator, const char* path); + +/// Free a path from arg_path_new() if necessary +void +arg_path_free(ZixAllocator* allocator, ArgPathChar* 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 diff --git a/src/zix_config.h b/src/zix_config.h index 0aab586..460b712 100644 --- a/src/zix_config.h +++ b/src/zix_config.h @@ -1,4 +1,4 @@ -// Copyright 2021-2023 David Robillard <d@drobilla.net> +// Copyright 2021-2024 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC /* @@ -55,6 +55,13 @@ # define ZIX_POSIX_VERSION 0 # endif +// Define ZIX_WINAPI_UWP to 1 (if this is a sandboxed UWP app) or 0 +# if defined(WINAPI_FAMILY) && WINAPI_FAMILY < 10 +# define ZIX_WINAPI_UWP 1 +# else +# define ZIX_WINAPI_UWP 0 +# endif + // POSIX.1-2001: clock_gettime() # ifndef HAVE_CLOCK_GETTIME # if ZIX_POSIX_VERSION >= 200112L @@ -78,9 +85,24 @@ # endif # endif -// Windows: CreateSymbolicLink() +// Windows 8 (Desktop, UWP): CreateFile2() +# ifndef HAVE_CREATEFILE2 +# if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0602 && !defined(__MINGW32__) +# define HAVE_CREATEFILE2 1 +# endif +# endif + +// Windows XP (Desktop): CreateHardLink() +# ifndef HAVE_CREATEHARDLINK +# if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0501 && !ZIX_WINAPI_UWP +# define HAVE_CREATEHARDLINK 1 +# endif +# endif + +// Windows Vista (Desktop): CreateSymbolicLink() # ifndef HAVE_CREATESYMBOLICLINK -# if defined(_MSC_VER) && _MSC_VER >= 1910 +# if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0600 && \ + !defined(__MINGW32__) && !ZIX_WINAPI_UWP # define HAVE_CREATESYMBOLICLINK 1 # endif # endif @@ -94,11 +116,18 @@ // Classic UNIX: flock() # ifndef HAVE_FLOCK -# if defined(__APPLE__) || defined(__unix__) +# if (defined(__APPLE__) || defined(__unix__)) && !defined(__EMSCRIPTEN__) # define HAVE_FLOCK 1 # endif # endif +// Windows Vista (Desktop, UWP): GetFinalPathNameByHandle() +# ifndef HAVE_GETFINALPATHNAMEBYHANDLE +# if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0600 +# define HAVE_GETFINALPATHNAMEBYHANDLE 1 +# endif +# endif + // POSIX.1-2001: mlock() # ifndef HAVE_MLOCK # if ZIX_POSIX_VERSION >= 200112L @@ -148,6 +177,13 @@ # endif # endif +// Windows XP (Desktop): VirtualLock +# ifndef HAVE_VIRTUALLOCK +# if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0501 && !ZIX_WINAPI_UWP +# define HAVE_VIRTUALLOCK 1 +# endif +# endif + #endif // !defined(ZIX_NO_DEFAULT_CONFIG) /* @@ -176,6 +212,18 @@ # define USE_COPY_FILE_RANGE 0 #endif +#if defined(HAVE_CREATEFILE2) && HAVE_CREATEFILE2 +# define USE_CREATEFILE2 1 +#else +# define USE_CREATEFILE2 0 +#endif + +#if defined(HAVE_CREATEHARDLINK) && HAVE_CREATEHARDLINK +# define USE_CREATEHARDLINK 1 +#else +# define USE_CREATEHARDLINK 0 +#endif + #if defined(HAVE_CREATESYMBOLICLINK) && HAVE_CREATESYMBOLICLINK # define USE_CREATESYMBOLICLINK 1 #else @@ -194,6 +242,12 @@ # define USE_FLOCK 0 #endif +#if defined(HAVE_GETFINALPATHNAMEBYHANDLE) && HAVE_GETFINALPATHNAMEBYHANDLE +# define USE_GETFINALPATHNAMEBYHANDLE 1 +#else +# define USE_GETFINALPATHNAMEBYHANDLE 0 +#endif + #if defined(HAVE_MLOCK) && HAVE_MLOCK # define USE_MLOCK 1 #else @@ -236,4 +290,10 @@ # define USE_SYSCONF 0 #endif +#if defined(HAVE_VIRTUALLOCK) && HAVE_VIRTUALLOCK +# define USE_VIRTUALLOCK 1 +#else +# define USE_VIRTUALLOCK 0 +#endif + #endif // ZIX_CONFIG_H |