From d011daad3b1006482941ee21c911a006d8036429 Mon Sep 17 00:00:00 2001 From: q66 Date: Tue, 17 Apr 2018 02:26:23 +0200 Subject: [PATCH] implement path cwd/home/temp/absolute/relative/canonical/existence/equivalence --- examples/listdir.cc | 2 +- ostd/path.hh | 20 ++++++- src/posix/path.cc | 138 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 157 insertions(+), 3 deletions(-) diff --git a/examples/listdir.cc b/examples/listdir.cc index 5cc911e..fb7b07d 100644 --- a/examples/listdir.cc +++ b/examples/listdir.cc @@ -1,6 +1,6 @@ /** @example listdir.cc * - * A simple example of integration of std::filesystem with ranges. + * A simple example of using ostd::path and ostd::path::fs. */ #include diff --git a/ostd/path.hh b/ostd/path.hh index e5e3231..eb6e7fb 100644 --- a/ostd/path.hh +++ b/ostd/path.hh @@ -1001,8 +1001,24 @@ private: std::shared_ptr p_impl; }; -OSTD_EXPORT path cwd(); -OSTD_EXPORT path home(); +OSTD_EXPORT path current_path(); +OSTD_EXPORT path home_path(); +OSTD_EXPORT path temp_path(); + +OSTD_EXPORT path absolute(path const &p); + +OSTD_EXPORT path canonical(path const &p); +OSTD_EXPORT path weakly_canonical(path const &p); + +OSTD_EXPORT path relative(path const &p, path const &base = current_path()); + +inline bool exists(file_status s) noexcept { + return (status_known(s) && (s.type() != file_type::not_found)); +} + +OSTD_EXPORT bool exists(path const &p); + +OSTD_EXPORT bool equivalent(path const &p1, path const &p2); /** @} */ diff --git a/src/posix/path.cc b/src/posix/path.cc index 8963ae0..8da7f52 100644 --- a/src/posix/path.cc +++ b/src/posix/path.cc @@ -4,9 +4,14 @@ */ #include +#include #include +#include +#include #include +#include + #include "ostd/path.hh" namespace ostd { @@ -60,6 +65,9 @@ static file_type mode_to_type(mode_t mode) { OSTD_EXPORT file_status status(path const &p) { struct stat sb; if (stat(p.string().data(), &sb) < 0) { + if (errno == ENOENT) { + return file_status{file_type::not_found, perms::none}; + } /* FIXME: throw */ abort(); } @@ -69,6 +77,9 @@ OSTD_EXPORT file_status status(path const &p) { OSTD_EXPORT file_status symlink_status(path const &p) { struct stat sb; if (lstat(p.string().data(), &sb) < 0) { + if (errno == ENOENT) { + return file_status{file_type::not_found, perms::none}; + } /* FIXME: throw */ abort(); } @@ -187,3 +198,130 @@ OSTD_EXPORT void rdir_range_impl::read_next() { } /* namespace detail */ } /* namesapce fs */ } /* namespace ostd */ + +namespace ostd { +namespace fs { + +OSTD_EXPORT path current_path() { + auto pmax = pathconf(".", _PC_PATH_MAX); + std::vector rbuf; + if (pmax > 0) { + rbuf.reserve((pmax > 1024) ? 1024 : pmax); + } + for (;;) { + auto p = getcwd(rbuf.data(), rbuf.capacity()); + if (!p) { + if (errno != ERANGE) { + abort(); + } + rbuf.reserve(rbuf.capacity() * 2); + continue; + } + break; + } + return path{string_range{rbuf.data()}}; +} + +OSTD_EXPORT path home_path() { + char const *hdir = getenv("HOME"); + if (hdir) { + return path{string_range{hdir}}; + } + struct passwd pwd; + struct passwd *ret; + std::vector buf; + long bufs = sysconf(_SC_GETPW_R_SIZE_MAX); + if (bufs < 0) { + bufs = 2048; + } + buf.reserve(bufs); + getpwuid_r(getuid(), &pwd, buf.data(), buf.capacity(), &ret); + if (!ret) { + abort(); + } + return path{string_range{pwd.pw_dir}}; +} + +OSTD_EXPORT path temp_path() { + char const *envs[] = { "TMPDIR", "TMP", "TEMP", "TEMPDIR", NULL }; + for (char const **p = envs; *p; ++p) { + char const *tdir = getenv(*p); + if (tdir) { + return path{string_range{tdir}}; + } + } + return path{"/tmp"}; +} + +OSTD_EXPORT path absolute(path const &p) { + if (p.is_absolute()) { + return p; + } + return current_path() / p; +} + +OSTD_EXPORT path canonical(path const &p) { + /* TODO: legacy system support */ + char *rp = realpath(p.string().data(), nullptr); + if (!rp) { + abort(); + } + path ret{string_range{rp}}; + free(rp); + return ret; +} + +static inline bool try_access(path const &p) { + bool ret = !access(p.string().data(), F_OK); + if (!ret && (errno != ENOENT)) { + abort(); + } + return ret; +} + +OSTD_EXPORT path weakly_canonical(path const &p) { + if (try_access(p)) { + return canonical(p); + } + path cp{p}; + for (;;) { + if (!cp.has_name()) { + /* went all the way back to root */ + return p; + } + cp.remove_name(); + if (try_access(cp)) { + break; + } + } + /* cp refers to an existing section, canonicalize */ + path ret = canonical(cp); + auto pstr = p.string(); + /*a append the unresolved rest */ + ret.append(pstr.slice(cp.string().size(), pstr.size())); + return ret; +} + +OSTD_EXPORT path relative(path const &p, path const &base) { + return weakly_canonical(p).relative_to(weakly_canonical(base)); +} + +OSTD_EXPORT bool exists(path const &p) { + return try_access(p); +} + +OSTD_EXPORT bool equivalent(path const &p1, path const &p2) { + struct stat sb; + if (stat(p1.string().data(), &sb) < 0) { + abort(); + } + auto stdev = sb.st_dev; + auto stino = sb.st_ino; + if (stat(p2.string().data(), &sb) < 0) { + abort(); + } + return ((sb.st_dev == stdev) && (sb.st_ino == stino)); +} + +} /* namespace fs */ +} /* namespace ostd */