diff options
author | Mitya Selivanov <automainint@guattari.tech> | 2023-12-29 06:21:33 +0100 |
---|---|---|
committer | Mitya Selivanov <automainint@guattari.tech> | 2023-12-29 06:21:33 +0100 |
commit | 2d6c8fec45b23a8a28668ecf3ef281139ab778a7 (patch) | |
tree | 75d2a8538992129a83c0c2b83688289443d697e5 /source/kit/file.c | |
parent | 820b171245f2f14766f3accdb0246a4e2c0d596a (diff) | |
download | saw-2d6c8fec45b23a8a28668ecf3ef281139ab778a7.zip |
refactor dependencies; include dependencies source code
Diffstat (limited to 'source/kit/file.c')
-rw-r--r-- | source/kit/file.c | 692 |
1 files changed, 692 insertions, 0 deletions
diff --git a/source/kit/file.c b/source/kit/file.c new file mode 100644 index 0000000..e5b834f --- /dev/null +++ b/source/kit/file.c @@ -0,0 +1,692 @@ +#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 +# endif +# ifndef NOMINMAX +# define NOMINMAX +# endif +# include <windows.h> +# include <shlwapi.h> +#else +# include <dirent.h> +# include <sys/mman.h> +# include <sys/stat.h> +# include <fcntl.h> +# include <unistd.h> +# include <limits.h> +#endif + +#ifdef __APPLE__ +# define st_mtim st_mtimespec +#endif + +#ifndef PATH_MAX +# define PATH_MAX MAX_PATH +#endif + +static i32 is_delim(char c) { + return c == '/' || c == '\\'; +} + +kit_str_builder_t kit_get_env(kit_str_t name, + kit_allocator_t *alloc) { + char *val = getenv(BS(name)); + i64 size = val != NULL ? (i64) strlen(val) : 0; + + str_builder_t result; + DA_INIT(result, size, alloc); + assert(result.size == size); + + if (result.size == size && size > 0) + memcpy(result.values, val, result.size); + else + DA_RESIZE(result, 0); + + return result; +} + +kit_str_builder_t kit_path_norm(kit_str_t path, + kit_allocator_t *alloc) { + str_t parent = SZ(".."); + i64 i, i1, j; + + str_builder_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 (i1 = 0, i = 0; i < path.size; i++) { + if (!is_delim(path.values[i])) + continue; + + str_t s = { .size = i - i1 - 1, .values = path.values + i1 + 1 }; + if (AR_EQUAL(s, parent)) { + i32 have_parent = 0; + i64 i0 = 0; + + for (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; + } + + i64 size = 0; + + for (i = 0; i < norm.size; i++) { + if (norm.values[i] != '\0') { + if (is_delim(norm.values[i])) + norm.values[size] = KIT_PATH_DELIM_C; + else + norm.values[size] = norm.values[i]; + size++; + } + } + + norm.size = size; + return norm; +} + +kit_str_builder_t kit_path_join(kit_str_t left, kit_str_t right, + kit_allocator_t *alloc) { + i64 left_size = left.size; + i64 right_size = right.size; + char *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_str_builder_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_C; + memcpy(joined.values + left_size + 1, right_values, right_size); + + return joined; +} + +kit_str_builder_t kit_path_user(kit_allocator_t *alloc) { + kit_str_builder_t user = kit_get_env(SZ(KIT_ENV_HOME), alloc); + if (user.size == 0) { + DA_RESIZE(user, 1); + if (user.size == 1) + user.values[0] = '.'; + } + return user; +} + +kit_str_builder_t kit_path_cache(kit_allocator_t *alloc) { + kit_str_builder_t cache, user; + + cache = kit_get_env(SZ("XDG_CACHE_HOME"), alloc); + if (cache.size != 0) + return cache; + DA_DESTROY(cache); + +#if defined(_WIN32) && !defined(__CYGWIN__) + cache = kit_get_env(SZ("TEMP"), alloc); + if (cache.size != 0) + return cache; + DA_DESTROY(cache); +#endif + + user = kit_path_user(alloc); + cache = +#ifdef __APPLE__ + kit_path_join(WRAP_STR(user), SZ("Library" PATH_DELIM "Caches"), + alloc); +#else + kit_path_join(WRAP_STR(user), SZ(".cache"), alloc); +#endif + DA_DESTROY(user); + + return cache; +} + +kit_str_builder_t kit_path_data(kit_allocator_t *alloc) { + kit_str_builder_t data, user; + + data = kit_get_env(SZ("XDG_DATA_HOME"), alloc); + if (data.size != 0) + return data; + DA_DESTROY(data); + +#if defined(_WIN32) && !defined(__CYGWIN__) + data = kit_get_env(SZ("LOCALAPPDATA"), alloc); + if (data.size != 0) + return data; + DA_DESTROY(data); +#endif + + user = kit_path_user(alloc); + data = +#ifdef __APPLE__ + kit_path_join(WRAP_STR(user), SZ("Library"), alloc); +#else + kit_path_join(WRAP_STR(user), SZ(".local" PATH_DELIM "share"), + alloc); +#endif + DA_DESTROY(user); + + return data; +} + +kit_str_t kit_path_index(kit_str_t path, i64 index) { + str_t s = { .size = 0, .values = NULL }; + + i64 i0 = 0; + i64 i = 0; + i64 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 path, i64 count) { + str_t s = { .size = 0, .values = path.values }; + + i64 i0 = 0; + i64 i = 0; + i64 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; +} + +// TODO +// Long path support for Windows +// +static void kit_prepare_path_(char *buf, kit_str_t path) { + assert(path.size == 0 || path.values != NULL); + assert(path.size + 1 < PATH_BUF_SIZE); + + 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]; \ + kit_prepare_path_(buf, path) + +kit_status_t kit_folder_create(kit_str_t path) { + PREPARE_PATH_BUF_; +#if defined(_WIN32) && !defined(__CYGWIN__) + return CreateDirectoryA(buf, NULL) ? KIT_OK + : KIT_ERROR_MKDIR_FAILED; +#else + return mkdir(buf, 0775) == 0 ? KIT_OK : KIT_ERROR_MKDIR_FAILED; +#endif +} + +kit_status_t kit_folder_create_recursive(kit_str_t path) { + for (i32 i = 0;; i++) { + str_t part = kit_path_take(path, i); + i32 type = kit_path_type(part); + if (type == KIT_PATH_FILE) + return KIT_ERROR_FILE_ALREADY_EXISTS; + if (type == KIT_PATH_NONE) { + kit_status_t s = kit_folder_create(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 path) { + PREPARE_PATH_BUF_; +#if defined(_WIN32) && !defined(__CYGWIN__) + return DeleteFileA(buf) ? KIT_OK : KIT_ERROR_UNLINK_FAILED; +#else + return unlink(buf) == 0 ? KIT_OK : KIT_ERROR_UNLINK_FAILED; +#endif +} + +kit_status_t kit_folder_remove(kit_str_t path) { + PREPARE_PATH_BUF_; +#if defined(_WIN32) && !defined(__CYGWIN__) + return RemoveDirectoryA(buf) ? KIT_OK : KIT_ERROR_RMDIR_FAILED; +#else + return rmdir(buf) == 0 ? KIT_OK : KIT_ERROR_RMDIR_FAILED; +#endif +} + +kit_status_t kit_file_remove_recursive(kit_str_t path, + kit_allocator_t *alloc) { + i32 type = kit_path_type(path); + i64 i; + + switch (type) { + case KIT_PATH_FILE: { + kit_status_t s = kit_file_remove(path); + assert(s == KIT_OK); + return s; + } + + case KIT_PATH_FOLDER: { + kit_path_list_t list = kit_folder_enum(path, alloc); + + assert(list.status == KIT_OK); + if (list.status != KIT_OK) { + kit_path_list_destroy(list); + return list.status; + } + + for (i = 0; i < list.files.size; i++) { + str_builder_t full_path = kit_path_join( + path, WRAP_STR(list.files.values[i]), alloc); + kit_status_t s = kit_file_remove_recursive( + WRAP_STR(full_path), alloc); + DA_DESTROY(full_path); + assert(s == KIT_OK); + } + + kit_path_list_destroy(list); + + kit_status_t s = kit_folder_remove(path); + assert(s == KIT_OK); + return s; + } + + default:; + } + + return KIT_ERROR_FILE_DOES_NOT_EXIST; +} + +kit_path_type_t kit_path_type(kit_str_t path) { + PREPARE_PATH_BUF_; +#if defined(_WIN32) && !defined(__CYGWIN__) + if (PathFileExistsA(buf)) { + if ((GetFileAttributesA(buf) & FILE_ATTRIBUTE_DIRECTORY) != 0) + return KIT_PATH_FOLDER; + else + return KIT_PATH_FILE; + } +#else + struct stat info; + if (stat(buf, &info) == 0) { + if (S_ISREG(info.st_mode)) + return KIT_PATH_FILE; + if (S_ISDIR(info.st_mode)) + return KIT_PATH_FOLDER; + } +#endif + return KIT_PATH_NONE; +} + +kit_file_info_t kit_file_info(kit_str_t path) { + kit_file_info_t result; + memset(&result, 0, sizeof result); + + PREPARE_PATH_BUF_; + +#if defined(_WIN32) && !defined(__CYGWIN__) + HANDLE f = CreateFileA(buf, GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (f != INVALID_HANDLE_VALUE) { + FILETIME ft; + if (GetFileTime(f, NULL, NULL, &ft) != 0) { + i64 nsec100 = (((u64) ft.dwHighDateTime) << 32) | + (u64) ft.dwLowDateTime; + result.time_modified_sec = (i64) (nsec100 / 10000000); + result.time_modified_nsec = (i32) (100 * (nsec100 % 10000000)); + } else { + assert(0); + } + + DWORD high; + DWORD low = GetFileSize(f, &high); + + result.size = (i64) ((((u64) high) << 32) | (u64) low); + result.status = KIT_OK; + + CloseHandle(f); + return result; + } +#else + struct stat info; + if (stat(buf, &info) == 0 && S_ISREG(info.st_mode)) { + result.size = (i64) info.st_size; +# ifndef st_mtime + // No support for nanosecond timestamps. + // + result.time_modified_sec = (i64) info.st_mtime; +# else + result.time_modified_sec = (i64) info.st_mtim.tv_sec; + result.time_modified_nsec = (i32) info.st_mtim.tv_nsec; +# endif + result.status = KIT_OK; + return result; + } +#endif + + result.status = KIT_ERROR_FILE_DOES_NOT_EXIST; + return result; +} + +kit_path_list_t kit_folder_enum(kit_str_t path, + kit_allocator_t *alloc) { + PREPARE_PATH_BUF_; + + kit_path_list_t result = { .status = KIT_OK }; + DA_INIT(result.files, 0, alloc); + +#if defined(_WIN32) && !defined(__CYGWIN__) + if (path.size + 3 >= PATH_BUF_SIZE) { + result.status = KIT_ERROR_PATH_TOO_LONG; + return result; + } + + buf[path.size] = '\\'; + buf[path.size + 1] = '*'; + + WIN32_FIND_DATAA data; + HANDLE find = FindFirstFileA(buf, &data); + + if (find == INVALID_HANDLE_VALUE) + return result; + + do { + if (strcmp(data.cFileName, ".") == 0 || + strcmp(data.cFileName, "..") == 0) + continue; + + i64 n = result.files.size; + DA_RESIZE(result.files, n + 1); + if (result.files.size != n + 1) { + result.status = KIT_ERROR_BAD_ALLOC; + break; + } + + i64 size = (i64) strlen(data.cFileName); + DA_INIT(result.files.values[n], size, alloc); + if (result.files.values[n].size != size) { + DA_RESIZE(result.files, n); + result.status = KIT_ERROR_BAD_ALLOC; + break; + } + + memcpy(result.files.values[n].values, data.cFileName, size); + } while (FindNextFileA(find, &data) != 0); + + FindClose(find); +#else + DIR *directory = opendir(buf); + + if (directory == NULL) + return result; + + for (;;) { + struct dirent *entry = readdir(directory); + + if (entry == NULL) + break; + + if (entry->d_name[0] == '.') + continue; + + i64 n = result.files.size; + DA_RESIZE(result.files, n + 1); + if (result.files.size != n + 1) { + result.status = KIT_ERROR_BAD_ALLOC; + break; + } + + i64 size = (i64) strlen(entry->d_name); + DA_INIT(result.files.values[n], size, alloc); + if (result.files.values[n].size != size) { + DA_RESIZE(result.files, n); + result.status = KIT_ERROR_BAD_ALLOC; + break; + } + + if (size > 0) + memcpy(result.files.values[n].values, entry->d_name, size); + } + + closedir(directory); +#endif + + return result; +} + +void kit_path_list_destroy(kit_path_list_t list) { + i64 i; + for (i = 0; i < list.files.size; i++) + DA_DESTROY(list.files.values[i]); + DA_DESTROY(list.files); +} + +kit_mapped_file_t kit_file_map(kit_str_t path, i64 size, i32 mode) { + assert(size > 0); + assert(path.size > 0); + assert(path.size <= PATH_MAX); + assert(path.values != NULL); + + kit_mapped_file_t mf; + memset(&mf, 0, sizeof mf); + + if (size <= 0) { + mf.status = KIT_ERROR_INVALID_SIZE; + return mf; + } + + if (path.size <= 0) { + mf.status = KIT_ERROR_INVALID_ARGUMENT; + return mf; + } + + if (path.size > PATH_MAX) { + mf.status = KIT_ERROR_PATH_TOO_LONG; + return mf; + } + +#if defined(_WIN32) && !defined(__CYGWIN__) + char buf[MAX_PATH + 1]; + memcpy(buf, path.values, path.size); + buf[path.size] = '\0'; + + HANDLE file = CreateFileA( + buf, GENERIC_READ | GENERIC_WRITE, + mode == FILE_MAP_SHARED ? FILE_SHARE_READ | FILE_SHARE_WRITE + : 0, + NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + if (file == INVALID_HANDLE_VALUE) { + mf.status = KIT_ERROR_OPEN_FAILED; + return mf; + } + + LONG high = (LONG) (size >> 32); + + if (SetFilePointer(file, (LONG) size, &high, FILE_BEGIN) == + INVALID_SET_FILE_POINTER) { + CloseHandle(file); + assert(0); + mf.status = KIT_ERROR_TRUNCATE_FAILED; + return mf; + } + + if (!SetEndOfFile(file)) { + CloseHandle(file); + assert(0); + mf.status = KIT_ERROR_TRUNCATE_FAILED; + return mf; + } + + HANDLE map = CreateFileMappingA(file, NULL, PAGE_READWRITE, + (DWORD) (size >> 32), (DWORD) size, + NULL); + + if (map == INVALID_HANDLE_VALUE) { + CloseHandle(file); + assert(0); + mf.status = KIT_ERROR_MAP_FAILED; + return mf; + } + + void *p = MapViewOfFile(map, FILE_MAP_ALL_ACCESS, 0, 0, + (SIZE_T) size); + + if (p == NULL) { + CloseHandle(map); + CloseHandle(file); + assert(0); + mf.status = KIT_ERROR_MAP_FAILED; + return mf; + } + + mf.status = KIT_OK; + mf.size = size; + mf.bytes = p; + mf._file = file; + mf._map = map; +#else + char buf[PATH_MAX + 1]; + memcpy(buf, path.values, path.size); + buf[path.size] = '\0'; + + i32 fd = open(buf, O_RDWR | O_CREAT, 0664); + + if (fd == -1) { + mf.status = KIT_ERROR_OPEN_FAILED; + return mf; + } + + if (ftruncate(fd, size) == -1) { + close(fd); + assert(0); + mf.status = KIT_ERROR_TRUNCATE_FAILED; + return mf; + } + + void *p = mmap( + NULL, size, PROT_READ | PROT_WRITE, + mode == KIT_FILE_MAP_SHARED ? MAP_SHARED : MAP_PRIVATE, fd, 0); + + if (p == MAP_FAILED) { + close(fd); + assert(0); + mf.status = KIT_ERROR_MAP_FAILED; + return mf; + } + + mf.status = KIT_OK; + mf.size = size; + mf.bytes = (u8 *) p; + mf._fd = fd; +#endif + + return mf; +} + +kit_status_t kit_file_sync(kit_mapped_file_t *mf) { + assert(mf != NULL); + + if (mf == NULL) + return KIT_ERROR_INVALID_ARGUMENT; + + kit_status_t status = KIT_OK; + +#if !defined(_WIN32) || defined(__CYGWIN__) + if (msync(mf->bytes, mf->size, MS_SYNC) != 0) + status |= KIT_ERROR_SYNC_FAILED; +#endif + + return status; +} + +kit_status_t kit_file_unmap(kit_mapped_file_t *mf) { + assert(mf != NULL); + + if (mf == NULL) + return KIT_ERROR_INVALID_ARGUMENT; + + kit_status_t status = KIT_OK; + +#if defined(_WIN32) && !defined(__CYGWIN__) + if (!UnmapViewOfFile(mf->bytes)) + status |= KIT_ERROR_UNMAP_FAILED; + if (!CloseHandle(mf->_map) || !CloseHandle(mf->_file)) + status |= KIT_ERROR_CLOSE_FAILED; +#else + if (munmap(mf->bytes, mf->size) != 0) + status |= KIT_ERROR_UNMAP_FAILED; + if (close(mf->_fd) != 0) + status |= KIT_ERROR_CLOSE_FAILED; +#endif + + return status; +} |