diff --git a/io/sys/darwin/file.c b/io/sys/darwin/file.c index 7b0ff29..8b191e1 100644 --- a/io/sys/darwin/file.c +++ b/io/sys/darwin/file.c @@ -75,7 +75,7 @@ static fx_result file_open_shadow( fx_string_prepend_cstr(filename, ".~"); - fx_path *shadow_filename = fx_path_create_from_cstr(fx_string_ptr(filename)); + fx_path *shadow_filename = fx_path_create_from_cstr(fx_string_get_cstr(filename)); fx_string_unref(filename); const fx_path *parts[] = { diff --git a/io/sys/darwin/path.c b/io/sys/darwin/path.c index c8f50f9..7f49223 100644 --- a/io/sys/darwin/path.c +++ b/io/sys/darwin/path.c @@ -36,13 +36,13 @@ static fx_path *path_make_canonical(const struct fx_path_p *in) static bool path_is_absolute(const struct fx_path_p *path) { - const char *s = fx_string_ptr(path->p_pathstr); + const char *s = fx_string_get_cstr(path->p_pathstr); return s[0] == '/'; } static const char *path_ptr(const struct fx_path_p *path) { - return fx_string_ptr(path->p_pathstr); + return fx_string_get_cstr(path->p_pathstr); } static enum fx_status path_stat(const struct fx_path_p *path, struct fx_file_info *out) @@ -98,8 +98,8 @@ static void append_path(struct fx_path_p *dest, const struct fx_path_p *src) } if (fx_string_get_size(dest->p_pathstr, FX_STRLEN_NORMAL) > 0 - && fx_string_back(dest->p_pathstr) != '/' - && fx_string_front(src->p_pathstr) != '/') { + && fx_string_get_last_char(dest->p_pathstr) != '/' + && fx_string_get_first_char(src->p_pathstr) != '/') { char s[] = {'/', 0}; fx_string_append_cstr(dest->p_pathstr, s); } @@ -109,7 +109,7 @@ static void append_path(struct fx_path_p *dest, const struct fx_path_p *src) static enum fx_status path_unlink(const struct fx_path_p *path) { - int err = remove(fx_string_ptr(path->p_pathstr)); + int err = remove(fx_string_get_cstr(path->p_pathstr)); return err == 0 ? FX_SUCCESS : fx_status_from_errno(errno, FX_ERR_IO_FAILURE); } @@ -119,7 +119,7 @@ static enum fx_status path_get_directory( fx_string *path_str = path->p_pathstr; long len = fx_string_get_size(path_str, FX_STRLEN_NORMAL); - const char *path_cstr = fx_string_ptr(path_str); + const char *path_cstr = fx_string_get_cstr(path_str); char *sep = strrchr(path_cstr, '/'); if (!sep) { @@ -128,9 +128,9 @@ static enum fx_status path_get_directory( } size_t dir_path_len = (size_t)(sep - path_cstr); - fx_string *dir_path_s = fx_string_substr(path_str, 0, dir_path_len); + fx_string *dir_path_s = fx_string_get_substr(path_str, 0, dir_path_len); - fx_path *dir_path = fx_path_create_from_cstr(fx_string_ptr(dir_path_s)); + fx_path *dir_path = fx_path_create_from_cstr(fx_string_get_cstr(dir_path_s)); fx_string_unref(dir_path_s); *out_dir_path = dir_path; @@ -144,7 +144,7 @@ static enum fx_status path_get_filename( fx_string *path_str = path->p_pathstr; long len = fx_string_get_size(path_str, FX_STRLEN_NORMAL); - char *sep = strrchr(fx_string_ptr(path_str), '/'); + char *sep = strrchr(fx_string_get_cstr(path_str), '/'); if (!sep) { fx_string_append_s(out_name, path_str); @@ -245,7 +245,7 @@ fx_path *fx_path_create_from_cstr(const char *cstr) prev = c; } - while (fx_string_back(p->p_pathstr) == '/') { + while (fx_string_get_last_char(p->p_pathstr) == '/') { fx_string_pop_back(p->p_pathstr); } @@ -379,7 +379,7 @@ void path_to_string(const fx_object *obj, fx_stream *out) { struct fx_path_p *path = fx_object_get_private(obj, FX_TYPE_PATH); - fx_stream_write_cstr(out, fx_string_ptr(path->p_pathstr), NULL); + fx_stream_write_cstr(out, fx_string_get_cstr(path->p_pathstr), NULL); } /*** CLASS DEFINITION *********************************************************/ diff --git a/io/sys/linux/directory.c b/io/sys/linux/directory.c new file mode 100644 index 0000000..76dcbd0 --- /dev/null +++ b/io/sys/linux/directory.c @@ -0,0 +1,689 @@ +#define _POSIX_C_SOURCE 200809L + +#include "misc.h" +#include "posix.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/*** PRIVATE DATA *************************************************************/ + +enum directory_flags { + DIRECTORY_DELETE_ON_CLOSE = 0x01u, +}; + +struct fx_directory_p { + enum directory_flags d_flags; + int d_fd; + fx_path *d_path_rel; + fx_path *d_path_abs; +}; + +struct fx_directory_iterator_p { + struct fx_directory_p *_p; + + FTS *fts; + FTSENT *ent; + + fx_directory_iterator_flags flags; + fx_directory *root; + + fx_directory_entry entry; +}; + +/*** PRIVATE FUNCTIONS ********************************************************/ +static const fx_path *directory_get_path(const struct fx_directory_p *dir) +{ + return dir->d_path_abs; +} + +static const fx_path *directory_get_rel_path(const struct fx_directory_p *dir) +{ + return dir->d_path_rel; +} + +static const char *directory_get_path_cstr(const struct fx_directory_p *dir) +{ + return fx_path_ptr(dir->d_path_abs); +} + +static const char *directory_get_rel_path_cstr(const struct fx_directory_p *dir) +{ + return fx_path_ptr(dir->d_path_rel); +} + +static fx_result directory_delete(fx_directory *dir, struct fx_directory_p *dir_p) +{ + enum fx_status status = FX_SUCCESS; + + fx_iterator *it + = fx_directory_begin(dir, FX_DIRECTORY_ITERATE_PARENT_LAST); + while (FX_OK(fx_iterator_get_status(it))) { + fx_iterator_erase(it); + } + + status = fx_iterator_get_status(it); + if (!FX_OK(status) && status != FX_ERR_NO_DATA) { + return FX_RESULT_STATUS(status); + } + + status = fx_path_unlink(dir_p->d_path_abs); + if (!FX_OK(status)) { + return FX_RESULT_STATUS(status); + } + + return FX_RESULT_SUCCESS; +} + +static bool directory_path_exists( + const struct fx_directory_p *root, const fx_path *path) +{ + const fx_path *parts[] = { + root ? root->d_path_abs : NULL, + path, + }; + + fx_path *abs_path = fx_path_join(parts, sizeof parts / sizeof parts[0]); + if (!abs_path) { + return false; + } + + bool result = fx_path_exists(abs_path); + fx_path_unref(abs_path); + + return result; +} + +static bool directory_path_is_file( + const struct fx_directory_p *root, const fx_path *path) +{ + const fx_path *parts[] = { + root ? root->d_path_abs : NULL, + path, + }; + + fx_path *abs_path = fx_path_join(parts, sizeof parts / sizeof parts[0]); + if (!abs_path) { + return false; + } + + bool result = fx_path_is_file(abs_path); + fx_path_unref(abs_path); + + return result; +} + +static bool directory_path_is_directory( + const struct fx_directory_p *root, const fx_path *path) +{ + const fx_path *parts[] = { + root ? root->d_path_abs : NULL, + path, + }; + + fx_path *abs_path = fx_path_join(parts, sizeof parts / sizeof parts[0]); + if (!abs_path) { + return false; + } + + bool result = fx_path_is_directory(abs_path); + fx_path_unref(abs_path); + + return result; +} + +static fx_result directory_path_stat( + const struct fx_directory_p *root, const fx_path *path, + struct fx_file_info *out) +{ + const fx_path *parts[] = { + root ? root->d_path_abs : NULL, + path, + }; + + fx_path *abs_path = fx_path_join(parts, sizeof parts / sizeof parts[0]); + if (!abs_path) { + return FX_RESULT_ERR(NO_MEMORY); + } + + enum fx_status status = fx_path_stat(abs_path, out); + fx_path_unref(abs_path); + + return FX_RESULT_STATUS(status); +} + +static fx_result directory_path_unlink( + const struct fx_directory_p *root, const fx_path *path) +{ + const fx_path *parts[] = { + root ? root->d_path_abs : NULL, + path, + }; + + fx_path *abs_path = fx_path_join(parts, sizeof parts / sizeof parts[0]); + if (!abs_path) { + return FX_RESULT_ERR(NO_MEMORY); + } + + enum fx_status status = fx_path_unlink(abs_path); + fx_path_unref(abs_path); + + return FX_RESULT_STATUS(status); +} + +static fx_result create_directory(struct fx_directory_p *root, const char *path) +{ + int root_fd = root ? root->d_fd : -1; + int err; + + if (root_fd == -1) { + err = mkdir(path, 0755); + } else { + err = mkdirat(root_fd, path, 0755); + } + + if (err == 0 || errno == EEXIST) { + return FX_RESULT_SUCCESS; + } + + return fx_result_from_errno_with_subfilepath( + errno, path, directory_get_rel_path_cstr(root), FX_ERR_IO_FAILURE); +} + +static fx_result create_directory_hierarchy( + struct fx_directory_p *root, const char *path) +{ + int root_fd = root ? root->d_fd : AT_FDCWD; + + char *path_buf = fx_strdup(path); + if (!path_buf) { + return FX_RESULT_ERR(NO_MEMORY); + } + + fx_result result = FX_RESULT_SUCCESS; + for (size_t i = 0; path_buf[i]; i++) { + if (path_buf[i] != '/') { + continue; + } + + path_buf[i] = 0; + + int err = mkdirat(root_fd, path_buf, 0755); + if (err != 0 && errno != EEXIST) { + result = fx_result_from_errno_with_subfilepath( + errno, path_buf, directory_get_rel_path_cstr(root), + FX_ERR_IO_FAILURE); + break; + } + + path_buf[i] = '/'; + } + + int err = mkdirat(root_fd, path_buf, 0755); + if (err != 0 && errno != EEXIST) { + result = fx_result_from_errno_with_subfilepath( + errno, path_buf, directory_get_rel_path_cstr(root), + FX_ERR_IO_FAILURE); + } + + free(path_buf); + return result; +} + +static fx_result directory_open( + struct fx_directory_p *root, const fx_path *path, + fx_directory_open_flags flags, fx_directory **out) +{ + enum fx_status status = FX_SUCCESS; + + int root_fd = root ? root->d_fd : AT_FDCWD; + + const char *path_cstr = fx_path_ptr(path); + if (root) { + while (*path_cstr == '/') { + path_cstr++; + } + } + + fx_result result = FX_RESULT_SUCCESS; + if ((flags & FX_DIRECTORY_OPEN_CREATE_INTERMEDIATE) + == FX_DIRECTORY_OPEN_CREATE_INTERMEDIATE) { + result = create_directory_hierarchy(root, path_cstr); + } else if ((flags & FX_DIRECTORY_OPEN_CREATE) == FX_DIRECTORY_OPEN_CREATE) { + result = create_directory(root, path_cstr); + } + + if (fx_result_is_error(result)) { + return fx_result_propagate(result); + } + + int fd = openat(root_fd, path_cstr, O_DIRECTORY); + + if (fd == -1) { + status = fx_status_from_errno(errno, FX_ERR_IO_FAILURE); + return FX_RESULT_STATUS(status); + } + + fx_directory *dir = fx_object_create(FX_TYPE_DIRECTORY); + fx_path *cwd = NULL; + struct fx_directory_p *p = fx_object_get_private(dir, FX_TYPE_DIRECTORY); + + if (!root) { + cwd = fx_path_create_cwd(); + } + + const fx_path *parts[] = { + root ? root->d_path_abs : cwd, + path, + }; + + fx_path *new_path = fx_path_join(parts, sizeof parts / sizeof parts[0]); + if (!new_path) { + return FX_RESULT_ERR(NO_MEMORY); + } + + if (cwd) { + fx_path_unref(cwd); + } + + p->d_path_abs = new_path; + p->d_path_rel = fx_path_duplicate(path); + p->d_fd = fd; + + if (flags & FX_DIRECTORY_OPEN_DELETE_ON_CLOSE) { + p->d_flags = DIRECTORY_DELETE_ON_CLOSE; + } + + *out = dir; + + return FX_RESULT_SUCCESS; +} + +/*** PUBLIC FUNCTIONS *********************************************************/ + +fx_result fx_directory_open( + fx_directory *root, const fx_path *path, fx_directory_open_flags flags, + fx_directory **out) +{ + FX_CLASS_DISPATCH_STATIC( + FX_TYPE_DIRECTORY, directory_open, root, path, flags, out); +} + +fx_result fx_directory_open_temp(fx_directory **out) +{ + char name[16]; + char path[128]; + + while (1) { + z__fx_io_generate_tmp_filename(name, sizeof name); + snprintf(path, sizeof path, "/tmp/%s", name); + fx_path *rpath = fx_path_create_from_cstr(path); + + fx_directory *dir = NULL; + fx_result status = fx_directory_open( + FX_DIRECTORY_ROOT, rpath, FX_DIRECTORY_OPEN_CREATE, &dir); + struct fx_directory_p *p + = fx_object_get_private(dir, FX_TYPE_DIRECTORY); + + if (fx_error_get_status_code(status) == FX_ERR_NAME_EXISTS) { + fx_path_unref(rpath); + continue; + } + + if (dir) { + p->d_flags |= DIRECTORY_DELETE_ON_CLOSE; + } + + fx_path_unlink(rpath); + fx_path_unref(rpath); + + return status; + } +} + +const fx_path *fx_directory_get_path(const fx_directory *dir) +{ + FX_CLASS_DISPATCH_STATIC_0(FX_TYPE_DIRECTORY, directory_get_path, dir); +} + +const fx_path *fx_directory_get_rel_path(const fx_directory *dir) +{ + FX_CLASS_DISPATCH_STATIC_0(FX_TYPE_DIRECTORY, directory_get_rel_path, dir); +} + +const char *fx_directory_get_path_cstr(const fx_directory *dir) +{ + FX_CLASS_DISPATCH_STATIC_0(FX_TYPE_DIRECTORY, directory_get_path_cstr, dir); +} + +const char *fx_directory_get_rel_path_cstr(const fx_directory *dir) +{ + FX_CLASS_DISPATCH_STATIC_0( + FX_TYPE_DIRECTORY, directory_get_rel_path_cstr, dir); +} + +fx_result fx_directory_delete(fx_directory *dir) +{ + struct fx_directory_p *p = fx_object_get_private(dir, FX_TYPE_DIRECTORY); + p->d_flags |= DIRECTORY_DELETE_ON_CLOSE; + /* TODO allow object release functions to return a fx_result value */ + fx_directory_unref(dir); + return FX_RESULT_SUCCESS; +} + +bool fx_directory_path_exists(const fx_directory *root, const fx_path *path) +{ + FX_CLASS_DISPATCH_STATIC( + FX_TYPE_DIRECTORY, directory_path_exists, root, path); +} + +bool fx_directory_path_is_file(const fx_directory *root, const fx_path *path) +{ + FX_CLASS_DISPATCH_STATIC( + FX_TYPE_DIRECTORY, directory_path_is_file, root, path); +} + +bool fx_directory_path_is_directory(const fx_directory *root, const fx_path *path) +{ + FX_CLASS_DISPATCH_STATIC( + FX_TYPE_DIRECTORY, directory_path_is_directory, root, path); +} + +fx_result fx_directory_path_stat( + const fx_directory *root, const fx_path *path, struct fx_file_info *out) +{ + FX_CLASS_DISPATCH_STATIC( + FX_TYPE_DIRECTORY, directory_path_stat, root, path, out); +} + +fx_result fx_directory_path_unlink(const fx_directory *root, const fx_path *path) +{ + FX_CLASS_DISPATCH_STATIC( + FX_TYPE_DIRECTORY, directory_path_unlink, root, path); +} + +/*** VIRTUAL FUNCTIONS ********************************************************/ + +static void directory_init(fx_object *obj, void *priv) +{ + struct fx_directory_p *dir = priv; +} + +static void directory_fini(fx_object *obj, void *priv) +{ + struct fx_directory_p *dir = priv; + + close(dir->d_fd); + + if (dir->d_flags & DIRECTORY_DELETE_ON_CLOSE) { + directory_delete(obj, dir); + } + + fx_path_unref(dir->d_path_abs); +} + +/*** ITERATOR FUNCTIONS *******************************************************/ + +static int ftsent_compare(const FTSENT **one, const FTSENT **two) +{ + return (strcmp((*one)->fts_name, (*two)->fts_name)); +} + +static void update_iterator_data(struct fx_directory_iterator_p *it) +{ + if (it->entry.filepath) { + fx_path_unref((fx_path *)it->entry.filepath); + it->entry.filepath = NULL; + } + + FTSENT *ent = it->ent; + + fx_path *path = fx_path_create_from_cstr( + ent->fts_path + fx_path_length(it->_p->d_path_abs) + 1); + + it->entry.filename = ent->fts_name; + it->entry.filepath = path; + + memset(&it->entry.info, 0x0, sizeof it->entry.info); + + it->entry.info.length = ent->fts_statp->st_size; + + if (S_ISREG(ent->fts_statp->st_mode)) { + it->entry.info.attrib |= FX_FILE_ATTRIB_NORMAL; + } + + if (S_ISDIR(ent->fts_statp->st_mode)) { + it->entry.info.attrib |= FX_FILE_ATTRIB_DIRECTORY; + } + + if (S_ISBLK(ent->fts_statp->st_mode)) { + it->entry.info.attrib |= FX_FILE_ATTRIB_BLOCK_DEVICE; + } + + if (S_ISCHR(ent->fts_statp->st_mode)) { + it->entry.info.attrib |= FX_FILE_ATTRIB_CHAR_DEVICE; + } + + if (S_ISLNK(ent->fts_statp->st_mode)) { + it->entry.info.attrib |= FX_FILE_ATTRIB_SYMLINK; + } +} + +static void iterator_fini(fx_object *obj, void *priv) +{ + struct fx_directory_iterator_p *it = priv; + + if (it->entry.filepath) { + fx_path_unref((fx_path *)it->entry.filepath); + it->entry.filepath = NULL; + } + + if (it->fts) { + fts_close(it->fts); + } +} + +fx_iterator *fx_directory_begin( + fx_directory *directory, enum fx_directory_iterator_flags flags) +{ + fx_iterator *it_obj = fx_object_create(FX_TYPE_DIRECTORY_ITERATOR); + struct fx_directory_iterator_p *it + = fx_object_get_private(it_obj, FX_TYPE_DIRECTORY_ITERATOR); + + it->flags = flags; + it->root = directory; + it->_p = fx_object_get_private(directory, FX_TYPE_DIRECTORY); + + int fts_flags = FTS_COMFOLLOW | FTS_NOCHDIR; + + const char *path_list[] = { + fx_path_ptr(it->_p->d_path_abs), + NULL, + }; + + it->fts = fts_open((char *const *)path_list, fts_flags, ftsent_compare); + + bool done = false; + + while (!done) { + it->ent = fts_read(it->fts); + + if (!it->ent) { + fx_iterator_set_status(it_obj, FX_ERR_NO_DATA); + return it_obj; + } + + if (it->ent->fts_level == 0) { + continue; + } + + switch (it->ent->fts_info) { + case FTS_DOT: + continue; + case FTS_F: + done = true; + break; + case FTS_D: + if (it->flags & FX_DIRECTORY_ITERATE_PARENT_LAST) { + continue; + } + + done = true; + break; + case FTS_DP: + if (it->flags & FX_DIRECTORY_ITERATE_PARENT_FIRST) { + continue; + } + done = true; + break; + default: + done = true; + break; + } + } + + update_iterator_data(it); + return it_obj; +} + +static fx_iterator *iterator_begin(fx_object *obj) +{ + return fx_directory_begin(obj, FX_DIRECTORY_ITERATE_PARENT_FIRST); +} + +static const fx_iterator *iterator_cbegin(const fx_object *obj) +{ + return fx_directory_begin( + (fx_object *)obj, FX_DIRECTORY_ITERATE_PARENT_FIRST); +} + +static enum fx_status iterator_move_next(const fx_iterator *obj) +{ + struct fx_directory_iterator_p *it + = fx_object_get_private(obj, FX_TYPE_DIRECTORY_ITERATOR); + if (!it || !it->fts) { + return FX_ERR_NO_DATA; + } + + bool done = false; + + while (!done) { + it->ent = fts_read(it->fts); + + if (!it->ent) { + return FX_ERR_NO_DATA; + } + + if (it->ent->fts_level == 0) { + continue; + } + + switch (it->ent->fts_info) { + case FTS_DOT: + continue; + case FTS_F: + done = true; + break; + case FTS_D: + if (it->flags & FX_DIRECTORY_ITERATE_PARENT_LAST) { + continue; + } + done = true; + break; + case FTS_DP: + if (it->flags & FX_DIRECTORY_ITERATE_PARENT_FIRST) { + continue; + } + done = true; + break; + default: + done = true; + break; + } + } + + update_iterator_data(it); + return FX_SUCCESS; +} + +static enum fx_status iterator_erase(fx_iterator *obj) +{ + struct fx_directory_iterator_p *it + = fx_object_get_private(obj, FX_TYPE_DIRECTORY_ITERATOR); + fx_result result = fx_directory_path_unlink(it->root, it->entry.filepath); + if (fx_result_is_error(result)) { + enum fx_status status = fx_error_get_status_code(result); + fx_error_discard(result); + return status; + } + + return iterator_move_next(obj); +} + +static fx_iterator_value iterator_get_value(fx_iterator *obj) +{ + struct fx_directory_iterator_p *it + = fx_object_get_private(obj, FX_TYPE_DIRECTORY_ITERATOR); + + return FX_ITERATOR_VALUE_PTR(&it->entry); +} + +static const fx_iterator_value iterator_get_cvalue(const fx_iterator *obj) +{ + struct fx_directory_iterator_p *it + = fx_object_get_private(obj, FX_TYPE_DIRECTORY_ITERATOR); + + return FX_ITERATOR_VALUE_CPTR(&it->entry); +} + +/*** CLASS DEFINITION *********************************************************/ + +// ---- fx_directory DEFINITION +FX_TYPE_CLASS_DEFINITION_BEGIN(fx_directory) +FX_TYPE_CLASS_INTERFACE_BEGIN(fx_object, FX_TYPE_OBJECT) +FX_INTERFACE_ENTRY(to_string) = NULL; +FX_TYPE_CLASS_INTERFACE_END(fx_object, FX_TYPE_OBJECT) + +FX_TYPE_CLASS_INTERFACE_BEGIN(fx_iterable, FX_TYPE_ITERABLE) +FX_INTERFACE_ENTRY(it_begin) = iterator_begin; +FX_INTERFACE_ENTRY(it_cbegin) = iterator_cbegin; +FX_TYPE_CLASS_INTERFACE_END(fx_iterable, FX_TYPE_ITERABLE) +FX_TYPE_CLASS_DEFINITION_END(fx_directory) + +FX_TYPE_DEFINITION_BEGIN(fx_directory) +FX_TYPE_ID(0x10d36546, 0x7f96, 0x464b, 0xbc4d, 0xe504b283fa45); +FX_TYPE_CLASS(fx_directory_class); +FX_TYPE_IMPLEMENTS(FX_TYPE_ITERABLE); +FX_TYPE_INSTANCE_PRIVATE(struct fx_directory_p); +FX_TYPE_INSTANCE_INIT(directory_init); +FX_TYPE_INSTANCE_FINI(directory_fini); +FX_TYPE_DEFINITION_END(fx_directory) + +// ---- fx_directory_iterator DEFINITION +FX_TYPE_CLASS_DEFINITION_BEGIN(fx_directory_iterator) +FX_TYPE_CLASS_INTERFACE_BEGIN(fx_object, FX_TYPE_OBJECT) +FX_INTERFACE_ENTRY(to_string) = NULL; +FX_TYPE_CLASS_INTERFACE_END(fx_object, FX_TYPE_OBJECT) + +FX_TYPE_CLASS_INTERFACE_BEGIN(fx_iterator, FX_TYPE_ITERATOR) +FX_INTERFACE_ENTRY(it_move_next) = iterator_move_next; +FX_INTERFACE_ENTRY(it_erase) = iterator_erase; +FX_INTERFACE_ENTRY(it_get_value) = iterator_get_value; +FX_INTERFACE_ENTRY(it_get_cvalue) = iterator_get_cvalue; +FX_TYPE_CLASS_INTERFACE_END(fx_iterator, FX_TYPE_ITERATOR) +FX_TYPE_CLASS_DEFINITION_END(fx_directory_iterator) + +FX_TYPE_DEFINITION_BEGIN(fx_directory_iterator) +FX_TYPE_ID(0xc707fce6, 0xc895, 0x4925, 0x8700, 0xa60641dee0cc); +FX_TYPE_EXTENDS(FX_TYPE_ITERATOR); +FX_TYPE_CLASS(fx_directory_iterator_class); +FX_TYPE_INSTANCE_PRIVATE(struct fx_directory_iterator_p); +FX_TYPE_DEFINITION_END(fx_directory_iterator) diff --git a/io/sys/linux/file.c b/io/sys/linux/file.c new file mode 100644 index 0000000..1029f6b --- /dev/null +++ b/io/sys/linux/file.c @@ -0,0 +1,574 @@ +#define _POSIX_C_SOURCE 200809L + +#include "misc.h" +#include "posix.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CHECK_FLAG(v, f) (((v) & (f)) == (f)) + +static enum fx_status stream_close(fx_stream *); +static enum fx_status stream_getc(fx_stream *, int *); +static enum fx_status stream_read(fx_stream *, void *, size_t, size_t *); +static enum fx_status stream_write(fx_stream *, const void *, size_t, size_t *); +static enum fx_status stream_seek(fx_stream *, long long, fx_stream_seek_origin); +static enum fx_status stream_tell(const fx_stream *, size_t *); + +/*** PRIVATE DATA *************************************************************/ + +struct fx_file_p { + enum fx_file_mode mode; + int fd; + fx_path *path; +}; + +/*** PRIVATE FUNCTIONS ********************************************************/ + +static unsigned int fx_mode_to_unix_mode(enum fx_file_mode mode) +{ + unsigned int result = 0; + + if (CHECK_FLAG(mode, FX_FILE_READ_WRITE)) { + result |= O_RDWR; + } else if (CHECK_FLAG(mode, FX_FILE_READ_ONLY)) { + result |= O_RDONLY; + } else if (CHECK_FLAG(mode, FX_FILE_WRITE_ONLY)) { + result |= O_WRONLY; + } else { + return (unsigned int)-1; + } + + if (CHECK_FLAG(mode, FX_FILE_TRUNCATE)) { + result |= O_TRUNC; + } + + if (CHECK_FLAG(mode, FX_FILE_CREATE)) { + result |= O_CREAT; + } + + if (CHECK_FLAG(mode, FX_FILE_CREATE_ONLY)) { + result |= O_EXCL; + } + + return result; +} + +static fx_result file_open_shadow( + struct fx_file_p *original, enum fx_file_mode mode, fx_file **out) +{ + mode |= FX_FILE_SHADOW | FX_FILE_DELETE_ON_CLOSE | FX_FILE_CREATE; + + fx_path *dir; + fx_path_get_directory(original->path, &dir); + + fx_string *filename = fx_string_create(); + fx_path_get_filename(original->path, filename); + + fx_string_prepend_cstr(filename, ".~"); + + fx_path *shadow_filename = fx_path_create_from_cstr(fx_string_get_cstr(filename)); + fx_string_unref(filename); + + const fx_path *parts[] = { + dir, + shadow_filename, + }; + + fx_path *shadow_filepath + = fx_path_join(parts, sizeof parts / sizeof parts[0]); + fx_path_unref(dir); + fx_path_unref(shadow_filename); + + if (fx_path_exists(shadow_filepath)) { + fx_path_unlink(shadow_filepath); + } + + fx_file *shadow_file; + fx_result status = fx_file_open( + FX_DIRECTORY_ROOT, shadow_filepath, mode, &shadow_file); + fx_path_unref(shadow_filepath); + + if (fx_result_is_error(status)) { + return status; + } + + *out = shadow_file; + return FX_RESULT_SUCCESS; +} + +static const fx_path *file_path(const struct fx_file_p *file) +{ + return file->path; +} + +static enum fx_status file_stat(struct fx_file_p *file, struct fx_file_info *out) +{ + struct stat st; + int err = fstat(file->fd, &st); + + if (err != 0) { + return fx_status_from_errno(errno, FX_ERR_IO_FAILURE); + } + + memset(out, 0x0, sizeof *out); + + return fx_file_info_from_stat(&st, out); +} + +static enum fx_status file_size(struct fx_file_p *file, size_t *out_len) +{ + off_t cur = lseek(file->fd, 0, SEEK_CUR); + if (cur == (off_t)-1) { + return fx_status_from_errno(errno, FX_ERR_IO_FAILURE); + } + + off_t len = lseek(file->fd, 0, SEEK_END); + if (len == (off_t)-1) { + return fx_status_from_errno(errno, FX_ERR_IO_FAILURE); + } + + cur = lseek(file->fd, cur, SEEK_SET); + if (cur == (off_t)-1) { + return fx_status_from_errno(errno, FX_ERR_IO_FAILURE); + } + + *out_len = (size_t)len; + return FX_SUCCESS; +} + +static enum fx_status file_cursor(struct fx_file_p *file, size_t *out_pos) +{ + off_t cur = lseek(file->fd, 0, SEEK_CUR); + if (cur == (off_t)-1) { + return fx_status_from_errno(errno, FX_ERR_IO_FAILURE); + } + + *out_pos = (size_t)cur; + return FX_SUCCESS; +} + +static enum fx_status file_resize(struct fx_file_p *file, size_t len) +{ + int err = ftruncate(file->fd, len); + if (err == 0) { + return FX_SUCCESS; + } + + return fx_status_from_errno(errno, FX_ERR_IO_FAILURE); +} + +static enum fx_status file_seek( + struct fx_file_p *file, long long offset, enum fx_seek_basis basis) +{ + int whence; + switch (basis) { + case FX_SEEK_BEGINNING: + whence = SEEK_SET; + break; + case FX_SEEK_CURRENT: + whence = SEEK_CUR; + break; + case FX_SEEK_END: + whence = SEEK_END; + break; + default: + return FX_ERR_INVALID_ARGUMENT; + } + + int err = lseek(file->fd, offset, whence); + if (err == (off_t)-1) { + return fx_status_from_errno(errno, FX_ERR_IO_FAILURE); + } + + return FX_SUCCESS; +} + +static enum fx_status file_swap_shadow( + struct fx_file_p *main_file, struct fx_file_p *shadow_file) +{ + if (main_file->mode & FX_FILE_SHADOW) { + return FX_ERR_NOT_SUPPORTED; + } + + if (!(shadow_file->mode & FX_FILE_SHADOW)) { + return FX_ERR_NOT_SUPPORTED; + } + + fx_path *dir_path; + fx_path_get_directory(main_file->path, &dir_path); + + fx_path *tmp_path = NULL; + + while (1) { + char tmp_name[16]; + z__fx_io_generate_tmp_filename(tmp_name, sizeof tmp_name); + fx_path *tmp_name_p = fx_path_create_from_cstr(tmp_name); + + const fx_path *parts[] = { + dir_path, + tmp_name_p, + }; + + tmp_path = fx_path_join(parts, sizeof parts / sizeof parts[0]); + + fx_path_unref(tmp_name_p); + + if (!fx_path_exists(tmp_path)) { + break; + } + + fx_path_unref(tmp_path); + tmp_path = NULL; + } + + fx_path_unref(dir_path); + + int err; + + err = rename(fx_path_ptr(main_file->path), fx_path_ptr(tmp_path)); + err = rename(fx_path_ptr(shadow_file->path), fx_path_ptr(main_file->path)); + err = rename(fx_path_ptr(tmp_path), fx_path_ptr(shadow_file->path)); + + fx_path_unref(tmp_path); + + int fd = main_file->fd; + main_file->fd = shadow_file->fd; + shadow_file->fd = fd; + + return FX_SUCCESS; +} + +static enum fx_status file_read( + struct fx_file_p *file, size_t offset, size_t len, void *buf, size_t *nr_read) +{ + if (offset != FX_OFFSET_CURRENT) { + lseek(file->fd, offset, SEEK_SET); + } + + long r = read(file->fd, buf, len); + + enum fx_status status = FX_SUCCESS; + + if (r < 0) { + status = fx_status_from_errno(errno, FX_ERR_IO_FAILURE); + } + + *nr_read = r; + return status; +} + +static enum fx_status file_write( + struct fx_file_p *file, size_t offset, size_t len, const void *buf, + size_t *nr_written) +{ + if (offset != FX_OFFSET_CURRENT) { + lseek(file->fd, offset, SEEK_SET); + } + + long w = write(file->fd, buf, len); + + enum fx_status status = FX_SUCCESS; + + if (w < 0) { + status = fx_status_from_errno(errno, FX_ERR_IO_FAILURE); + } + + *nr_written = w; + return status; +} + +/*** STREAM FUNCTIONS *********************************************************/ + +static enum fx_status stream_close(fx_stream *stream) +{ + return FX_SUCCESS; +} + +static enum fx_status stream_getc(fx_stream *stream, int *out) +{ + struct fx_file_p *file = fx_object_get_private(stream, FX_TYPE_FILE); + char c; + size_t nr_read = 0; + + enum fx_status status + = file_read(file, FX_OFFSET_CURRENT, sizeof c, &c, &nr_read); + if (status != FX_SUCCESS) { + return status; + } + + if (nr_read == 0) { + return FX_ERR_NO_DATA; + } + + *out = c; + return FX_SUCCESS; +} + +static enum fx_status stream_read( + fx_stream *stream, void *buf, size_t max, size_t *nr_read) +{ + struct fx_file_p *file = fx_object_get_private(stream, FX_TYPE_FILE); + + enum fx_status status + = file_read(file, FX_OFFSET_CURRENT, max, buf, nr_read); + + return status; +} + +static enum fx_status stream_write( + fx_stream *stream, const void *buf, size_t count, size_t *nr_written) +{ + struct fx_file_p *file = fx_object_get_private(stream, FX_TYPE_FILE); + + enum fx_status status + = file_write(file, FX_OFFSET_CURRENT, count, buf, nr_written); + + return status; +} + +static enum fx_status stream_seek( + fx_stream *stream, long long offset, fx_stream_seek_origin origin) +{ + fx_seek_basis basis; + switch (origin) { + case FX_STREAM_SEEK_START: + basis = FX_SEEK_BEGINNING; + break; + case FX_STREAM_SEEK_CURRENT: + basis = FX_SEEK_CURRENT; + break; + case FX_STREAM_SEEK_END: + basis = FX_SEEK_END; + break; + default: + return FX_ERR_INVALID_ARGUMENT; + } + + struct fx_file_p *file = fx_object_get_private(stream, FX_TYPE_FILE); + + return file_seek(file, offset, basis); +} + +static enum fx_status stream_tell(const fx_stream *stream, size_t *pos) +{ + const struct fx_file_p *file = fx_object_get_private(stream, FX_TYPE_FILE); + off_t v = lseek(file->fd, 0, SEEK_CUR); + if (v == (off_t)-1) { + return fx_status_from_errno(errno, FX_ERR_IO_FAILURE); + } + + *pos = v; + return FX_SUCCESS; +} + +/*** PUBLIC FUNCTIONS *********************************************************/ + +fx_result fx_file_open( + fx_directory *root, const fx_path *path, enum fx_file_mode mode, fx_file **out) +{ + const fx_path *file_path = path; + unsigned int flags = fx_mode_to_unix_mode(mode); + + if (flags == (unsigned int)-1) { + return FX_RESULT_ERR(INVALID_ARGUMENT); + } + + const fx_path *root_path = NULL; + bool free_root_path = false; + if (root) { + root_path = fx_directory_get_path(root); + } else { + root_path = fx_path_create_cwd(); + free_root_path = true; + } + + const fx_path *parts[] = { + root_path, + file_path, + }; + + fx_path *abs_path = fx_path_join(parts, sizeof parts / sizeof parts[0]); + + if (free_root_path) { + fx_path_unref((fx_path *)root_path); + } + + if (!abs_path) { + return FX_RESULT_ERR(NO_MEMORY); + } + + int fd = open(fx_path_ptr(abs_path), flags, 0644); + if (fd == -1) { + fx_path_unref(abs_path); + return FX_RESULT_STATUS( + fx_status_from_errno(errno, FX_ERR_IO_FAILURE)); + } + + fx_file *file = fx_object_create(FX_TYPE_FILE); + if (!file) { + close(fd); + fx_path_unref(abs_path); + return FX_RESULT_ERR(NO_MEMORY); + } + + struct fx_file_p *p = fx_object_get_private(file, FX_TYPE_FILE); + fx_stream_cfg *cfg = fx_object_get_protected(file, FX_TYPE_STREAM); + + if (mode & FX_FILE_READ_ONLY) { + cfg->s_mode |= FX_STREAM_READ; + } + + if (mode & FX_FILE_WRITE_ONLY) { + cfg->s_mode |= FX_STREAM_WRITE; + } + + if (mode & FX_FILE_BINARY) { + cfg->s_mode |= FX_STREAM_BINARY; + } + + p->fd = fd; + p->path = abs_path; + p->mode = mode; + + *out = file; + return FX_RESULT_SUCCESS; +} + +fx_result fx_file_open_temp(enum fx_file_mode mode, fx_file **out) +{ + mode |= FX_FILE_DELETE_ON_CLOSE; + + char name[16]; + char path[128]; + + while (1) { + z__fx_io_generate_tmp_filename(name, sizeof name); + snprintf(path, sizeof path, "/tmp/%s", name); + fx_path *rpath = fx_path_create_from_cstr(path); + + fx_result status = fx_file_open( + FX_DIRECTORY_ROOT, rpath, mode | FX_FILE_CREATE_ONLY, out); + + if (fx_error_get_status_code(status) == FX_ERR_NAME_EXISTS) { + fx_path_unref(rpath); + continue; + } + + fx_path_unlink(rpath); + fx_path_unref(rpath); + + return status; + } +} + +fx_result fx_file_open_shadow(fx_file *original, enum fx_file_mode mode, fx_file **out) +{ + FX_CLASS_DISPATCH_STATIC(FX_TYPE_FILE, file_open_shadow, original, mode, out); +} + +const fx_path *fx_file_path(const fx_file *file) +{ + FX_CLASS_DISPATCH_STATIC_0(FX_TYPE_FILE, file_path, file); +} + +enum fx_status fx_file_stat(fx_file *file, struct fx_file_info *out) +{ + FX_CLASS_DISPATCH_STATIC(FX_TYPE_FILE, file_stat, file, out); +} + +enum fx_status fx_file_size(fx_file *file, size_t *out_len) +{ + FX_CLASS_DISPATCH_STATIC(FX_TYPE_FILE, file_size, file, out_len); +} + +enum fx_status fx_file_cursor(fx_file *file, size_t *out_pos) +{ + FX_CLASS_DISPATCH_STATIC(FX_TYPE_FILE, file_cursor, file, out_pos); +} + +enum fx_status fx_file_resize(fx_file *file, size_t len) +{ + FX_CLASS_DISPATCH_STATIC(FX_TYPE_FILE, file_resize, file, len); +} + +enum fx_status fx_file_seek(fx_file *file, long long offset, enum fx_seek_basis basis) +{ + FX_CLASS_DISPATCH_STATIC(FX_TYPE_FILE, file_seek, file, offset, basis); +} + +enum fx_status fx_file_swap_shadow(fx_file *main_file, fx_file *shadow_file) +{ + struct fx_file_p *main_p = fx_object_get_private(main_file, FX_TYPE_FILE); + struct fx_file_p *shadow_p = fx_object_get_private(main_file, FX_TYPE_FILE); + return file_swap_shadow(main_p, shadow_p); +} + +enum fx_status fx_file_read( + fx_file *file, size_t offset, size_t len, void *buf, size_t *nr_read) +{ + FX_CLASS_DISPATCH_STATIC( + FX_TYPE_FILE, file_read, file, offset, len, buf, nr_read); +} + +enum fx_status fx_file_write( + fx_file *file, size_t offset, size_t len, const void *buf, size_t *nr_written) +{ + FX_CLASS_DISPATCH_STATIC( + FX_TYPE_FILE, file_write, file, offset, len, buf, nr_written); +} + +/*** VIRTUAL FUNCTIONS ********************************************************/ + +static void file_init(fx_object *obj, void *priv) +{ + struct fx_file_p *file = priv; +} + +static void file_fini(fx_object *obj, void *priv) +{ + struct fx_file_p *file = priv; + close(file->fd); + + if (file->mode & FX_FILE_DELETE_ON_CLOSE) { + fx_path_unlink(file->path); + } + + fx_path_unref(file->path); +} + +/*** CLASS DEFINITION *********************************************************/ + +FX_TYPE_CLASS_DEFINITION_BEGIN(fx_file) + FX_TYPE_CLASS_INTERFACE_BEGIN(fx_object, FX_TYPE_OBJECT) + FX_INTERFACE_ENTRY(to_string) = NULL; + FX_TYPE_CLASS_INTERFACE_END(fx_object, FX_TYPE_OBJECT) + + FX_TYPE_CLASS_INTERFACE_BEGIN(fx_stream, FX_TYPE_STREAM) + FX_INTERFACE_ENTRY(s_close) = stream_close; + FX_INTERFACE_ENTRY(s_getc) = stream_getc; + FX_INTERFACE_ENTRY(s_read) = stream_read; + FX_INTERFACE_ENTRY(s_write) = stream_write; + FX_INTERFACE_ENTRY(s_seek) = stream_seek; + FX_INTERFACE_ENTRY(s_tell) = stream_tell; + FX_TYPE_CLASS_INTERFACE_END(fx_stream, FX_TYPE_STREAM) +FX_TYPE_CLASS_DEFINITION_END(fx_file) + +FX_TYPE_DEFINITION_BEGIN(fx_file) + FX_TYPE_ID(0x495a73f6, 0xb8c3, 0x4e17, 0xb5f4, 0x6fc321f67c7b); + FX_TYPE_EXTENDS(FX_TYPE_STREAM); + FX_TYPE_CLASS(fx_file_class); + FX_TYPE_INSTANCE_PRIVATE(struct fx_file_p); + FX_TYPE_INSTANCE_INIT(file_init); + FX_TYPE_INSTANCE_FINI(file_fini); +FX_TYPE_DEFINITION_END(fx_file) diff --git a/io/sys/linux/misc.c b/io/sys/linux/misc.c new file mode 100644 index 0000000..b70ab49 --- /dev/null +++ b/io/sys/linux/misc.c @@ -0,0 +1,21 @@ +#include "misc.h" + +#include + +void z__fx_io_generate_tmp_filename(char *out, size_t len) +{ + static const char *alphabet + = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + "01234567" + "89+=-_."; + static const size_t alphabet_len = 67; + + fx_random_ctx *ctx = fx_random_global_ctx(); + + for (size_t i = 0; i < len; i++) { + int v = fx_random_next_int64(ctx) % alphabet_len; + out[i] = alphabet[v]; + } + + out[len - 1] = 0; +} diff --git a/io/sys/linux/misc.h b/io/sys/linux/misc.h new file mode 100644 index 0000000..303affe --- /dev/null +++ b/io/sys/linux/misc.h @@ -0,0 +1,8 @@ +#ifndef _IO_DARWIN_MISC_H_ +#define _IO_DARWIN_MISC_H_ + +#include + +extern void z__fx_io_generate_tmp_filename(char *out, size_t len); + +#endif diff --git a/io/sys/linux/path.c b/io/sys/linux/path.c new file mode 100644 index 0000000..7f49223 --- /dev/null +++ b/io/sys/linux/path.c @@ -0,0 +1,399 @@ +#include "posix.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/*** PRIVATE DATA *************************************************************/ + +struct fx_path_p { + fx_string *p_pathstr; +}; + +/*** PRIVATE FUNCTIONS ********************************************************/ + +static fx_path *path_make_absolute(const struct fx_path_p *in) +{ + return NULL; +} + +static fx_path *path_make_relative(const struct fx_path_p *in) +{ + return NULL; +} + +static fx_path *path_make_canonical(const struct fx_path_p *in) +{ + return NULL; +} + +static bool path_is_absolute(const struct fx_path_p *path) +{ + const char *s = fx_string_get_cstr(path->p_pathstr); + return s[0] == '/'; +} + +static const char *path_ptr(const struct fx_path_p *path) +{ + return fx_string_get_cstr(path->p_pathstr); +} + +static enum fx_status path_stat(const struct fx_path_p *path, struct fx_file_info *out) +{ + struct stat st; + int err = stat(path_ptr(path), &st); + + if (err != 0) { + return fx_status_from_errno(errno, FX_ERR_IO_FAILURE); + } + + memset(out, 0x0, sizeof *out); + + return fx_file_info_from_stat(&st, out); +} + +static bool path_exists(const struct fx_path_p *path) +{ + fx_file_info info; + if (!FX_OK(path_stat(path, &info))) { + return false; + } + + return true; +} + +static bool path_is_file(const struct fx_path_p *path) +{ + fx_file_info info; + if (!FX_OK(path_stat(path, &info))) { + return false; + } + + return (info.attrib & FX_FILE_ATTRIB_NORMAL) != 0; +} + +static bool path_is_directory(const struct fx_path_p *path) +{ + fx_file_info info; + if (!FX_OK(path_stat(path, &info))) { + return false; + } + + return (info.attrib & FX_FILE_ATTRIB_DIRECTORY) != 0; +} + +static void append_path(struct fx_path_p *dest, const struct fx_path_p *src) +{ + if (path_is_absolute(src)) { + fx_string_clear(dest->p_pathstr); + fx_string_append_s(dest->p_pathstr, src->p_pathstr); + return; + } + + if (fx_string_get_size(dest->p_pathstr, FX_STRLEN_NORMAL) > 0 + && fx_string_get_last_char(dest->p_pathstr) != '/' + && fx_string_get_first_char(src->p_pathstr) != '/') { + char s[] = {'/', 0}; + fx_string_append_cstr(dest->p_pathstr, s); + } + + fx_string_append_s(dest->p_pathstr, src->p_pathstr); +} + +static enum fx_status path_unlink(const struct fx_path_p *path) +{ + int err = remove(fx_string_get_cstr(path->p_pathstr)); + return err == 0 ? FX_SUCCESS : fx_status_from_errno(errno, FX_ERR_IO_FAILURE); +} + +static enum fx_status path_get_directory( + const struct fx_path_p *path, fx_path **out_dir_path) +{ + fx_string *path_str = path->p_pathstr; + long len = fx_string_get_size(path_str, FX_STRLEN_NORMAL); + + const char *path_cstr = fx_string_get_cstr(path_str); + char *sep = strrchr(path_cstr, '/'); + + if (!sep) { + *out_dir_path = fx_path_create(); + return FX_SUCCESS; + } + + size_t dir_path_len = (size_t)(sep - path_cstr); + fx_string *dir_path_s = fx_string_get_substr(path_str, 0, dir_path_len); + + fx_path *dir_path = fx_path_create_from_cstr(fx_string_get_cstr(dir_path_s)); + fx_string_unref(dir_path_s); + + *out_dir_path = dir_path; + + return FX_SUCCESS; +} + +static enum fx_status path_get_filename( + const struct fx_path_p *path, fx_string *out_name) +{ + fx_string *path_str = path->p_pathstr; + long len = fx_string_get_size(path_str, FX_STRLEN_NORMAL); + + char *sep = strrchr(fx_string_get_cstr(path_str), '/'); + + if (!sep) { + fx_string_append_s(out_name, path_str); + return FX_SUCCESS; + } + + const char *filename = sep; + while (*filename == '/' && *filename != '\0') { + filename++; + } + + if (*filename == '\0') { + fx_string_clear(out_name); + return FX_SUCCESS; + } + + fx_string_append_cstr(out_name, filename); + + return FX_SUCCESS; +} + +static size_t path_length(const struct fx_path_p *path) +{ + return fx_string_get_size(path->p_pathstr, FX_STRLEN_NORMAL); +} + +/*** PUBLIC FUNCTIONS *********************************************************/ + +fx_path *fx_path_create_root() +{ + const char *system_drive = "/"; + size_t system_drive_len = strlen(system_drive); + + fx_path *path = fx_path_create(); + if (!path) { + return NULL; + } + + struct fx_path_p *p = fx_object_get_private(path, FX_TYPE_PATH); + + fx_string_append_cstr(p->p_pathstr, system_drive); + if (system_drive[system_drive_len - 1] != '\\') { + fx_string_append_c(p->p_pathstr, '\\'); + } + + return path; +} + +fx_path *fx_path_create_cwd() +{ + const long buf_len = 2048; + char *buf = malloc(buf_len); + if (!buf) { + return NULL; + } + + if (!getcwd(buf, buf_len)) { + free(buf); + return NULL; + } + + fx_path *path = fx_path_create(); + if (!path) { + free(buf); + return NULL; + } + + struct fx_path_p *p = fx_object_get_private(path, FX_TYPE_PATH); + + fx_string_append_cstr(p->p_pathstr, buf); + free(buf); + + return path; +} + +fx_path *fx_path_create_from_cstr(const char *cstr) +{ + fx_path *path = fx_path_create(); + if (!path) { + return NULL; + } + + struct fx_path_p *p = fx_object_get_private(path, FX_TYPE_PATH); + + char prev = 0; + + for (size_t i = 0; cstr[i]; i++) { + char c = cstr[i]; + if (c == '\\') { + c = '/'; + } + + if (prev == '/' && c == '/') { + continue; + } + + fx_string_append_c(p->p_pathstr, c); + prev = c; + } + + while (fx_string_get_last_char(p->p_pathstr) == '/') { + fx_string_pop_back(p->p_pathstr); + } + + return path; +} + +fx_path *fx_path_duplicate(const fx_path *path) +{ + fx_path *new_path = fx_path_create(); + if (!path) { + return NULL; + } + + struct fx_path_p *old_p = fx_object_get_private(path, FX_TYPE_PATH); + struct fx_path_p *new_p = fx_object_get_private(new_path, FX_TYPE_PATH); + + new_p->p_pathstr = fx_string_duplicate(old_p->p_pathstr); + if (!new_p->p_pathstr) { + fx_path_unref(new_path); + return NULL; + } + + return new_path; +} + +fx_path *fx_path_join(const fx_path *paths[], size_t nr_paths) +{ + fx_path *result = fx_path_create(); + if (!result) { + return NULL; + } + + struct fx_path_p *result_p = fx_object_get_private(result, FX_TYPE_PATH); + + for (size_t i = 0; i < nr_paths; i++) { + if (paths[i]) { + struct fx_path_p *path_p + = fx_object_get_private(paths[i], FX_TYPE_PATH); + append_path(result_p, path_p); + } + } + + return result; +} + +fx_path *fx_path_make_absolute(const fx_path *in) +{ + FX_CLASS_DISPATCH_STATIC_0(FX_TYPE_PATH, path_make_absolute, in); +} + +fx_path *fx_path_make_relative(const fx_path *in) +{ + FX_CLASS_DISPATCH_STATIC_0(FX_TYPE_PATH, path_make_relative, in); +} + +fx_path *fx_path_make_canonical(const fx_path *in) +{ + FX_CLASS_DISPATCH_STATIC_0(FX_TYPE_PATH, path_make_canonical, in); +} + +bool fx_path_is_absolute(const fx_path *path) +{ + FX_CLASS_DISPATCH_STATIC_0(FX_TYPE_PATH, path_is_absolute, path); +} + +bool fx_path_exists(const fx_path *path) +{ + FX_CLASS_DISPATCH_STATIC_0(FX_TYPE_PATH, path_exists, path); +} + +bool fx_path_is_file(const fx_path *path) +{ + FX_CLASS_DISPATCH_STATIC_0(FX_TYPE_PATH, path_is_file, path); +} + +bool fx_path_is_directory(const fx_path *path) +{ + FX_CLASS_DISPATCH_STATIC_0(FX_TYPE_PATH, path_is_directory, path); +} + +enum fx_status fx_path_stat(const fx_path *path, struct fx_file_info *out) +{ + FX_CLASS_DISPATCH_STATIC(FX_TYPE_PATH, path_stat, path, out); +} + +enum fx_status fx_path_unlink(const fx_path *path) +{ + FX_CLASS_DISPATCH_STATIC_0(FX_TYPE_PATH, path_unlink, path); +} + +enum fx_status fx_path_get_directory(const fx_path *path, fx_path **out_dir_path) +{ + FX_CLASS_DISPATCH_STATIC(FX_TYPE_PATH, path_get_directory, path, out_dir_path); +} + +enum fx_status fx_path_get_filename(const fx_path *path, fx_string *out_name) +{ + FX_CLASS_DISPATCH_STATIC(FX_TYPE_PATH, path_get_filename, path, out_name); +} + +const char *fx_path_ptr(const fx_path *path) +{ + FX_CLASS_DISPATCH_STATIC_0(FX_TYPE_PATH, path_ptr, path); +} + +size_t fx_path_length(const fx_path *path) +{ + FX_CLASS_DISPATCH_STATIC_0(FX_TYPE_PATH, path_length, path); +} + +/*** VIRTUAL FUNCTIONS ********************************************************/ + +static void path_init(fx_object *obj, void *priv) +{ + struct fx_path_p *path = priv; + + path->p_pathstr = fx_string_create(); + if (!path->p_pathstr) { + /* TODO return error */ + } +} + +void path_fini(fx_object *obj, void *priv) +{ + struct fx_path_p *path = priv; + + fx_string_unref(path->p_pathstr); +} + +void path_to_string(const fx_object *obj, fx_stream *out) +{ + struct fx_path_p *path = fx_object_get_private(obj, FX_TYPE_PATH); + + fx_stream_write_cstr(out, fx_string_get_cstr(path->p_pathstr), NULL); +} + +/*** CLASS DEFINITION *********************************************************/ + +FX_TYPE_CLASS_DEFINITION_BEGIN(fx_path) + FX_TYPE_CLASS_INTERFACE_BEGIN(fx_object, FX_TYPE_OBJECT) + FX_INTERFACE_ENTRY(to_string) = path_to_string; + FX_TYPE_CLASS_INTERFACE_END(fx_object, FX_TYPE_OBJECT) +FX_TYPE_CLASS_DEFINITION_END(fx_path) + +FX_TYPE_DEFINITION_BEGIN(fx_path) + FX_TYPE_ID(0x56dc32eb, 0xea96, 0x46ed, 0x85d3, 0x760fa4ad61f4); + FX_TYPE_CLASS(fx_path_class); + FX_TYPE_INSTANCE_PRIVATE(struct fx_path_p); + FX_TYPE_INSTANCE_INIT(path_init); + FX_TYPE_INSTANCE_FINI(path_fini); +FX_TYPE_DEFINITION_END(fx_path) diff --git a/io/sys/linux/posix.c b/io/sys/linux/posix.c new file mode 100644 index 0000000..5a8805c --- /dev/null +++ b/io/sys/linux/posix.c @@ -0,0 +1,131 @@ +#include +#include +#include +#include +#include + +enum fx_status fx_status_from_errno(int error, enum fx_status default_value) +{ + switch (error) { + case 0: + return FX_SUCCESS; + case ENOENT: + return FX_ERR_NO_ENTRY; + case EEXIST: + return FX_ERR_NAME_EXISTS; + case ENOMEM: + return FX_ERR_NO_MEMORY; + case EINVAL: + return FX_ERR_INVALID_ARGUMENT; + case EIO: + return FX_ERR_IO_FAILURE; + case EISDIR: + return FX_ERR_IS_DIRECTORY; + case ENOTDIR: + return FX_ERR_NOT_DIRECTORY; + case EPERM: + case EACCES: + return FX_ERR_PERMISSION_DENIED; + case ENOTSUP: + case ENOSYS: + return FX_ERR_NOT_SUPPORTED; + default: + return default_value; + } +} + +fx_result fx_result_from_errno_with_filepath( + int error, const char *path, enum fx_status default_value) +{ + switch (error) { + case 0: + return FX_RESULT_SUCCESS; + case ENOENT: + return FX_RESULT_STATUS_WITH_STRING( + FX_ERR_NO_ENTRY, "Path @i{%s} does not exist", path); + case ENOTDIR: + return FX_RESULT_STATUS_WITH_STRING( + FX_ERR_NOT_DIRECTORY, "Path @i{%s} is not a directory", + path); + case EISDIR: + return FX_RESULT_STATUS_WITH_STRING( + FX_ERR_IS_DIRECTORY, "Path @i{%s} is a directory", path); + case EPERM: + case EACCES: + return FX_RESULT_STATUS_WITH_STRING( + FX_ERR_PERMISSION_DENIED, + "Permission denied while accessing path @i{%s}", path); + default: + return FX_RESULT_STATUS(fx_status_from_errno(error, default_value)); + } + + return FX_RESULT_SUCCESS; +} + +fx_result fx_result_from_errno_with_subfilepath( + int error, const char *path, const char *dir_path, + enum fx_status default_value) +{ + if (!dir_path) { + return fx_result_propagate(fx_result_from_errno_with_filepath( + error, path, default_value)); + } + + switch (error) { + case 0: + return FX_RESULT_SUCCESS; + case ENOENT: + return FX_RESULT_STATUS_WITH_STRING( + FX_ERR_NO_ENTRY, + "Path @i{%s} in directory @i{%s} does not exist", path, + dir_path); + case ENOTDIR: + return FX_RESULT_STATUS_WITH_STRING( + FX_ERR_NOT_DIRECTORY, + "Path @i{%s} in directory @i{%s} is not a directory", + path, dir_path); + case EISDIR: + return FX_RESULT_STATUS_WITH_STRING( + FX_ERR_IS_DIRECTORY, + "Path @i{%s} in directory @i{%s} is a directory", path, + dir_path); + case EPERM: + case EACCES: + return FX_RESULT_STATUS_WITH_STRING( + FX_ERR_PERMISSION_DENIED, + "Permission denied while accessing path @i{%s} in " + "directory @i{%s}", + path, dir_path); + default: + return FX_RESULT_STATUS(fx_status_from_errno(error, default_value)); + } + + return FX_RESULT_SUCCESS; +} + +enum fx_status fx_file_info_from_stat(const struct stat *st, struct fx_file_info *out) +{ + out->length = st->st_size; + + if (S_ISREG(st->st_mode)) { + out->attrib |= FX_FILE_ATTRIB_NORMAL; + } + + if (S_ISDIR(st->st_mode)) { + out->attrib |= FX_FILE_ATTRIB_DIRECTORY; + } + + if (S_ISBLK(st->st_mode)) { + out->attrib |= FX_FILE_ATTRIB_BLOCK_DEVICE; + } + + if (S_ISCHR(st->st_mode)) { + out->attrib |= FX_FILE_ATTRIB_CHAR_DEVICE; + } + + if (S_ISLNK(st->st_mode)) { + out->attrib |= FX_FILE_ATTRIB_SYMLINK; + } + + return FX_SUCCESS; +} diff --git a/io/sys/linux/posix.h b/io/sys/linux/posix.h new file mode 100644 index 0000000..620e651 --- /dev/null +++ b/io/sys/linux/posix.h @@ -0,0 +1,19 @@ +#ifndef _IO_DARWIN_POSIX_H_ +#define _IO_DARWIN_POSIX_H_ + +#include +#include + +struct stat; +struct fx_file_info; + +extern enum fx_status fx_status_from_errno(int error, enum fx_status default_value); +extern fx_result fx_result_from_errno_with_filepath( + int error, const char *path, enum fx_status default_value); +extern fx_result fx_result_from_errno_with_subfilepath( + int error, const char *path, const char *dir_path, + enum fx_status default_value); +extern enum fx_status fx_file_info_from_stat( + const struct stat *in, struct fx_file_info *out); + +#endif diff --git a/io/sys/windows/path.c b/io/sys/windows/path.c index df9ce08..dcf77c7 100644 --- a/io/sys/windows/path.c +++ b/io/sys/windows/path.c @@ -123,7 +123,7 @@ struct fx_path *fx_path_create_from_cstr(const char *cstr) prev = c; } - while (fx_string_back(path->pathstr) == '/') { + while (fx_string_get_last_char(path->pathstr) == '/') { fx_string_pop_back(path->pathstr); } @@ -138,8 +138,8 @@ static void append_path(struct fx_path *dest, const struct fx_path *src) return; } - if (fx_string_back(dest->pathstr) != '/' - && fx_string_front(src->pathstr) != '/') { + if (fx_string_get_last_char(dest->pathstr) != '/' + && fx_string_get_first_char(src->pathstr) != '/') { char s[] = {'/', 0}; fx_string_append_cstr(dest->pathstr, s); } @@ -181,7 +181,7 @@ struct fx_path *fx_path_make_canonical(const struct fx_path *in) bool fx_path_is_absolute(const struct fx_path *path) { - const char *s = fx_string_ptr(path->pathstr); + const char *s = fx_string_get_cstr(path->pathstr); size_t i; for (i = 0; s[i]; i++) { @@ -231,7 +231,7 @@ bool fx_path_is_directory(const struct fx_path *path) const char *fx_path_ptr(const struct fx_path *path) { - return fx_string_ptr(path->pathstr); + return fx_string_get_cstr(path->pathstr); } void path_release(struct fx_dsref* obj) @@ -244,5 +244,5 @@ void path_to_string(struct fx_dsref* obj, struct fx_stringstream* out) { struct fx_path *path = (struct fx_path *)obj; - fx_stringstream_add(out, fx_string_ptr(path->pathstr)); + fx_stringstream_add(out, fx_string_get_cstr(path->pathstr)); } \ No newline at end of file