From cb970010330aff8b590fdd8add9748990eed3b4f Mon Sep 17 00:00:00 2001 From: Max Wash Date: Sun, 19 Jul 2026 13:25:53 +0100 Subject: [PATCH] fx.io: add basic rosetta support --- fx.io/sys/apple/file.c | 2 - fx.io/sys/rosetta/directory.c | 759 ++++++++++++++++++++++++++++++++++ fx.io/sys/rosetta/file.c | 653 +++++++++++++++++++++++++++++ fx.io/sys/rosetta/misc.c | 21 + fx.io/sys/rosetta/misc.h | 8 + fx.io/sys/rosetta/path.c | 486 ++++++++++++++++++++++ fx.io/sys/rosetta/pipe.c | 32 ++ fx.io/sys/rosetta/posix.c | 152 +++++++ fx.io/sys/rosetta/posix.h | 26 ++ fx.io/sys/rosetta/stream.c | 149 +++++++ 10 files changed, 2286 insertions(+), 2 deletions(-) create mode 100644 fx.io/sys/rosetta/directory.c create mode 100644 fx.io/sys/rosetta/file.c create mode 100644 fx.io/sys/rosetta/misc.c create mode 100644 fx.io/sys/rosetta/misc.h create mode 100644 fx.io/sys/rosetta/path.c create mode 100644 fx.io/sys/rosetta/pipe.c create mode 100644 fx.io/sys/rosetta/posix.c create mode 100644 fx.io/sys/rosetta/posix.h create mode 100644 fx.io/sys/rosetta/stream.c diff --git a/fx.io/sys/apple/file.c b/fx.io/sys/apple/file.c index 849e462..3f1d7ea 100644 --- a/fx.io/sys/apple/file.c +++ b/fx.io/sys/apple/file.c @@ -433,8 +433,6 @@ fx_result fx_file_open( free_root_path = true; } - const fx_value *parts[] = {}; - fx_path *abs_path = fx_path_join_list(2, &FX_VALUE_OBJECT(root_path), &path); diff --git a/fx.io/sys/rosetta/directory.c b/fx.io/sys/rosetta/directory.c new file mode 100644 index 0000000..a916adc --- /dev/null +++ b/fx.io/sys/rosetta/directory.c @@ -0,0 +1,759 @@ +#define _POSIX_C_SOURCE 200809L + +#include "misc.h" +#include "posix.h" + +#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; + +#if 0 + FTS *fts; + FTSENT *ent; +#endif + + fx_directory_iterator_flags flags; + fx_directory *root; + + fx_directory_entry entry; + fx_value entry_value; +}; + +/*** 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_get_cstr(dir->d_path_abs); +} + +static const char *directory_get_rel_path_cstr(const struct fx_directory_p *dir) +{ + return fx_path_get_cstr(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, + fx_value path) +{ + fx_path *abs_path = fx_path_join_list( + 2, + &FX_VALUE_OBJECT(root ? root->d_path_abs : NULL), + &path); + 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, + fx_value path) +{ + const fx_value *parts[] = {}; + + fx_path *abs_path = fx_path_join_list( + 2, + &FX_VALUE_OBJECT(root ? root->d_path_abs : NULL), + &path); + 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, + fx_value path) +{ + const fx_value *parts[] = {}; + + fx_path *abs_path = fx_path_join_list( + 2, + &FX_VALUE_OBJECT(root ? root->d_path_abs : NULL), + &path); + 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, + fx_value path, + struct fx_file_info *out) +{ + const fx_value *parts[] = {}; + + fx_path *abs_path = fx_path_join_list( + 2, + &FX_VALUE_OBJECT(root ? root->d_path_abs : NULL), + &path); + 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, + fx_value path) +{ + const fx_value *parts[] = {}; + + fx_path *abs_path = fx_path_join_list( + 2, + &FX_VALUE_OBJECT(root ? root->d_path_abs : NULL), + &path); + 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) +{ +#if 0 + 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); +#endif + return FX_RESULT_ERR(NOT_SUPPORTED); +} + +static fx_result create_directory_hierarchy( + struct fx_directory_p *root, + const char *path) +{ +#if 0 + 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; +#endif + return FX_RESULT_ERR(NOT_SUPPORTED); +} + +static fx_result directory_open( + struct fx_directory_p *root, + fx_value path, + fx_directory_open_flags flags, + fx_directory **out) +{ +#if 0 + enum fx_status status = FX_SUCCESS; + + int root_fd = root ? root->d_fd : AT_FDCWD; + + fx_stringstream *path_str = fx_stringstream_create(); + fx_value_to_string(&path, path_str, NULL); + const char *path_cstr = fx_stringstream_get_cstr(path_str); + + 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_get_system(FX_PATH_CWD); + } + + const fx_value *parts[] = { + &FX_VALUE_OBJECT(root ? root->d_path_abs : cwd), + &FX_CSTR(path_cstr), + }; + + fx_path *new_path + = fx_path_join_array(parts, sizeof parts / sizeof parts[0]); + if (!new_path) { + fx_stringstream_unref(path_str); + return FX_RESULT_ERR(NO_MEMORY); + } + + if (cwd) { + fx_path_unref(cwd); + } + + p->d_path_abs = new_path; + p->d_path_rel = fx_path_create_from_cstr(path_cstr); + p->d_fd = fd; + + if (flags & FX_DIRECTORY_OPEN_DELETE_ON_CLOSE) { + p->d_flags = DIRECTORY_DELETE_ON_CLOSE; + } + + *out = dir; + fx_stringstream_unref(path_str); + + return FX_RESULT_SUCCESS; +#endif + return FX_RESULT_ERR(NOT_SUPPORTED); +} + +/*** PUBLIC FUNCTIONS *********************************************************/ + +fx_result fx_directory_open( + fx_directory *root, + fx_value 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_directory *dir = NULL; + fx_result status = fx_directory_open( + FX_DIRECTORY_ROOT, + FX_CSTR(path), + 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) { + continue; + } + + if (dir) { + p->d_flags |= DIRECTORY_DELETE_ON_CLOSE; + } + + *out = dir; + 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, fx_value path) +{ + FX_CLASS_DISPATCH_STATIC( + FX_TYPE_DIRECTORY, + directory_path_exists, + root, + path); +} + +bool fx_directory_path_is_file(const fx_directory *root, fx_value path) +{ + FX_CLASS_DISPATCH_STATIC( + FX_TYPE_DIRECTORY, + directory_path_is_file, + root, + path); +} + +bool fx_directory_path_is_directory(const fx_directory *root, fx_value path) +{ + FX_CLASS_DISPATCH_STATIC( + FX_TYPE_DIRECTORY, + directory_path_is_directory, + root, + path); +} + +fx_result fx_directory_path_stat( + const fx_directory *root, + fx_value 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, fx_value 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 *******************************************************/ + +#if 0 +static int ftsent_compare(const FTSENT **one, const FTSENT **two) +{ + return (strcmp((*one)->fts_name, (*two)->fts_name)); +} +#endif + +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; + } + +#if 0 + FTSENT *ent = it->ent; + + fx_path *path = fx_path_create_from_cstr( + ent->fts_path + fx_path_length(it->_p->d_path_abs) + 1); + + fx_value_unset(&it->entry_value); + it->entry_value = FX_POINTER(&it->entry); + 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; + } +#endif +} + +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 0 + if (it->fts) { + fts_close(it->fts); + } +#endif +} + +fx_iterator *fx_directory_begin( + fx_directory *directory, + enum fx_directory_iterator_flags flags) +{ +#if 0 + 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_get_cstr(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; +#endif + return NULL; +} + +static const fx_iterator *iterator_begin(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) +{ +#if 0 + 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; +#endif + return FX_ERR_NOT_SUPPORTED; +} + +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, + FX_VALUE_OBJECT(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 const fx_value *iterator_get_value(const fx_iterator *obj) +{ + struct fx_directory_iterator_p *it + = fx_object_get_private(obj, FX_TYPE_DIRECTORY_ITERATOR); + + return &it->entry_value; +} + +/*** CLASS DEFINITION *********************************************************/ + +// ---- fx_directory DEFINITION +FX_TYPE_CLASS_BEGIN(fx_directory) + FX_TYPE_VTABLE_INTERFACE_BEGIN(fx_object, FX_TYPE_OBJECT) + FX_INTERFACE_ENTRY(to_string) = NULL; + FX_TYPE_VTABLE_INTERFACE_END(fx_object, FX_TYPE_OBJECT) + + FX_TYPE_VTABLE_INTERFACE_BEGIN(fx_iterable, FX_TYPE_ITERABLE) + FX_INTERFACE_ENTRY(it_begin) = iterator_begin; + FX_TYPE_VTABLE_INTERFACE_END(fx_iterable, FX_TYPE_ITERABLE) +FX_TYPE_CLASS_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_BEGIN(fx_directory_iterator) + FX_TYPE_VTABLE_INTERFACE_BEGIN(fx_object, FX_TYPE_OBJECT) + FX_INTERFACE_ENTRY(to_string) = NULL; + FX_TYPE_VTABLE_INTERFACE_END(fx_object, FX_TYPE_OBJECT) + + FX_TYPE_VTABLE_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_TYPE_VTABLE_INTERFACE_END(fx_iterator, FX_TYPE_ITERATOR) +FX_TYPE_CLASS_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/fx.io/sys/rosetta/file.c b/fx.io/sys/rosetta/file.c new file mode 100644 index 0000000..8b06d83 --- /dev/null +++ b/fx.io/sys/rosetta/file.c @@ -0,0 +1,653 @@ +#define _POSIX_C_SOURCE 200809L + +#include "misc.h" +#include "posix.h" + +#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_value *parts[] = { + &FX_VALUE_OBJECT(dir), + &FX_VALUE_OBJECT(shadow_filename), + }; + + fx_path *shadow_filepath + = fx_path_join_array(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, + FX_VALUE_OBJECT(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) +{ +#if 0 + 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); +#endif + return FX_ERR_NOT_SUPPORTED; +} + +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) +{ +#if 0 + int err = ftruncate(file->fd, len); + if (err == 0) { + return FX_SUCCESS; + } + + return fx_status_from_errno(errno, FX_ERR_IO_FAILURE); +#endif + return FX_ERR_NOT_SUPPORTED; +} + +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_value *parts[] = { + &FX_VALUE_OBJECT(dir_path), + &FX_VALUE_OBJECT(tmp_name_p), + }; + + tmp_path = fx_path_join_array( + 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; + +#if 0 + err = rename( + fx_path_get_cstr(main_file->path), + fx_path_get_cstr(tmp_path)); + err = rename( + fx_path_get_cstr(shadow_file->path), + fx_path_get_cstr(main_file->path)); + err = rename( + fx_path_get_cstr(tmp_path), + fx_path_get_cstr(shadow_file->path)); +#endif + + 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, + fx_value path, + enum fx_file_mode mode, + fx_file **out) +{ + 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_get_system(FX_PATH_CWD); + free_root_path = true; + } + + fx_path *abs_path + = fx_path_join_list(2, &FX_VALUE_OBJECT(root_path), &path); + + 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_get_cstr(abs_path), flags); + 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, + FX_VALUE_OBJECT(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_BEGIN(fx_file) + FX_TYPE_VTABLE_INTERFACE_BEGIN(fx_object, FX_TYPE_OBJECT) + FX_INTERFACE_ENTRY(to_string) = NULL; + FX_TYPE_VTABLE_INTERFACE_END(fx_object, FX_TYPE_OBJECT) + + FX_TYPE_VTABLE_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_VTABLE_INTERFACE_END(fx_stream, FX_TYPE_STREAM) +FX_TYPE_CLASS_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/fx.io/sys/rosetta/misc.c b/fx.io/sys/rosetta/misc.c new file mode 100644 index 0000000..3703538 --- /dev/null +++ b/fx.io/sys/rosetta/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/fx.io/sys/rosetta/misc.h b/fx.io/sys/rosetta/misc.h new file mode 100644 index 0000000..303affe --- /dev/null +++ b/fx.io/sys/rosetta/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/fx.io/sys/rosetta/path.c b/fx.io/sys/rosetta/path.c new file mode 100644 index 0000000..3ddd3c2 --- /dev/null +++ b/fx.io/sys/rosetta/path.c @@ -0,0 +1,486 @@ +#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) +{ +#if 0 + 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); +#endif + return FX_ERR_NOT_SUPPORTED; +} + +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 fx_string *src) +{ + const char *src_cstr = fx_string_get_cstr(src); + if (src_cstr[0] == '/') { + fx_string_clear(dest->p_pathstr); + fx_string_append_s(dest->p_pathstr, src); + 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) != '/') { + fx_string_append_c(dest->p_pathstr, '/'); + } + + fx_string_append_s(dest->p_pathstr, src); +} + +static enum fx_status path_unlink(const struct fx_path_p *path) +{ +#if 0 + int err = remove(fx_string_get_cstr(path->p_pathstr)); + return err == 0 ? FX_SUCCESS + : fx_status_from_errno(errno, FX_ERR_IO_FAILURE); +#endif + return FX_ERR_NOT_SUPPORTED; +} + +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_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_get_system(fx_system_path path_type) +{ +#if 0 + char path_buf[4096]; + const char *path_value = NULL, *default_value = NULL; + switch (path_type) { + case FX_PATH_ROOT: + path_value = "/"; + break; + case FX_PATH_CWD: + if (getcwd(path_buf, sizeof path_buf) == NULL) { + path_value = path_buf; + } + break; + case FX_PATH_HOME: + path_value = getenv("HOME"); + default_value = "~"; + break; + case FX_PATH_CONFIG: { + const char *home = getenv("HOME"); + path_value = getenv("XDG_CONFIG_HOME"); + snprintf( + path_buf, + sizeof path_buf, + "%s/.config", + home ? home : "~"); + default_value = path_buf; + break; + } + default: + return NULL; + } + + if (path_value) { + return fx_path_create_from_cstr(path_value); + } + + if (default_value) { + return fx_path_create_from_cstr(default_value); + } +#endif + + return NULL; +} + +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_array(const fx_value *values[], size_t nr_values) +{ + fx_path *result = fx_path_create(); + if (!result) { + return NULL; + } + + struct fx_path_p *result_p + = fx_object_get_private(result, FX_TYPE_PATH); + + fx_stringstream *tmp = fx_stringstream_create(); + fx_string *value_s = fx_string_create(); + for (size_t i = 0; i < nr_values; i++) { + if (!values[i]) { + continue; + } + + fx_stringstream_reset(tmp); + fx_value_to_string(values[i], tmp, NULL); + + fx_string_clear(value_s); + fx_string_append_cstr(value_s, fx_stringstream_get_cstr(tmp)); + append_path(result_p, value_s); + } + + fx_string_unref(value_s); + fx_stringstream_unref(tmp); + return result; +} + +fx_path *fx_path_join_list(size_t count, ...) +{ + fx_path *result = fx_path_create(); + if (!result) { + return NULL; + } + + struct fx_path_p *result_p + = fx_object_get_private(result, FX_TYPE_PATH); + va_list arg; + va_start(arg, count); + + fx_stringstream *tmp = fx_stringstream_create(); + fx_string *value_s = fx_string_create(); + for (size_t i = 0; i < count; i++) { + const fx_value *value = va_arg(arg, const fx_value *); + + fx_stringstream_reset(tmp); + fx_value_to_string(value, tmp, NULL); + + fx_string_clear(value_s); + fx_string_append_cstr(value_s, fx_stringstream_get_cstr(tmp)); + append_path(result_p, value_s); + } + + fx_string_unref(value_s); + fx_stringstream_unref(tmp); + 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_get_cstr(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); +} + +static fx_status path_to_string( + const fx_value *obj, + fx_stream *out, + const char *format) +{ + struct fx_path_p *path + = fx_object_get_private(obj->v_object, FX_TYPE_PATH); + + return fx_stream_write_cstr( + out, + fx_string_get_cstr(path->p_pathstr), + NULL); +} + +/*** CLASS DEFINITION *********************************************************/ + +FX_TYPE_CLASS_BEGIN(fx_path) + FX_TYPE_VTABLE_INTERFACE_BEGIN(fx_object, FX_TYPE_OBJECT) + FX_INTERFACE_ENTRY(to_string) = path_to_string; + FX_TYPE_VTABLE_INTERFACE_END(fx_object, FX_TYPE_OBJECT) +FX_TYPE_CLASS_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/fx.io/sys/rosetta/pipe.c b/fx.io/sys/rosetta/pipe.c new file mode 100644 index 0000000..d8c59a6 --- /dev/null +++ b/fx.io/sys/rosetta/pipe.c @@ -0,0 +1,32 @@ +#include "posix.h" + +#include +#include +#include + +fx_status fx_pipe_create(fx_iostream **reader, fx_iostream **writer) +{ +#if 0 + int fds[2]; + int err = pipe(fds); + if (err != 0) { + return fx_status_from_errno(errno, FX_ERR_NOT_SUPPORTED); + } + + fx_iostream *r = fx_iostream_create(FX_STREAM_READ, fds[0], true); + fx_iostream *w = fx_iostream_create(FX_STREAM_WRITE, fds[1], true); + + if (!r || !w) { + fx_iostream_unref(r); + fx_iostream_unref(w); + close(fds[0]); + close(fds[1]); + return FX_ERR_NO_MEMORY; + } + + *reader = r; + *writer = w; + return FX_SUCCESS; +#endif + return FX_ERR_NOT_SUPPORTED; +} diff --git a/fx.io/sys/rosetta/posix.c b/fx.io/sys/rosetta/posix.c new file mode 100644 index 0000000..43f9f38 --- /dev/null +++ b/fx.io/sys/rosetta/posix.c @@ -0,0 +1,152 @@ +#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; +} + +#if 0 +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; +} +#endif diff --git a/fx.io/sys/rosetta/posix.h b/fx.io/sys/rosetta/posix.h new file mode 100644 index 0000000..7168c9c --- /dev/null +++ b/fx.io/sys/rosetta/posix.h @@ -0,0 +1,26 @@ +#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/fx.io/sys/rosetta/stream.c b/fx.io/sys/rosetta/stream.c new file mode 100644 index 0000000..0444481 --- /dev/null +++ b/fx.io/sys/rosetta/stream.c @@ -0,0 +1,149 @@ +#include "posix.h" + +#include +#include +#include +#include +#include +#include +#include + +/*** PRIVATE DATA *************************************************************/ + +struct fx_iostream_p { + int s_fd; + bool s_fd_close_on_release; +}; + +/*** PRIVATE FUNCTIONS ********************************************************/ + +static uptr iostream_get_os_handle(const struct fx_iostream_p *stream) +{ + return stream->s_fd; +} + +static uptr iostream_steal_os_handle(struct fx_iostream_p *stream) +{ + uptr out = stream->s_fd; + stream->s_fd = -1; + stream->s_fd_close_on_release = false; + return out; +} + +/*** PUBLIC FUNCTIONS *********************************************************/ + +fx_iostream *fx_iostream_create( + fx_stream_mode mode, + uptr os_handle, + bool close_handle_on_release) +{ + fx_iostream *s = fx_object_create(FX_IO_TYPE_STREAM); + if (!s) { + return NULL; + } + + fx_stream_cfg *cfg = fx_object_get_protected(s, FX_TYPE_STREAM); + struct fx_iostream_p *p = fx_object_get_private(s, FX_IO_TYPE_STREAM); + + cfg->s_mode = mode | Z__FX_STREAM_STATIC; + p->s_fd = (int)os_handle; + p->s_fd_close_on_release = close_handle_on_release; + + return s; +} + +uptr fx_iostream_get_os_handle(const fx_iostream *stream) +{ + FX_CLASS_DISPATCH_STATIC_0( + FX_IO_TYPE_STREAM, + iostream_get_os_handle, + stream); +} + +uptr fx_iostream_steal_os_handle(fx_iostream *stream) +{ + FX_CLASS_DISPATCH_STATIC_0( + FX_IO_TYPE_STREAM, + iostream_steal_os_handle, + stream); +} + +/*** VIRTUAL FUNCTIONS ********************************************************/ + +static void iostream_init(fx_object *obj, void *priv) +{ + struct fx_iostream_p *stream = priv; +} + +static void iostream_fini(fx_object *obj, void *priv) +{ + struct fx_iostream_p *stream = priv; + + if (stream->s_fd_close_on_release) { + close(stream->s_fd); + } +} + +static enum fx_status stream_read( + fx_stream *stream, + void *buf, + size_t count, + size_t *nr_read) +{ + struct fx_iostream_p *s + = fx_object_get_private(stream, FX_IO_TYPE_STREAM); + + long r = read(s->s_fd, buf, count); + if (r < 0) { + return fx_status_from_errno(errno, FX_ERR_IO_FAILURE); + } + + *nr_read = r; + return FX_SUCCESS; +} + +static enum fx_status stream_write( + fx_stream *stream, + const void *buf, + size_t count, + size_t *nr_written) +{ + struct fx_iostream_p *s + = fx_object_get_private(stream, FX_IO_TYPE_STREAM); + + long w = write(s->s_fd, buf, count); + if (w < 0) { + return fx_status_from_errno(errno, FX_ERR_IO_FAILURE); + } + + *nr_written = w; + return FX_SUCCESS; +} + +/*** CLASS DEFINITION *********************************************************/ + +FX_TYPE_CLASS_BEGIN(fx_iostream) + FX_TYPE_VTABLE_INTERFACE_BEGIN(fx_object, FX_TYPE_OBJECT) + FX_INTERFACE_ENTRY(to_string) = NULL; + FX_TYPE_VTABLE_INTERFACE_END(fx_object, FX_TYPE_OBJECT) + + FX_TYPE_VTABLE_INTERFACE_BEGIN(fx_stream, FX_TYPE_STREAM) + FX_INTERFACE_ENTRY(s_close) = NULL; + FX_INTERFACE_ENTRY(s_seek) = NULL; + FX_INTERFACE_ENTRY(s_tell) = NULL; + FX_INTERFACE_ENTRY(s_getc) = NULL; + FX_INTERFACE_ENTRY(s_read) = stream_read; + FX_INTERFACE_ENTRY(s_write) = stream_write; + FX_INTERFACE_ENTRY(s_reserve) = NULL; + FX_TYPE_VTABLE_INTERFACE_END(fx_stream, FX_TYPE_STREAM) +FX_TYPE_CLASS_END(fx_iostream) + +FX_TYPE_DEFINITION_BEGIN(fx_iostream) + FX_TYPE_ID(0xc0b1c3c9, 0xa9c6, 0x4910, 0x8786, 0x574b0696e7aa); + FX_TYPE_NAME("fx.io.stream"); + FX_TYPE_EXTENDS(FX_TYPE_STREAM); + FX_TYPE_CLASS(fx_iostream_class); + FX_TYPE_INSTANCE_PRIVATE(struct fx_iostream_p); + FX_TYPE_INSTANCE_INIT(iostream_init); + FX_TYPE_INSTANCE_FINI(iostream_fini); +FX_TYPE_DEFINITION_END(fx_iostream)