diff --git a/ostd/path.hh b/ostd/path.hh index 4d63cd6..e5e3231 100644 --- a/ostd/path.hh +++ b/ostd/path.hh @@ -21,7 +21,7 @@ #ifndef OSTD_PATH_HH #define OSTD_PATH_HH -#include +#include #include #include @@ -696,6 +696,177 @@ namespace fs { * @{ */ +enum class file_type { + none = 0, + not_found, + regular, + directory, + symlink, + block, + character, + fifo, + socket, + unknown +}; + +enum class perms { + /* occupies 12 bits */ + none = 0, + owner_read = 0400, + owner_write = 0200, + owner_exec = 0100, + owner_all = 0700, + group_read = 040, + group_write = 020, + group_exec = 010, + group_all = 070, + others_read = 04, + others_write = 02, + others_exec = 01, + others_all = 07, + all = 0777, + set_uid = 04000, + set_gid = 02000, + sticky_bit = 01000, + mask = 07777, + + unknown = 0xFFFF +}; + +inline perms operator|(perms a, perms b) { + return perms(int(a) | int(b)); +} + +inline perms operator&(perms a, perms b) { + return perms(int(a) & int(b)); +} + +inline perms operator^(perms a, perms b) { + return perms(int(a) ^ int(b)); +} + +inline perms operator~(perms v) { + return perms(~int(v)); +} + +inline perms &operator|=(perms &a, perms b) { + a = (a | b); + return a; +} + +inline perms &operator&=(perms &a, perms b) { + a = (a & b); + return a; +} + +inline perms &operator^=(perms &a, perms b) { + a = (a ^ b); + return a; +} + +struct file_status { +private: + using UT = std::uint_least32_t; + UT p_val; + +public: + file_status() noexcept: file_status(file_type::none) {} + file_status(file_type type, perms permissions = perms::unknown) noexcept: + p_val(UT(permissions) | (UT(type) << 16)) + {} + + file_type type() const noexcept { + return file_type(p_val >> 16); + } + + void type(file_type type) noexcept { + p_val = ((p_val & 0xFFFF) | (UT(type) << 16)); + } + + perms permissions() const noexcept { + return perms(p_val & 0xFFFF); + } + + void permissions(perms perm) noexcept { + p_val = ((p_val & ~UT(0xFFFF)) | UT(perm)); + } +}; + +OSTD_EXPORT file_status status(path const &p); +OSTD_EXPORT file_status symlink_status(path const &p); + +inline bool is_block_file(file_status st) { + return st.type() == file_type::block; +} + +inline bool is_block_file(path const &p) { + return is_block_file(status(p)); +} + +inline bool is_character_file(file_status st) { + return st.type() == file_type::character; +} + +inline bool is_character_file(path const &p) { + return is_character_file(status(p)); +} + +inline bool is_directory(file_status st) { + return st.type() == file_type::directory; +} + +inline bool is_directory(path const &p) { + return is_directory(status(p)); +} + +inline bool is_regular_file(file_status st) { + return st.type() == file_type::regular; +} + +inline bool is_regular_file(path const &p) { + return is_regular_file(status(p)); +} + +inline bool is_fifo(file_status st) { + return st.type() == file_type::fifo; +} + +inline bool is_fifo(path const &p) { + return is_fifo(status(p)); +} + +inline bool is_symlink(file_status st) { + return st.type() == file_type::symlink; +} + +inline bool is_symlink(path const &p) { + return is_symlink(status(p)); +} + +inline bool is_socket(file_status st) { + return st.type() == file_type::socket; +} + +inline bool is_socket(path const &p) { + return is_socket(status(p)); +} + +inline bool is_other(file_status st) { + return st.type() == file_type::unknown; +} + +inline bool is_other(path const &p) { + return is_other(status(p)); +} + +inline bool status_known(file_status st) { + return st.type() != file_type::none; +} + +inline bool status_known(path const &p) { + return status_known(status(p)); +} + struct directory_entry { directory_entry() {} directory_entry(ostd::path const &p): p_path(p) {} diff --git a/src/posix/path.cc b/src/posix/path.cc index 810b7c5..3ced4b0 100644 --- a/src/posix/path.cc +++ b/src/posix/path.cc @@ -9,6 +9,75 @@ #include "ostd/path.hh" +namespace ostd { +namespace fs { + +static perms mode_to_perms(mode_t mode) { + perms ret = perms::none; + switch (mode & S_IRWXU) { + case S_IRUSR: ret |= perms::owner_read; + case S_IWUSR: ret |= perms::owner_write; + case S_IXUSR: ret |= perms::owner_exec; + case S_IRWXU: ret |= perms::owner_all; + } + switch (mode & S_IRWXG) { + case S_IRGRP: ret |= perms::group_read; + case S_IWGRP: ret |= perms::group_write; + case S_IXGRP: ret |= perms::group_exec; + case S_IRWXG: ret |= perms::group_all; + } + switch (mode & S_IRWXO) { + case S_IROTH: ret |= perms::others_read; + case S_IWOTH: ret |= perms::others_write; + case S_IXOTH: ret |= perms::others_exec; + case S_IRWXO: ret |= perms::others_all; + } + if (mode & S_ISUID) { + ret |= perms::set_uid; + } + if (mode & S_ISGID) { + ret |= perms::set_gid; + } + if (mode & S_ISVTX) { + ret |= perms::sticky_bit; + } + return ret; +} + +static file_type mode_to_type(mode_t mode) { + switch (mode & S_IFMT) { + case S_IFBLK: return file_type::block; + case S_IFCHR: return file_type::character; + case S_IFIFO: return file_type::fifo; + case S_IFREG: return file_type::regular; + case S_IFDIR: return file_type::directory; + case S_IFLNK: return file_type::symlink; + case S_IFSOCK: return file_type::socket; + } + return file_type::unknown; +} + +OSTD_EXPORT file_status status(path const &p) { + struct stat sb; + if (stat(p.string().data(), &sb) < 0) { + /* FIXME: throw */ + abort(); + } + return file_status{mode_to_type(sb.st_mode), mode_to_perms(sb.st_mode)}; +} + +OSTD_EXPORT file_status symlink_status(path const &p) { + struct stat sb; + if (lstat(p.string().data(), &sb) < 0) { + /* FIXME: throw */ + abort(); + } + return file_status{mode_to_type(sb.st_mode), mode_to_perms(sb.st_mode)}; +} + +} /* namespace fs */ +} /* namespace ostd */ + namespace ostd { namespace fs { namespace detail { @@ -81,12 +150,7 @@ OSTD_EXPORT void rdir_range_impl::read_next() { if (p_handles.empty()) { return; } - struct stat sb; - if (stat(p_current.path().string().data(), &sb) < 0) { - /* FIXME: throw */ - abort(); - } - if (S_ISDIR(sb.st_mode)) { + if (is_directory(p_current.path())) { /* directory, recurse into it and if it contains stuff, return */ DIR *nd = opendir(p_current.path().string().data()); if (!nd) {