summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source/kit/CMakeLists.txt6
-rw-r--r--source/kit/dynamic_array.h3
-rw-r--r--source/kit/file.c417
-rw-r--r--source/kit/file.h83
-rw-r--r--source/kit/options.h16
-rw-r--r--source/kit/threads.win32.c2
-rw-r--r--source/test/unittests/CMakeLists.txt2
-rw-r--r--source/test/unittests/file.test.c198
8 files changed, 723 insertions, 4 deletions
diff --git a/source/kit/CMakeLists.txt b/source/kit/CMakeLists.txt
index 7bc3ba5..1dbaaf8 100644
--- a/source/kit/CMakeLists.txt
+++ b/source/kit/CMakeLists.txt
@@ -3,8 +3,8 @@ target_sources(
PRIVATE
input_buffer.c threads.win32.c time.c atomic.win32.c
threads.posix.c condition_variable.c move_back.c input_stream.c
- lower_bound.c string_ref.c async_function.c allocator.c array_ref.c
- dynamic_array.c mutex.c mersenne_twister_64.c
+ lower_bound.c file.c string_ref.c async_function.c allocator.c
+ array_ref.c dynamic_array.c mutex.c mersenne_twister_64.c
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/mutex.h>
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/time.h>
@@ -19,5 +19,7 @@ target_sources(
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/input_stream.h>
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/input_buffer.h>
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/lower_bound.h>
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/file.h>
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/options.h>
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/array_ref.h>
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/mersenne_twister_64.h>)
diff --git a/source/kit/dynamic_array.h b/source/kit/dynamic_array.h
index 409e7df..cc35a28 100644
--- a/source/kit/dynamic_array.h
+++ b/source/kit/dynamic_array.h
@@ -101,10 +101,13 @@ void kit_da_resize(kit_da_void_t *array, ptrdiff_t element_size,
KIT_DA_RESIZE((array_), (array_).size - 1); \
} while (0)
+KIT_DA_TYPE(kit_string_t, char);
+
#ifndef KIT_DISABLE_SHORT_NAMES
# define da_void_t kit_da_void_t
# define da_init kit_da_init
# define da_resize kit_da_resize
+# define string_t kit_string_t
# define DA_TYPE KIT_DA_TYPE
# define DA KIT_DA
diff --git a/source/kit/file.c b/source/kit/file.c
new file mode 100644
index 0000000..17aae79
--- /dev/null
+++ b/source/kit/file.c
@@ -0,0 +1,417 @@
+#include "file.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+enum { PATH_BUF_SIZE = 4096 };
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+# ifndef WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN 1
+# endif
+# include <Windows.h>
+#else
+# include <dirent.h>
+# include <sys/stat.h>
+# include <unistd.h>
+#endif
+
+static int is_delim(char const c) {
+ return c == '/' || c == '\\';
+}
+
+kit_string_t kit_path_norm(kit_str_t const path,
+ kit_allocator_t const alloc) {
+ SZ(parent, "..");
+
+ kit_string_t norm;
+ DA_INIT(norm, path.size, alloc);
+ assert(norm.size == path.size);
+
+ if (norm.size != path.size)
+ return norm;
+
+ memcpy(norm.values, path.values, path.size);
+
+ for (ptrdiff_t i1 = 0, i = 0; i < path.size; i++) {
+ if (!is_delim(path.values[i]))
+ continue;
+
+ str_t const s = { .size = i - i1 - 1,
+ .values = path.values + i1 + 1 };
+ if (AR_EQUAL(s, parent)) {
+ int have_parent = 0;
+ ptrdiff_t i0 = 0;
+
+ for (ptrdiff_t j = 0; j < i1; j++) {
+ if (norm.values[j] != '\0')
+ have_parent = 1;
+ if (is_delim(norm.values[j]))
+ i0 = j;
+ }
+
+ if (have_parent) {
+ memset(norm.values + i0, '\0', i - i0);
+
+ if (!is_delim(path.values[i0]))
+ norm.values[i] = '\0';
+ }
+ }
+
+ i1 = i;
+ }
+
+ ptrdiff_t size = 0;
+
+ for (ptrdiff_t i = 0; i < norm.size; i++) {
+ if (norm.values[i] != '\0') {
+ if (is_delim(norm.values[i]))
+ norm.values[size] = KIT_PATH_DELIM;
+ else
+ norm.values[size] = norm.values[i];
+ size++;
+ }
+ }
+
+ norm.size = size;
+ return norm;
+}
+
+kit_string_t kit_path_join(kit_str_t const left,
+ kit_str_t const right,
+ kit_allocator_t const alloc) {
+ ptrdiff_t left_size = left.size;
+ ptrdiff_t right_size = right.size;
+ char const *right_values = right.values;
+
+ if (left_size > 0 && is_delim(left.values[left_size - 1]))
+ left_size--;
+ if (right_size > 0 && is_delim(right.values[0])) {
+ right_size--;
+ right_values++;
+ }
+
+ kit_string_t joined;
+ DA_INIT(joined, left_size + right_size + 1, alloc);
+ assert(joined.size == left_size + right_size + 1);
+
+ if (joined.size != left_size + right_size + 1)
+ return joined;
+
+ memcpy(joined.values, left.values, left_size);
+ joined.values[left_size] = KIT_PATH_DELIM;
+ memcpy(joined.values + left_size + 1, right_values, right_size);
+
+ return joined;
+}
+
+kit_string_t kit_path_user(kit_allocator_t const alloc) {
+ char *home = getenv(KIT_ENV_HOME);
+ ptrdiff_t const size = home != NULL ? (ptrdiff_t) strlen(home) : 0;
+
+ string_t user;
+ DA_INIT(user, size, alloc);
+ assert(user.size == size);
+
+ if (user.size > 0)
+ memcpy(user.values, home, user.size);
+ else {
+ DA_RESIZE(user, 1);
+ assert(user.size == 1);
+
+ if (user.size == 1)
+ user.values[0] = '.';
+ }
+
+ return user;
+}
+
+kit_str_t kit_path_index(kit_str_t const path,
+ ptrdiff_t const index) {
+ str_t s = { .size = 0, .values = NULL };
+
+ ptrdiff_t i0 = 0;
+ ptrdiff_t i = 0;
+ ptrdiff_t n = 0;
+
+ for (; i < path.size; i++) {
+ if (!is_delim(path.values[i]))
+ continue;
+
+ if (i0 < i) {
+ if (n++ == index) {
+ s.values = path.values + i0;
+ s.size = i - i0;
+ return s;
+ }
+ }
+
+ i0 = i + 1;
+ }
+
+ if (n == index) {
+ s.values = path.values + i0;
+ s.size = i - i0;
+ }
+
+ return s;
+}
+
+kit_str_t kit_path_take(kit_str_t const path, ptrdiff_t const count) {
+ str_t s = { .size = 0, .values = path.values };
+
+ ptrdiff_t i0 = 0;
+ ptrdiff_t i = 0;
+ ptrdiff_t n = 0;
+
+ for (; i < path.size; i++) {
+ if (!is_delim(path.values[i]))
+ continue;
+
+ if (i0 < i) {
+ if (n++ == count) {
+ s.size = i;
+ return s;
+ }
+ }
+
+ i0 = i + 1;
+ }
+
+ if (n == count)
+ s.size = i;
+
+ return s;
+}
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+static void win32_prepare_path_(WCHAR *const buf,
+ kit_str_t const path) {
+ memset(buf, 0, PATH_BUF_SIZE);
+ buf[0] = L'\\';
+ buf[1] = L'\\';
+ buf[2] = L'?';
+ buf[3] = L'\\';
+ if (path.size > 0 && path.size + 5 < PATH_BUF_SIZE)
+ for (ptrdiff_t i = 0; i < path.size; i++) {
+ if (path.values[i] == '/')
+ buf[4 + i] = L'\\';
+ else
+ buf[4 + i] = path.values[i];
+ }
+}
+# define PREPARE_PATH_BUF_ \
+ WCHAR buf[PATH_BUF_SIZE]; \
+ win32_prepare_path_(buf, path)
+#else
+static void unix_prepare_path_(char *const buf,
+ kit_str_t const path) {
+ memset(buf, 0, PATH_BUF_SIZE);
+ if (path.size > 0 && path.size + 1 < PATH_BUF_SIZE)
+ memcpy(buf, path.values, path.size);
+}
+# define PREPARE_PATH_BUF_ \
+ char buf[PATH_BUF_SIZE]; \
+ unix_prepare_path_(buf, path)
+#endif
+
+kit_status_t kit_file_create_folder(kit_str_t const path) {
+ PREPARE_PATH_BUF_;
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ if (CreateDirectoryW(temp, NULL))
+ return KIT_OK;
+#else
+ if (mkdir(buf, 0755) == 0)
+ return KIT_OK;
+#endif
+ return KIT_ERROR;
+}
+
+kit_status_t kit_file_create_folder_recursive(kit_str_t const path) {
+ for (ptrdiff_t i = 0;; i++) {
+ str_t const part = kit_path_take(path, i);
+ int const type = kit_path_type(part);
+ if (type == KIT_PATH_FILE)
+ return KIT_ERROR;
+ if (type == KIT_PATH_NONE) {
+ kit_status_t const s = kit_file_create_folder(part);
+ if (s != KIT_OK)
+ return s;
+ }
+ if (part.size == path.size)
+ break;
+ }
+
+ return KIT_OK;
+}
+
+kit_status_t kit_file_remove(kit_str_t const path) {
+ PREPARE_PATH_BUF_;
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ if (DeleteFileW(buf))
+ return KIT_OK;
+#else
+ if (unlink(buf) == 0)
+ return KIT_OK;
+#endif
+ return KIT_ERROR;
+}
+
+kit_status_t kit_file_remove_folder(kit_str_t const path) {
+ PREPARE_PATH_BUF_;
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ if (DeleteDirectoryW(buf))
+ return KIT_OK;
+#else
+ if (rmdir(buf) == 0)
+ return KIT_OK;
+#endif
+ return KIT_ERROR;
+}
+
+kit_status_t kit_file_remove_recursive(kit_str_t const path,
+ kit_allocator_t const alloc) {
+ int type = kit_path_type(path);
+
+ switch (type) {
+ case KIT_PATH_FILE: return kit_file_remove(path);
+
+ case KIT_PATH_FOLDER: {
+ kit_path_list_t list = kit_file_enum_folder(path, alloc);
+ for (ptrdiff_t i = 0; i < list.size; i++) {
+ str_t const s = { .size = list.values[i].size,
+ .values = list.values[i].values };
+ kit_file_remove_recursive(s, alloc);
+ }
+ kit_path_list_destroy(list);
+ return kit_file_remove_folder(path);
+ }
+
+ default:;
+ }
+
+ return KIT_ERROR;
+}
+
+int kit_path_type(kit_str_t const path) {
+ PREPARE_PATH_BUF_;
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ if (PathFileExistsW(buf)) {
+ if ((GetFileAttributesW(buf) & FILE_ATTRIBUTE_DIRECTORY) != 0)
+ return KIT_PATH_FOLDER;
+ else
+ return KIT_PATH_FILE;
+ }
+#else
+ struct stat info;
+ if (stat(buf, &info) == 0) {
+ if ((info.st_mode & S_IFREG) != 0)
+ return KIT_PATH_FILE;
+ if ((info.st_mode & S_IFDIR) != 0)
+ return KIT_PATH_FOLDER;
+ }
+#endif
+ return KIT_PATH_NONE;
+}
+
+ptrdiff_t kit_file_size(kit_str_t const path) {
+ PREPARE_PATH_BUF_;
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ HFILE f = CreateFileW(buf, GENERIC_READ, FILE_SHARE_READ, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL);
+ if (f != INVALID_HANDLE_VALUE) {
+ DWORD high;
+ DWORD low = GetFileSize(f, &high);
+ CloseHandle(f);
+ return (ptrdiff_t) (((uint64_t) high << 32) | (uint64_t) low);
+ }
+#else
+ struct stat info;
+ if (stat(buf, &info) == 0)
+ return info.st_size;
+#endif
+ return KIT_FILE_SIZE_ERROR;
+}
+
+kit_path_list_t kit_file_enum_folder(kit_str_t const path,
+ kit_allocator_t const alloc) {
+ PREPARE_PATH_BUF_;
+
+ kit_path_list_t list;
+ DA_INIT(list, 0, alloc);
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ if (path.size + 7 >= PATH_BUF_SIZE)
+ return list;
+
+ buf[path.size + 4] = '\\';
+ buf[path.size + 5] = '*';
+
+ WIN32_FIND_DATAW data;
+ HANDLE find = FindFirstFileW(buf, &data);
+
+ if (find == INVALID_HANDLE_VALUE)
+ return list;
+
+ do {
+ ptrdiff_t const n = list.size;
+ DA_RESIZE(list, n + 1);
+ if (list.size != n + 1)
+ break;
+
+ ptrdiff_t size = 0;
+ while (size < MAX_PATH && data.cFileName[size] != L'\0') size++;
+ DA_INIT(list.values[n], size, alloc);
+ if (list.values[n].size != size) {
+ DA_RESIZE(list, n);
+ break;
+ }
+
+ for (ptrdiff_t i = 0; i < size; i++)
+ list.values[n].values[i] = data.cFileName[i];
+ } while (FindNextFileW(find, &data) != 0);
+
+ FindClose(find);
+#else
+ DIR *directory = opendir(buf);
+
+ if (directory == NULL)
+ return list;
+
+ for (;;) {
+ struct dirent *entry = readdir(directory);
+
+ if (entry == NULL)
+ break;
+
+ if (entry->d_name[0] == '.')
+ continue;
+
+ ptrdiff_t const n = list.size;
+ DA_RESIZE(list, n + 1);
+ if (list.size != n + 1)
+ break;
+
+ ptrdiff_t const size = (ptrdiff_t) strlen(entry->d_name);
+ DA_INIT(list.values[n], size, alloc);
+ if (list.values[n].size != size) {
+ DA_RESIZE(list, n);
+ break;
+ }
+
+ if (size > 0)
+ memcpy(list.values[n].values, entry->d_name, size);
+ }
+
+ closedir(directory);
+#endif
+
+ return list;
+}
+
+void kit_path_list_destroy(kit_path_list_t list) {
+ for (ptrdiff_t i = 0; i < list.size; i++)
+ DA_DESTROY(list.values[i]);
+ DA_DESTROY(list);
+}
diff --git a/source/kit/file.h b/source/kit/file.h
new file mode 100644
index 0000000..6313c2e
--- /dev/null
+++ b/source/kit/file.h
@@ -0,0 +1,83 @@
+#ifndef KIT_FILE_H
+#define KIT_FILE_H
+
+#include "dynamic_array.h"
+#include "options.h"
+#include "string_ref.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+# define KIT_PATH_DELIM '\\'
+# define KIT_ENV_HOME "USERPROFILE"
+#else
+# define KIT_PATH_DELIM '/'
+# define KIT_ENV_HOME "HOME"
+#endif
+
+enum {
+ KIT_PATH_NONE,
+ KIT_PATH_FILE,
+ KIT_PATH_FOLDER,
+ KIT_FILE_SIZE_ERROR = -1
+};
+
+kit_string_t kit_path_norm(kit_str_t path, kit_allocator_t alloc);
+
+kit_string_t kit_path_join(kit_str_t left, kit_str_t right,
+ kit_allocator_t alloc);
+
+kit_string_t kit_path_user(kit_allocator_t alloc);
+
+kit_str_t kit_path_index(kit_str_t path, ptrdiff_t index);
+
+kit_str_t kit_path_take(kit_str_t path, ptrdiff_t count);
+
+kit_status_t kit_file_create_folder(kit_str_t path);
+
+kit_status_t kit_file_create_folder_recursive(kit_str_t path);
+
+kit_status_t kit_file_remove(kit_str_t path);
+
+kit_status_t kit_file_remove_folder(kit_str_t path);
+
+kit_status_t kit_file_remove_recursive(kit_str_t path,
+ kit_allocator_t alloc);
+
+int kit_path_type(kit_str_t path);
+
+ptrdiff_t kit_file_size(kit_str_t path);
+
+DA_TYPE(kit_path_list_t, kit_string_t);
+
+kit_path_list_t kit_file_enum_folder(kit_str_t path,
+ kit_allocator_t alloc);
+
+void kit_path_list_destroy(kit_path_list_t list);
+
+#ifndef KIT_DISABLE_SHORT_NAMES
+# define path_norm kit_path_norm
+# define path_join kit_path_join
+# define path_user kit_path_user
+# define path_index kit_path_index
+# define path_take kit_path_take
+# define file_create_folder kit_file_create_folder
+# define file_create_folder_recursive \
+ kit_file_create_folder_recursive
+# define file_remove kit_file_remove
+# define file_remove_folder kit_file_remove_folder
+# define file_remove_recursive kit_file_remove_recursive
+# define path_type kit_path_type
+# define file_size kit_file_size
+# define path_list_t kit_path_list_t
+# define file_enum_folder kit_file_enum_folder
+# define path_list_destroy kit_path_list_destroy
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/source/kit/options.h b/source/kit/options.h
new file mode 100644
index 0000000..d90ea5b
--- /dev/null
+++ b/source/kit/options.h
@@ -0,0 +1,16 @@
+#ifndef KIT_OPTIONS_H
+#define KIT_OPTIONS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum { KIT_OK, KIT_ERROR };
+
+typedef int kit_status_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/source/kit/threads.win32.c b/source/kit/threads.win32.c
index 0f54d6c..534b01c 100644
--- a/source/kit/threads.win32.c
+++ b/source/kit/threads.win32.c
@@ -45,7 +45,7 @@
# ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN 1
# endif
-# include <windows.h>
+# include <Windows.h>
/*
Configuration macro:
diff --git a/source/test/unittests/CMakeLists.txt b/source/test/unittests/CMakeLists.txt
index 29dd1b2..fe4c902 100644
--- a/source/test/unittests/CMakeLists.txt
+++ b/source/test/unittests/CMakeLists.txt
@@ -5,4 +5,4 @@ target_sources(
main.test.c string_ref.test.c atomic.test.c thread.test.c
array_ref.test.c input_stream.test.c lower_bound.test.c
condition_variable.test.c mersenne_twister_64.test.c input_buffer.test.c
- move_back.test.c dynamic_array.test.c)
+ move_back.test.c dynamic_array.test.c file.test.c)
diff --git a/source/test/unittests/file.test.c b/source/test/unittests/file.test.c
new file mode 100644
index 0000000..d6b72f6
--- /dev/null
+++ b/source/test/unittests/file.test.c
@@ -0,0 +1,198 @@
+#include "../../kit/file.h"
+#include "../../kit/string_ref.h"
+#include <string.h>
+
+#define KIT_TEST_FILE file
+#include "../../kit_test/test.h"
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+# define S_DELIM_ "\\"
+#else
+# define S_DELIM_ "/"
+#endif
+
+TEST("file path normalize one") {
+ SZ(foo, "foo/bar/../baz");
+ SZ(foo_norm, "foo" S_DELIM_ "baz");
+
+ string_t bar = path_norm(foo, kit_alloc_default());
+
+ REQUIRE(AR_EQUAL(foo_norm, bar));
+
+ DA_DESTROY(bar);
+}
+
+TEST("file path normalize two") {
+ SZ(foo, "foo/bar/../../baz");
+ SZ(foo_norm, "baz");
+
+ string_t bar = path_norm(foo, kit_alloc_default());
+
+ REQUIRE(AR_EQUAL(foo_norm, bar));
+
+ DA_DESTROY(bar);
+}
+
+TEST("file path normalize parent") {
+ SZ(foo, "../baz");
+ SZ(foo_norm, ".." S_DELIM_ "baz");
+
+ string_t bar = path_norm(foo, kit_alloc_default());
+
+ REQUIRE(AR_EQUAL(foo_norm, bar));
+
+ DA_DESTROY(bar);
+}
+
+TEST("file path normalize double parent") {
+ SZ(foo, "foo/../../baz");
+ SZ(foo_norm, ".." S_DELIM_ "baz");
+
+ string_t bar = path_norm(foo, kit_alloc_default());
+
+ REQUIRE(AR_EQUAL(foo_norm, bar));
+
+ DA_DESTROY(bar);
+}
+
+TEST("file path normalize windows delim") {
+ SZ(foo, "foo\\bar\\..\\baz");
+ SZ(foo_norm, "foo" S_DELIM_ "baz");
+
+ string_t bar = path_norm(foo, kit_alloc_default());
+
+ REQUIRE(AR_EQUAL(foo_norm, bar));
+
+ DA_DESTROY(bar);
+}
+
+TEST("file path join no delim") {
+ SZ(foo, "foo");
+ SZ(bar, "bar");
+ SZ(joined, "foo/bar");
+
+ string_t foobar = path_join(foo, bar, kit_alloc_default());
+
+ REQUIRE(AR_EQUAL(joined, foobar));
+
+ DA_DESTROY(foobar);
+}
+
+TEST("file path join delim left") {
+ SZ(foo, "foo/");
+ SZ(bar, "bar");
+ SZ(joined, "foo/bar");
+
+ string_t foobar = path_join(foo, bar, kit_alloc_default());
+
+ REQUIRE(AR_EQUAL(joined, foobar));
+
+ DA_DESTROY(foobar);
+}
+
+TEST("file path join delim right") {
+ SZ(foo, "foo");
+ SZ(bar, "/bar");
+ SZ(joined, "foo/bar");
+
+ string_t foobar = path_join(foo, bar, kit_alloc_default());
+
+ REQUIRE(AR_EQUAL(joined, foobar));
+
+ DA_DESTROY(foobar);
+}
+
+TEST("file path join delim both") {
+ SZ(foo, "foo/");
+ SZ(bar, "/bar");
+ SZ(joined, "foo/bar");
+
+ string_t foobar = path_join(foo, bar, kit_alloc_default());
+
+ REQUIRE(AR_EQUAL(joined, foobar));
+
+ DA_DESTROY(foobar);
+}
+
+TEST("file path user") {
+ string_t user = path_user(kit_alloc_default());
+
+ REQUIRE(user.size > 0);
+
+ DA_DESTROY(user);
+}
+
+TEST("file path index relative") {
+ SZ(foobar, "foo/bar");
+ SZ(foo, "foo");
+ SZ(bar, "bar");
+
+ REQUIRE(AR_EQUAL(path_index(foobar, 0), foo));
+ REQUIRE(AR_EQUAL(path_index(foobar, 1), bar));
+ REQUIRE(path_index(foobar, 2).size == 0);
+}
+
+TEST("file path index absolute") {
+ SZ(foobar, "/foo/bar");
+ SZ(foo, "foo");
+ SZ(bar, "bar");
+
+ REQUIRE(AR_EQUAL(path_index(foobar, 0), foo));
+ REQUIRE(AR_EQUAL(path_index(foobar, 1), bar));
+ REQUIRE(path_index(foobar, 2).size == 0);
+}
+
+TEST("file path index windows disk name") {
+ SZ(foobar, "c:\\foo\\bar");
+ SZ(disk, "c:");
+ SZ(foo, "foo");
+ SZ(bar, "bar");
+
+ REQUIRE(AR_EQUAL(path_index(foobar, 0), disk));
+ REQUIRE(AR_EQUAL(path_index(foobar, 1), foo));
+ REQUIRE(AR_EQUAL(path_index(foobar, 2), bar));
+ REQUIRE(path_index(foobar, 3).size == 0);
+}
+
+TEST("file path take relative") {
+ SZ(foobar, "foo/bar/");
+ SZ(foo, "foo");
+ SZ(bar, "foo/bar");
+ SZ(bar_end, "foo/bar/");
+
+ REQUIRE(AR_EQUAL(path_take(foobar, 0), foo));
+ REQUIRE(AR_EQUAL(path_take(foobar, 1), bar));
+ REQUIRE(AR_EQUAL(path_take(foobar, 2), bar_end));
+}
+
+TEST("file path take absolute") {
+ SZ(foobar, "/foo/bar");
+ SZ(foo, "/foo");
+ SZ(bar, "/foo/bar");
+
+ REQUIRE(AR_EQUAL(path_take(foobar, 0), foo));
+ REQUIRE(AR_EQUAL(path_take(foobar, 1), bar));
+}
+
+TEST("file path take windows disk name") {
+ SZ(foobar, "c:\\foo\\bar");
+ SZ(disk, "c:");
+ SZ(foo, "c:\\foo");
+ SZ(bar, "c:\\foo\\bar");
+
+ REQUIRE(AR_EQUAL(path_take(foobar, 0), disk));
+ REQUIRE(AR_EQUAL(path_take(foobar, 1), foo));
+ REQUIRE(AR_EQUAL(path_take(foobar, 2), bar));
+}
+
+TEST("file create folder") { }
+
+TEST("file create folder recursive") { }
+
+TEST("file remove") { }
+
+TEST("file remove folder") { }
+
+TEST("file remove recursive") { }
+
+TEST("file enum folder") { }