diff --git a/CMakeLists.txt b/CMakeLists.txt index d5e484f..876f679 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -114,8 +114,13 @@ add_custom_command( # mpw-shell-execute.cpp mpw-shell-builtins.cpp mpw-shell-read.cpp add_executable(mpw-shell mpw-shell.cpp mpw-shell-token.cpp mpw-shell-expand.cpp mpw-shell-parser.cpp value.cpp mpw-shell-quote.cpp - phase1.cpp phase2.cpp phase2-parser.cpp command.cpp environment.cpp builtins.cpp mapped_file.cpp - pathnames.cpp) + phase1.cpp phase2.cpp phase2-parser.cpp command.cpp environment.cpp builtins.cpp + pathnames.cpp + cxx/mapped_file.cpp + cxx/filesystem.cpp + cxx/path.cpp + cxx/directory_iterator.cpp +) target_link_libraries(mpw-shell edit) diff --git a/cxx/directory_iterator.cpp b/cxx/directory_iterator.cpp new file mode 100644 index 0000000..5b70ae5 --- /dev/null +++ b/cxx/directory_iterator.cpp @@ -0,0 +1,193 @@ +#include "filesystem.h" + +#include +#include + +#include + + +namespace filesystem { + + +#pragma mark - directory_entry + + directory_entry::directory_entry(const class path& p, file_status st, file_status symlink_st) : + _path(p), _st(st), _lst(symlink_st) + {} + + void directory_entry::assign(const class path& p, file_status st, file_status symlink_st) + { + _path = p; + _st = st; + _lst = symlink_st; + } + + + #if 0 + void directory_entry::replace_filename(const path& p, file_status st, file_status symlink_st) + { + + + } + #endif + + file_status directory_entry::status() const { + + if (!status_known(_st)) + { + if (status_known(_lst) && ! is_symlink(_lst)) { + _st = _lst; + } else { + _st = filesystem::status(_path); + } + } + + return _st; + } + + file_status directory_entry::status(error_code& ec) const noexcept { + + if (!status_known(_st)) + { + if (status_known(_lst) && ! is_symlink(_lst)) { + _st = _lst; + } else { + _st = filesystem::status(_path, ec); + } + } + + return _st; + } + + file_status directory_entry::symlink_status() const { + + if (!status_known(_lst)) + _lst = filesystem::symlink_status(_path); + + return _lst; + } + + file_status directory_entry::symlink_status(error_code& ec) const noexcept { + + ec.clear(); + + if (!status_known(_lst)) + _lst = filesystem::symlink_status(_path, ec); + + return _lst; + } + + + bool directory_entry::operator< (const directory_entry& rhs) const noexcept { + return _path < rhs._path; + } + bool directory_entry::operator==(const directory_entry& rhs) const noexcept { + return _path == rhs._path; + } + bool directory_entry::operator!=(const directory_entry& rhs) const noexcept { + return _path != rhs._path; + } + bool directory_entry::operator<=(const directory_entry& rhs) const noexcept { + return _path <= rhs._path; + } + bool directory_entry::operator> (const directory_entry& rhs) const noexcept { + return _path > rhs._path; + } + bool directory_entry::operator>=(const directory_entry& rhs) const noexcept { + return _path >= rhs._path; + } + + +#pragma mark - directory_iterator + + directory_iterator::directory_iterator(const path& p) { + + + error_code ec; + + open_dir(p, ec); + if (ec) throw filesystem_error("directory_iterator::directory_iterator", p, ec); + + increment(ec); + if (ec) throw filesystem_error("directory_iterator::directory_iterator", p, ec); + } + + directory_iterator::directory_iterator(const path& p, error_code& ec) noexcept { + ec.clear(); + + open_dir(p, ec); + if (!ec) increment(ec); + } + + void directory_iterator::open_dir(const path &p, error_code &ec) noexcept { + + DIR *dp = opendir(p.c_str()); + if (!dp) + { + ec = error_code(errno, std::system_category()); + return; + } + + _imp = std::make_shared(p, dp); + } + + + + directory_iterator& directory_iterator::operator++() { + + error_code ec; + + increment(ec); + if (ec) throw filesystem_error("directory_iterator::operator++", ec); + + return *this; + } + + directory_iterator& directory_iterator::increment(error_code& ec) noexcept { + + ec.clear(); + + if (_imp) { + + struct dirent entry; + struct dirent *result = nullptr; + int rv; + + DIR *dp = _imp->_dp; + + for(;;) + { + + + rv = readdir_r(dp, &entry, &result); + if (rv != 0) + { + ec = error_code(errno, std::system_category()); + _imp.reset(); + break; + } + + if (!result) { + _imp.reset(); + break; // end of directory. + } + + std::string s(entry.d_name); + if (s == ".") continue; + if (s == "..") continue; + + + path p = _imp->_path / s; + + _imp->_entry.assign(p); + + break; + } + } + + return *this; + } + + + +} diff --git a/cxx/filesystem.cpp b/cxx/filesystem.cpp new file mode 100644 index 0000000..e122d32 --- /dev/null +++ b/cxx/filesystem.cpp @@ -0,0 +1,289 @@ +#include "filesystem.h" + +#include +#include + +#include +#include +#include +#include + +namespace filesystem { + + namespace { + + + template + auto syscall(error_code &ec, FX fx, Args&&... args) -> decltype(fx(std::forward(args)...)) + { + auto rv = fx(std::forward(args)...); + if (rv < 0) { + ec = error_code(errno, std::system_category()); + } else { + ec.clear(); + } + + return rv; + } + + int fs_stat(const path &p, struct stat *buf, error_code &ec) { + int rv = stat(p.c_str(), buf); + if (rv < 0) { + ec = error_code(errno, std::system_category()); + } + else { + ec.clear(); + } + return rv; + } + + int fs_lstat(const path &p, struct stat *buf, error_code &ec) { + int rv = lstat(p.c_str(), buf); + if (rv < 0) { + ec = error_code(errno, std::system_category()); + } + else { + ec.clear(); + } + return rv; + } + + template + file_status status_common(FX fx, const path& p, error_code& ec) noexcept { + + + struct stat st; + int rv = fx(p, &st, ec); + if (rv < 0) + { + switch (ec.value()) + { + case ENOENT: + case ENOTDIR: + return file_status(file_type::not_found); + + case EOVERFLOW: + return file_status(file_type::unknown); + + //case ENAMETOOLONG: ??? + // case ELOOP ? + + default: + return file_status(file_type::none); + } + } + + ec.clear(); + perms prms = static_cast(st.st_mode & perms::mask); + + if (S_ISREG(st.st_mode)) + return file_status(file_type::regular, prms); + + if (S_ISDIR(st.st_mode)) + return file_status(file_type::directory, prms); + + if (S_ISBLK(st.st_mode)) + return file_status(file_type::block, prms); + + if (S_ISFIFO(st.st_mode)) + return file_status(file_type::fifo, prms); + + if (S_ISSOCK(st.st_mode)) + return file_status(file_type::socket, prms); + + return file_status(file_type::unknown, prms); + } + + } + + file_status status(const path& p) { + error_code ec; + file_status result = status(p, ec); + if (result.type() == file_type::none) + throw filesystem_error("filesystem::file_status", p, ec); + return result; + } + + file_status status(const path& p, error_code& ec) noexcept { + + return status_common(fs_stat, p, ec); +/* + struct stat st; + int rv = stat(p.c_str(), &st); + if (rv < 0) { + int e = errno; + ec = error_code(e, std::system_category()); + + switch(e){ + case ENOENT: + case ENOTDIR: + return file_status(file_type::not_found); + + case EOVERFLOW: + return file_status(file_type::unknown); + + //case ENAMETOOLONG: ??? + // case ELOOP ? + + default: + return file_status(file_type::none); + } + } + + ec.clear(); + perms prms = static_cast(st.st_mode & perms::mask); + + if (S_ISREG(st.st_mode)) + return file_status(file_type::regular, prms); + + if (S_ISDIR(st.st_mode)) + return file_status(file_type::directory, prms); + + if (S_ISBLK(st.st_mode)) + return file_status(file_type::block, prms); + + if (S_ISFIFO(st.st_mode)) + return file_status(file_type::fifo, prms); + + if (S_ISSOCK(st.st_mode)) + return file_status(file_type::socket, prms); + + return file_status(file_type::unknown, prms); +*/ + } + + file_status symlink_status(const path& p) { + error_code ec; + file_status result = symlink_status(p, ec); + if (result.type() == file_type::none) + throw filesystem_error("filesystem::symlink_status", p, ec); + return result; + } + + file_status symlink_status(const path& p, error_code& ec) noexcept { + + return status_common(fs_lstat, p, ec); + } + + + uintmax_t file_size(const path& p) { + error_code ec; + + struct stat st; + //if (fs_stat(p, &st, ec) < 0) + if (syscall(ec, ::stat, p.c_str(), &st) < 0) + throw filesystem_error("filesystem::file_size", p, ec); + + return st.st_size; + + } + + uintmax_t file_size(const path& p, error_code& ec) noexcept { + + struct stat st; + + //if (fs_stat(p, &st, ec) < 0) + if (syscall(ec, ::stat, p.c_str(), &st) < 0) + return static_cast(-1); + + return st.st_size; + } + + + + bool create_directory(const path& p) { + error_code ec; + bool rv = create_directory(p, ec); + if (ec) + throw filesystem_error("filesystem::create_directory", p, ec); + + return rv; + } + + bool create_directory(const path& p, error_code& ec) noexcept { + + int rv = ::mkdir(p.c_str(), S_IRWXU|S_IRWXG|S_IRWXO); + if (rv == 0) { + ec.clear(); + return true; + } + + int e = errno; + error_code tmp; + + // special case -- not an error if the directory already exists. + + if (e == EEXIST && is_directory(p, tmp)) { + ec.clear(); + return false; + } + + ec = error_code(e, std::system_category()); + return false; + } + + void resize_file(const path& p, uintmax_t new_size) { + error_code ec; + + if (syscall(ec, ::truncate, p.c_str(), new_size) < 0) + throw filesystem_error("filesystem::create_directory", p, ec); + } + + void resize_file(const path& p, uintmax_t new_size, error_code& ec) noexcept { + + syscall(ec, ::truncate, p.c_str(), new_size); + + } + + bool remove(const path& p) { + error_code ec; + + if (syscall(ec, ::remove, p.c_str()) < 0) + { + throw filesystem_error("filesystem::remove", p, ec); + } + return true; + } + + bool remove(const path& p, error_code& ec) noexcept { + if (syscall(ec, ::remove, p.c_str()) < 0) return false; + return true; + } + + + path current_path() { + error_code ec; + path p = current_path(ec); + + if (ec) + throw filesystem_error("filesystem::current_path", ec); + + return p; + } + + path current_path(error_code& ec) { + + char *cp; + char buffer[PATH_MAX]; + + ec.clear(); + cp = ::getcwd(buffer, sizeof(buffer)); + if (cp) return path(cp); + + ec = error_code(errno, std::system_category()); + return path(); + } + + void current_path(const path& p) { + error_code ec; + syscall(ec, ::chdir, p.c_str()); + if (ec) + throw filesystem_error("filesystem::current_path", p, ec); + } + + void current_path(const path& p, error_code& ec) noexcept { + + syscall(ec, ::chdir, p.c_str()); + } + +} diff --git a/cxx/filesystem.h b/cxx/filesystem.h new file mode 100644 index 0000000..0462ee6 --- /dev/null +++ b/cxx/filesystem.h @@ -0,0 +1,848 @@ +#ifndef __filesystem_h__ +#define __filesystem_h__ + +#include +#include +#include +#include +#include +#include +#include + +#include + +/* + * + * light-weight implementation of n3803 + * + * + */ + +namespace filesystem { + + class path; + class filesystem_error; + + using std::error_code; + using std::system_error; + + class path { + + public: + + typedef char value_type; + typedef std::basic_string string_type; + static constexpr value_type preferred_separator = '/'; + + + // constructors and destructor + path() = default; + path(const path& p) = default; + path(path&& p) noexcept : + _path(std::move(p._path)) { + p.invalidate(); + } + + template + path(Source const& source) : + _path(source) {} + + template + path(InputIterator begin, InputIterator end) : + _path(begin, end) {} + + template + path(Source const& source, const std::locale& loc); + + template + path(InputIterator begin, InputIterator end, const std::locale& loc); + + ~path() = default; + + // assignments + path& operator=(const path& p) { + if (this != &p) { + _path = p._path; + _info = p._info; + } + return *this; + } + path& operator=(path&& p) noexcept { + if (this != &p) { + _path = std::move(p._path); + _info = p._info; + p.invalidate(); + } + return *this; + } + + template + path& operator=(Source const& source) { + invalidate(); + _path = source; + return *this; + } + + path& assign(const path &p) { + return (*this = p); + } + + template + path& assign(Source const& source) { + invalidate(); + _path.assign(source); + return *this; + } + + template + path& assign(InputIterator begin, InputIterator end) { + invalidate(); + _path.assign(begin, end); + return *this; + } + + + // appends + path& operator/=(const path& p) { + return append(p); + } + + + + template + path& operator/=(Source const& source) { + return append(source); + } + + + + // consider this a specialization of append. + path& append(const path &p); + + path& append(const string_type &s); + + + template + path& append(Source const& source) { + return append(path(source)); + } + + template + path& append(InputIterator begin, InputIterator end) { + return append(path(begin, end)); + } + + + // concatenation + path& operator+=(const path& x) { + invalidate(); + _path += x._path; + return *this; + } + + path& operator+=(const string_type& x) { + invalidate(); + _path += x; + return *this; + } + + path& operator+=(const value_type* x) { + invalidate(); + _path += x; + return *this; + } + + path& operator+=(value_type x) { + invalidate(); + _path += x; + return *this; + } + + template + path& operator+=(Source const& x) { + invalidate(); + _path += x; + return *this; + } + + template + path& operator+=(charT x){ + invalidate(); + _path += x; + return *this; + } + + template + path& concat(Source const& x) { + invalidate(); + _path += x; + return *this; + } + + template + path& concat(InputIterator begin, InputIterator end) { + invalidate(); + _path.append(begin, end); + return *this; + } + + + // modifiers + void clear() noexcept { + invalidate(); + _path.clear(); + } + + path& make_preferred() { + return *this; + } + path& remove_filename(); + path& replace_filename(const path& replacement); + path& replace_extension(const path& replacement = path()); + + void swap(path& rhs) noexcept { + std::swap(_path, rhs._path); + std::swap(_info, rhs._info); + } + + // native format observers + const string_type& native() const noexcept { + return _path; + } + + const value_type* c_str() const noexcept { + return _path.c_str(); + } + + operator string_type() const { + return _path; + } + + template , + class Allocator = std::allocator > + std::basic_string string(const Allocator& a = Allocator()) const; + + std::string string() const { + return _path; + } + + std::wstring wstring() const; + std::string u8string() const; + std::u16string u16string() const; + std::u32string u32string() const; + + // generic format observers + template , + class Allocator = std::allocator > + std::basic_string + generic_string(const Allocator& a = Allocator()) const; + std::string generic_string() const { + return _path; + } + std::wstring generic_wstring() const; + std::string generic_u8string() const; + std::u16string generic_u16string() const; + std::u32string generic_u32string() const; + + // compare + int compare(const path& p) const noexcept; + int compare(const std::string& s) const; + int compare(const value_type* s) const; + + // decomposition + path root_name() const; + path root_directory() const; + path root_path() const; + path relative_path() const; + path parent_path() const; + path filename() const; + path stem() const; + path extension() const; + + // query + bool empty() const noexcept { + return _path.empty(); + } + + bool has_root_name() const; + bool has_root_directory() const; + bool has_root_path() const; + bool has_relative_path() const; + bool has_parent_path() const; + bool has_filename() const; + bool has_stem() const; + bool has_extension() const; + + bool is_absolute() const { + return !empty() && _path[0] == '/'; + } + + bool is_relative() const { + return empty() || _path[0] != '/'; + } + + // iterators + class iterator; + typedef iterator const_iterator; + iterator begin() const; + iterator end() const; + + private: + + void invalidate() const { + _info.valid = false; + } + + void study() const; + path &append_common(const std::string &s); + + + + + string_type _path; + + mutable struct { + bool valid = false; + value_type special; + int stem; + int extension; + } _info; + + }; + + + inline void swap(path& lhs, path& rhs) noexcept { + lhs.swap(rhs); + } + +/* + // ugh, this is boost, not stl. + inline std::size_t hash_value(const path& p) noexcept { + return std::hash_value(p._path); + } +*/ + inline bool operator==(const path& lhs, const path& rhs) noexcept { + return lhs.compare(rhs) == 0; + } + + inline bool operator!=(const path& lhs, const path& rhs) noexcept { + return lhs.compare(rhs) != 0; + } + + inline bool operator< (const path& lhs, const path& rhs) noexcept { + return lhs.compare(rhs) < 0; + } + inline bool operator<=(const path& lhs, const path& rhs) noexcept { + return lhs.compare(rhs) <= 0; + } + inline bool operator> (const path& lhs, const path& rhs) noexcept { + return lhs.compare(rhs) > 0; + } + inline bool operator>=(const path& lhs, const path& rhs) noexcept { + return lhs.compare(rhs) >= 0; + } + inline path operator/ (const path& lhs, const path& rhs) { + path tmp = lhs; + return tmp.append(rhs); + } + + //template + //basic_ostream& + //operator<<(basic_ostream& os, const path& p); + //template + //basic_istream& + ///operator>>(basic_istream& is, path& p); + template + path u8path(Source const& source); + template + path u8path(InputIterator begin, InputIterator end); + + + class filesystem_error : public system_error + { + public: + filesystem_error(const std::string& what_arg, error_code ec) : + system_error(ec, what_arg) + {} + + filesystem_error(const std::string& what_arg, const path& p1, error_code ec) : + system_error(ec, what_arg), _p1(p1) + {} + + filesystem_error(const std::string& what_arg, const path& p1, const path& p2, error_code ec) : + system_error(ec, what_arg), _p1(p1), _p2(p2) + {} + + const path& path1() const noexcept { + return _p1; + } + const path& path2() const noexcept { + return _p2; + } + + //const char* what() const noexcept; + + private: + path _p1; + path _p2; + }; + + + + enum class file_type { + + none = 0, + not_found = -1, + regular = 1, + directory = 2, + symlink = 3, + block = 4, + character = 5, + fifo = 6, + socket = 7, + unknown = 8 + }; + + enum perms { + + 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, + + add_perms = 0x10000, + remove_perms = 0x20000, + resolve_symlinks = 0x40000, + + }; + + + class file_status + { + public: + // constructors + explicit file_status(file_type ft = file_type::none, perms prms = perms::unknown) noexcept: + _ft(ft), _prms(prms) {} + + file_status(const file_status&) noexcept = default; + file_status(file_status&&) noexcept = default; + ~file_status() = default; + + file_status& operator=(const file_status&) noexcept = default; + file_status& operator=(file_status&&) noexcept = default; + + // observers + file_type type() const noexcept { + return _ft; + } + + perms permissions() const noexcept { + return _prms; + } + + // modifiers + void type(file_type ft) noexcept { + _ft = ft; + } + void permissions(perms prms) noexcept { + _prms = prms; + } + + private: + file_type _ft = file_type::none; + perms _prms = perms::unknown; + }; + + + struct space_info // returned by space function + { + uintmax_t capacity; + uintmax_t free; + uintmax_t available; // free space available to a non-privileged process + }; + + enum class directory_options + { + none, + follow_directory_symlink, + skip_permission_denied + }; + + //typedef std::chrono::time_point file_time_type; + typedef std::chrono::time_point file_time_type; + + enum class copy_options + { + none = 0, + skip_existing = 1, + overwrite_existing = 2, + update_existing = 4, + recurive = 8, + copy_symlinks = 16, + skip_symlinks = 32, + directories_only = 64, + create_symlinks = 128, + create_hard_links = 256 + }; + + path current_path(); + path current_path(error_code& ec); + void current_path(const path& p); + void current_path(const path& p, error_code& ec) noexcept; + + path absolute(const path& p, const path& base=current_path()); + + path canonical(const path& p, const path& base = current_path()); + path canonical(const path& p, error_code& ec); + path canonical(const path& p, const path& base, error_code& ec); + + void copy(const path& from, const path& to); + void copy(const path& from, const path& to, error_code& ec) noexcept; + void copy(const path& from, const path& to, copy_options options); + void copy(const path& from, const path& to, copy_options options, error_code& ec) noexcept; + + bool copy_file(const path& from, const path& to); + bool copy_file(const path& from, const path& to, error_code& ec) noexcept; + bool copy_file(const path& from, const path& to, copy_options option); + bool copy_file(const path& from, const path& to, copy_options option, error_code& ec) noexcept; + void copy_symlink(const path& existing_symlink, const path& new_symlink); + + void copy_symlink(const path& existing_symlink, const path& new_symlink, error_code& ec) noexcept; + + bool create_directories(const path& p); + bool create_directories(const path& p, error_code& ec) noexcept; + + bool create_directory(const path& p); + bool create_directory(const path& p, error_code& ec) noexcept; + bool create_directory(const path& p, const path& attributes); + bool create_directory(const path& p, const path& attributes, error_code& ec) noexcept; + + void create_directory_symlink(const path& to, const path& new_symlink); + void create_directory_symlink(const path& to, const path& new_symlink, error_code& ec) noexcept; + + void create_hard_link(const path& to, const path& new_hard_link); + void create_hard_link(const path& to, const path& new_hard_link, error_code& ec) noexcept; + + void create_symlink(const path& to, const path& new_symlink); + void create_symlink(const path& to, const path& new_symlink, error_code& ec) noexcept; + + + + + file_status status(const path& p); + file_status status(const path& p, error_code& ec) noexcept; + + file_status symlink_status(const path& p); + file_status symlink_status(const path& p, error_code& ec) noexcept; + + inline bool status_known(file_status s) noexcept { + return s.type() != file_type::none; + } + + inline bool exists(file_status s) noexcept { + return status_known(s) && s.type() != file_type::not_found; + } + + inline bool exists(const path& p) { + return exists(status(p)); + } + inline bool exists(const path& p, error_code& ec) noexcept { + return exists(status(p, ec)); + } + + + bool equivalent(const path& p1, const path& p2); + bool equivalent(const path& p1, const path& p2, error_code& ec) noexcept; + + uintmax_t file_size(const path& p); + uintmax_t file_size(const path& p, error_code& ec) noexcept; + + uintmax_t hard_link_count(const path& p); + uintmax_t hard_link_count(const path& p, error_code& ec) noexcept; + + inline bool is_block_file(file_status s) noexcept { + return s.type() == file_type::block; + } + inline bool is_block_file(const path& p) { + return is_block_file(status(p)); + } + inline bool is_block_file(const path& p, error_code& ec) noexcept { + return is_block_file(status(p, ec)); + } + + inline bool is_character_file(file_status s) noexcept { + return s.type() == file_type::character; + } + inline bool is_character_file(const path& p) { + return is_character_file(status(p)); + } + inline bool is_character_file(const path& p, error_code& ec) noexcept { + return is_character_file(status(p, ec)); + } + + inline bool is_directory(file_status s) noexcept { + return s.type() == file_type::directory; + } + inline bool is_directory(const path& p) { + return is_directory(status(p)); + } + inline bool is_directory(const path& p, error_code& ec) noexcept { + return is_directory(status(p, ec)); + } + + bool is_empty(const path& p); + bool is_empty(const path& p, error_code& ec) noexcept; + + + inline bool is_fifo(file_status s) noexcept { + return s.type() == file_type::fifo; + } + inline bool is_fifo(const path& p) { + return is_fifo(status(p)); + } + inline bool is_fifo(const path& p, error_code& ec) noexcept { + return is_fifo(status(p, ec)); + } + + inline bool is_socket(file_status s) noexcept { + return s.type() == file_type::socket; + } + inline bool is_socket(const path& p) { + return is_socket(status(p)); + } + inline bool is_socket(const path& p, error_code& ec) noexcept { + return is_socket(status(p, ec)); + } + + + inline bool is_symlink(file_status s) noexcept { + return s.type() == file_type::symlink; + } + inline bool is_symlink(const path& p) { + return is_symlink(status(p)); + } + inline bool is_symlink(const path& p, error_code& ec) noexcept { + return is_symlink(status(p, ec)); + } + + + inline bool is_regular_file(file_status s) noexcept { + return s.type() == file_type::regular; + } + + inline bool is_regular_file(const path& p) { + return is_regular_file(status(p)); + } + + inline bool is_regular_file(const path& p, error_code& ec) noexcept { + return is_regular_file(status(p, ec)); + } + + inline bool is_other(file_status s) noexcept { + return exists(s) && !is_regular_file(s) && ! is_directory(s) && !is_symlink(s); + } + + inline bool is_other(const path& p) { + return is_other(status(p)); + } + + inline bool is_other(const path& p, error_code& ec) noexcept { + return is_other(status(p, ec)); + } + + + + + file_time_type last_write_time(const path& p); + file_time_type last_write_time(const path& p, error_code& ec) noexcept; + void last_write_time(const path& p, file_time_type new_time); + void last_write_time(const path& p, file_time_type new_time, error_code& ec) noexcept; + + void permissions(const path& p, perms prms); + void permissions(const path& p, perms prms, error_code& ec) noexcept; + + path read_symlink(const path& p); + path read_symlink(const path& p, error_code& ec); + bool remove(const path& p); + bool remove(const path& p, error_code& ec) noexcept; + uintmax_t remove_all(const path& p); + uintmax_t remove_all(const path& p, error_code& ec) noexcept; + void rename(const path& from, const path& to); + void rename(const path& from, const path& to, error_code& ec) noexcept; + void resize_file(const path& p, uintmax_t size); + void resize_file(const path& p, uintmax_t size, error_code& ec) noexcept; + space_info space(const path& p); + space_info space(const path& p, error_code& ec) noexcept; + file_status status(const path& p); + file_status status(const path& p, error_code& ec) noexcept; + bool status_known(file_status s) noexcept; + file_status symlink_status(const path& p); + file_status symlink_status(const path& p, error_code& ec) noexcept; + path system_complete(const path& p); + path system_complete(const path& p, error_code& ec); + path temp_directory_path(); + path temp_directory_path(error_code& ec); + + + + + class directory_entry { + + public: + + // constructors and destructor + directory_entry() = default; + directory_entry(const directory_entry&) = default; + directory_entry(directory_entry&&) noexcept = default; + explicit directory_entry(const path& p, file_status st=file_status(), file_status symlink_st=file_status()); + + ~directory_entry() = default; + + // modifiers + directory_entry& operator=(const directory_entry&) = default; + directory_entry& operator=(directory_entry&&) noexcept = default; + void assign(const path& p, file_status st=file_status(), + file_status symlink_st=file_status()); + void replace_filename(const path& p, file_status st=file_status(), + file_status symlink_st=file_status()); + + + // observers + const filesystem::path& path() const noexcept { + return _path; + } + + file_status status() const; + file_status status(error_code& ec) const noexcept; + file_status symlink_status() const; + file_status symlink_status(error_code& ec) const noexcept; + + + bool operator< (const directory_entry& rhs) const noexcept; + bool operator==(const directory_entry& rhs) const noexcept; + bool operator!=(const directory_entry& rhs) const noexcept; + bool operator<=(const directory_entry& rhs) const noexcept; + bool operator> (const directory_entry& rhs) const noexcept; + bool operator>=(const directory_entry& rhs) const noexcept; + + + private: + class path _path; + mutable file_status _st; + mutable file_status _lst; + }; + + + class directory_iterator : public std::iterator + { + public: + // member functions + directory_iterator() noexcept = default; + explicit directory_iterator(const path& p); + directory_iterator(const path& p, error_code& ec) noexcept; + + directory_iterator(const directory_iterator&) = default; + + directory_iterator(directory_iterator&&) = default; + ~directory_iterator() = default; + + directory_iterator& operator=(const directory_iterator&) = default; + directory_iterator& operator=(directory_iterator&&) = default; + + const directory_entry& operator*() const { + return _imp->_entry; + } + const directory_entry* operator->() const { + return &_imp->_entry; + } + directory_iterator& operator++(); + directory_iterator& increment(error_code& ec) noexcept; + + bool operator == (const directory_iterator &rhs) const noexcept { + return _imp == rhs._imp; + } + bool operator != (const directory_iterator &rhs) const noexcept { + return _imp != rhs._imp; + } + + // other members as required by + // C++ Std, 24.1.1 Input iterators [input.iterators] + + + + private: + + void open_dir(const path &p, error_code &ec) noexcept; + + struct imp { + path _path; + directory_entry _entry; + DIR *_dp = nullptr; + + imp() = default; + imp(const path &p, DIR *dp) : _path(p), _dp(dp) + {} + + ~imp() { + if (_dp) closedir(_dp); + } + }; + + std::shared_ptr _imp; + }; + + + /* + directory_iterator non-member functions + */ + inline const directory_iterator& begin(const directory_iterator& iter) noexcept { + return iter; + } + + inline directory_iterator end(const directory_iterator&) noexcept { + return directory_iterator(); + } + + +} + + +namespace std +{ + + inline void swap(filesystem::path& lhs, filesystem::path& rhs) noexcept { + lhs.swap(rhs); + } + + template<> + struct hash + { + std::size_t operator()(const filesystem::path &p) const + { + std::hash hasher; + return hasher(p.native()); + } + }; +} + + +#endif diff --git a/mapped_file.cpp b/cxx/mapped_file.cpp similarity index 100% rename from mapped_file.cpp rename to cxx/mapped_file.cpp diff --git a/mapped_file.h b/cxx/mapped_file.h similarity index 100% rename from mapped_file.h rename to cxx/mapped_file.h diff --git a/cxx/path.cpp b/cxx/path.cpp new file mode 100644 index 0000000..258b212 --- /dev/null +++ b/cxx/path.cpp @@ -0,0 +1,285 @@ + +#include "filesystem.h" + + +namespace filesystem { + + namespace { + const path::value_type separator = '/'; + + // hmmm... these could be pre-studied since they're constant? + const path path_dot = "."; + const path path_dotdot = ".."; + const path path_sep = "/"; + } + + void path::study() const { + + if (_info.valid) return; + int length = _path.length(); + if (length == 0) + { + _info.valid = true; + _info.special = 0; + _info.stem = _info.extension = 0; + return; + } + + auto back = _path[length - 1]; + + // check for special cases (part 1) + // if the path is all /s, filename is / + // if the path ends with a / (but contains a non-/ char), + // the filename is . + + if (back == separator) { + value_type special = '.'; + if (_path.find_first_not_of(separator) == _path.npos) + special = separator; + + _info.valid = true; + _info.extension = length; + _info.stem = length; + _info.special = special; + return; + } + + int stem = 0; + int extension = length; + for (int i = length; i; ) { + auto c = _path[--i]; + if (c == '.' && extension == length) + extension = i; + if (c == '/') { + stem = i + 1; + break; + } + } + + + // check for special cases (part 2) + // ".." and "." are not extensions. + if (back == '.') { + int xlength = length - stem; + if (xlength == 1) + extension = length; + if (xlength == 2 && _path[stem] == '.') + extension = length; + } + + _info.valid = true; + _info.stem = stem; + _info.extension = extension; + _info.special = 0; + } + + +#if 0 + path::path(const path &rhs) : + _path(rhs._path), _info(rhs._info) + {} + + path::path(path &&rhs) noexcept : + _path(std::move(rhs._path)) + { + rhs.invalidate(); + } +#endif + + + // private. neither this->_path nor s are empty. + path &path::append_common(const std::string &s) + { + invalidate(); + if (_path.back() != separator && s.front() != separator) + _path.push_back(separator); + + _path.append(s); + + return *this; + } + + path& path::append(const path& p) + { + if (p.empty()) return *this; + + if (empty()) { + return (*this = p); + } + + invalidate(); + + // check for something stupid like xx.append(xx); + if (&p == this) { + return append_common(string_type(p._path)); + } + + return append_common(p._path); + } + + path& path::append(const string_type &s) + { + if (s.empty()) return *this; + invalidate(); + + if (empty()) { + _path = s; + return *this; + } + + + if (&s == &_path) { + string_type tmp(s); + if (_path.back() != separator && tmp[0] != separator) + _path.push_back(separator); + _path.append(tmp); + return *this; + } + + if (_path.back() != separator && s[0] != separator) + _path.push_back(separator); + + _path.append(s); + + return *this; + } + + + + + path path::filename() const { + + if (empty()) return *this; + if (!_info.valid) study(); + + if (_info.special == separator) return path_sep; + if (_info.special == '.') return path_dot; + + if (_info.stem == 0) return *this; + return _path.substr(_info.stem); + } + + path path::stem() const { + + // filename without the extension. + + if (empty()) return *this; + if (!_info.valid) study(); + + if (_info.special == separator) return path_sep; + if (_info.special == '.') return path_dot; + + return _path.substr(_info.stem, _info.extension - _info.stem); + } + + path path::extension() const { + + if (empty()) return *this; + if (!_info.valid) study(); + + return _path.substr(_info.extension); + } + + + bool path::has_parent_path() const + { + // if there is a /, it has a parent path. + // ... unless it's /. + + if (empty()) return false; + + if (!_info.valid) study(); + + if (_info.special == '/') return false; + + return _path.find(separator) != _path.npos; + } + + path path::parent_path() const { + + /* + * special cases: + * /abc -> / + * /abc/ -> /abc + * all trailing /s are removed. + * + */ + + + if (empty()) return *this; + + if (!_info.valid) study(); + + // "/" is a file of "/" with a parent of "" + if (_info.special == separator) return path(); + + // stem starts at 0, eg "abc" + if (!_info.stem) return path(); + + + auto tmp = _path.substr(0, _info.stem - 1); + + // remove trailing slashes, but return "/" if nothing BUT /s. + while (!tmp.empty() && tmp.back() == separator) tmp.pop_back(); + if (tmp.empty()) return path_sep; + + return path(tmp); + } + + path path::root_directory() const { + // for unix, root directory is / or "". + if (empty()) return *this; + + return _path.front() == '/' ? path_sep : path(); + } + + path path::root_name() const { + /* + * boost (unix) considers // or //component + * to be a root name (and only those cases). + * + * I do not. + */ + + return path(); + } + + path path::root_path() const { + // root_name + root_directory. + // since root_name is always empty... + + return root_directory(); + } + + + path path::relative_path() const { + // first pathname *after* the root path + // root_path is first / in this implementation. + + if (is_relative()) return *this; + + auto pos = _path.find_first_not_of(separator); + if (pos == _path.npos) return path(); + + return path(_path.substr(pos)); + } + + + + // compare + int path::compare(const path& p) const noexcept { + if (&p == this) return 0; + return _path.compare(p._path); + } + + int path::compare(const std::string& s) const { + if (&s == &_path) return 0; + return _path.compare(s); + } + + int path::compare(const value_type* s) const { + return _path.compare(s); + } + + +} diff --git a/cxx/string_splitter.h b/cxx/string_splitter.h new file mode 100644 index 0000000..7984a22 --- /dev/null +++ b/cxx/string_splitter.h @@ -0,0 +1,81 @@ +#ifndef __string_splitter__ +#define __string_splitter__ + +#include + +class string_splitter { +public: + string_splitter(const std::string &str, char sep) : + _parent(str), _sep(sep) + { + _begin = 0; + _end = _parent.find(_sep); + _str = _parent.substr(_begin, _end); + // _begin is 0, _end is either npos or offset from 0, + // so no need to calculate a count. + } + + operator bool() const { + return _begin != npos; + } + + string_splitter &operator++() { + increment(); + return *this; + } + + const std::string &operator *() const { + return _str; + } + + const std::string *operator ->() const { + return &_str; + } + +private: + void increment() { + _str.clear(); + if (_begin == npos) return; + if (_end == npos) { _begin = _end; return; } + + _begin = _end + 1; + _end = _parent.find(_sep, _begin); + auto count = _end == npos ? _end : _end - _begin; + _str = _parent.substr(_begin, count); + } + + const static auto npos = std::string::npos; + std::string _str; + const std::string &_parent; + char _sep; + std::string::size_type _begin = 0; + std::string::size_type _end = 0; +}; + + +#ifdef TEST +#include +#include + +int main(int argc, char **argv) +{ + if (argc != 3) { + fprintf(stderr, "Usage: %s sep string\n", argv[0]); + return 1; + } + if (strlen(argv[1]) != 1) { + fprintf(stderr, "Separator must be a single character\n"); + return 1; + } + char sep = argv[1][0]; + std::string str(argv[2]); + + for (auto iter = string_splitter(str, sep); iter; ++iter) { + printf("%s\n", iter->c_str()); + } + + return 0; +} +#endif + +#endif diff --git a/unique_resource.h b/cxx/unique_resource.h similarity index 100% rename from unique_resource.h rename to cxx/unique_resource.h diff --git a/mpw-shell.cpp b/mpw-shell.cpp index e095c92..8644cdb 100644 --- a/mpw-shell.cpp +++ b/mpw-shell.cpp @@ -17,7 +17,7 @@ #include "phase2.h" #include "command.h" -#include "mapped_file.h" +#include "cxx/mapped_file.h" #include "error.h" //#include