summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/.clang-tidy1
-rw-r--r--src/allocator.c16
-rw-r--r--src/btree.c90
-rw-r--r--src/bump_allocator.c21
-rw-r--r--src/darwin/sem_darwin.c4
-rw-r--r--src/digest.c2
-rw-r--r--src/errno_status.c4
-rw-r--r--src/errno_status.h10
-rw-r--r--src/filesystem.c16
-rw-r--r--src/hash.c9
-rw-r--r--src/path.c35
-rw-r--r--src/path_iter.h8
-rw-r--r--src/posix/environment_posix.c134
-rw-r--r--src/posix/filesystem_posix.c35
-rw-r--r--src/posix/sem_posix.c7
-rw-r--r--src/posix/thread_posix.c4
-rw-r--r--src/ring.c15
-rw-r--r--src/status.c2
-rw-r--r--src/string_view.c26
-rw-r--r--src/system.c2
-rw-r--r--src/system.h2
-rw-r--r--src/tree.c44
-rw-r--r--src/tree_debug.h139
-rw-r--r--src/win32/environment_win32.c37
-rw-r--r--src/win32/filesystem_win32.c298
-rw-r--r--src/win32/sem_win32.c4
-rw-r--r--src/win32/thread_win32.c4
-rw-r--r--src/win32/win32_util.c73
-rw-r--r--src/win32/win32_util.h31
-rw-r--r--src/zix_config.h68
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);
diff --git a/src/hash.c b/src/hash.c
index 7fc345d..6c98bcf 100644
--- a/src/hash.c
+++ b/src/hash.c
@@ -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);
diff --git a/src/path.c b/src/path.c
index c0140e4..2240031 100644
--- a/src/path.c
+++ b/src/path.c
@@ -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>
diff --git a/src/ring.c b/src/ring.c
index ad00f75..cb8a47e 100644
--- a/src/ring.c
+++ b/src/ring.c
@@ -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>
diff --git a/src/tree.c b/src/tree.c
index 3c705c6..4282418 100644
--- a/src/tree.c
+++ b/src/tree.c
@@ -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, &params);
+
+ 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