2018-04-15 21:23:19 +00:00
|
|
|
/* Path implementation bits.
|
|
|
|
*
|
|
|
|
* This file is part of libostd. See COPYING.md for futher information.
|
|
|
|
*/
|
|
|
|
|
2018-04-19 00:10:35 +00:00
|
|
|
/* mainly only for glibc, but it's harmless elsewhere */
|
2018-04-19 00:16:35 +00:00
|
|
|
|
|
|
|
#ifdef _POSIX_C_SOURCE
|
|
|
|
#undef _POSIX_C_SOURCE
|
|
|
|
#endif
|
|
|
|
|
2018-04-19 23:22:28 +00:00
|
|
|
/* only because glibc is awful, does not apply to other libcs */
|
|
|
|
#define _FILE_OFFSET_BITS 64
|
|
|
|
|
2018-04-19 00:10:35 +00:00
|
|
|
#define _POSIX_C_SOURCE 200809L
|
2018-04-19 00:16:35 +00:00
|
|
|
|
|
|
|
#ifndef _ATFILE_SOURCE
|
2018-04-19 00:10:35 +00:00
|
|
|
#define _ATFILE_SOURCE 1
|
2018-04-19 00:16:35 +00:00
|
|
|
#endif
|
2018-04-19 00:10:35 +00:00
|
|
|
|
2018-04-15 21:23:19 +00:00
|
|
|
#include <cstdlib>
|
2018-04-18 01:40:03 +00:00
|
|
|
#include <ctime>
|
2018-04-17 00:26:23 +00:00
|
|
|
#include <pwd.h>
|
2018-04-15 21:23:19 +00:00
|
|
|
#include <dirent.h>
|
2018-04-17 00:26:23 +00:00
|
|
|
#include <unistd.h>
|
|
|
|
#include <sys/types.h>
|
2018-04-15 22:19:48 +00:00
|
|
|
#include <sys/stat.h>
|
2018-04-15 21:23:19 +00:00
|
|
|
|
2018-04-17 00:26:23 +00:00
|
|
|
#include <vector>
|
|
|
|
|
2018-04-15 21:23:19 +00:00
|
|
|
#include "ostd/path.hh"
|
|
|
|
|
2018-04-16 01:10:15 +00:00
|
|
|
namespace ostd {
|
|
|
|
namespace fs {
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2018-04-18 08:55:56 +00:00
|
|
|
static std::error_code errno_ec() {
|
|
|
|
return std::error_code{errno, std::system_category()};
|
|
|
|
}
|
|
|
|
|
|
|
|
static std::error_code ec_from_int(int v) {
|
|
|
|
return std::error_code(v, std::system_category());
|
|
|
|
}
|
|
|
|
|
2018-04-19 23:22:28 +00:00
|
|
|
/* ugly test for whether nanosecond precision is available in stat
|
|
|
|
* could check for existence of st_mtime macro, but this is more reliable
|
|
|
|
*/
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
struct test_mtim {
|
|
|
|
template<typename TT, TT> struct test_stat;
|
|
|
|
|
|
|
|
struct fake_stat {
|
|
|
|
struct timespec st_mtim;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct stat_test: fake_stat, T {};
|
|
|
|
|
|
|
|
template<typename TT>
|
|
|
|
static char test(test_stat<struct timespec fake_stat::*, &TT::st_mtim> *);
|
|
|
|
|
|
|
|
template<typename>
|
|
|
|
static int test(...);
|
|
|
|
|
|
|
|
static constexpr bool value = (sizeof(test<stat_test>(0)) == sizeof(int));
|
|
|
|
};
|
|
|
|
|
|
|
|
template<bool B>
|
|
|
|
struct mtime_impl {
|
|
|
|
template<typename S>
|
|
|
|
static file_time_t get(S const &st) {
|
|
|
|
return std::chrono::system_clock::from_time_t(st.st_mtime);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template<>
|
|
|
|
struct mtime_impl<true> {
|
|
|
|
template<typename S>
|
|
|
|
static file_time_t get(S const &st) {
|
|
|
|
struct timespec ts = st.st_mtim;
|
|
|
|
auto d = std::chrono::seconds{ts.tv_sec} +
|
|
|
|
std::chrono::nanoseconds{ts.tv_nsec};
|
|
|
|
return file_time_t{
|
|
|
|
std::chrono::duration_cast<std::chrono::system_clock::duration>(d)
|
|
|
|
};
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
using mtime = mtime_impl<test_mtim<struct stat>::value>;
|
|
|
|
|
|
|
|
static file_status status_get(path const &p, int ret, struct stat &sb) {
|
|
|
|
if (ret < 0) {
|
2018-04-17 00:26:23 +00:00
|
|
|
if (errno == ENOENT) {
|
2018-04-19 23:22:28 +00:00
|
|
|
return file_status{
|
|
|
|
file_mode{file_type::not_found, perms::none},
|
|
|
|
file_time_t{}, 0, 0
|
|
|
|
};
|
2018-04-17 00:26:23 +00:00
|
|
|
}
|
2018-04-18 08:55:56 +00:00
|
|
|
throw fs_error{"stat failure", p, errno_ec()};
|
2018-04-16 01:10:15 +00:00
|
|
|
}
|
2018-04-19 23:22:28 +00:00
|
|
|
return file_status{
|
|
|
|
file_mode{mode_to_type(sb.st_mode), perms{sb.st_mode & 07777}},
|
|
|
|
mtime::get(sb),
|
|
|
|
std::uintmax_t(sb.st_size),
|
|
|
|
std::uintmax_t(sb.st_nlink)
|
|
|
|
};
|
2018-04-16 01:10:15 +00:00
|
|
|
}
|
|
|
|
|
2018-04-19 23:22:28 +00:00
|
|
|
OSTD_EXPORT file_status status(path const &p) {
|
2018-04-16 01:10:15 +00:00
|
|
|
struct stat sb;
|
2018-04-19 23:22:28 +00:00
|
|
|
return status_get(p, stat(p.string().data(), &sb), sb);
|
|
|
|
}
|
|
|
|
|
|
|
|
OSTD_EXPORT file_status symlink_status(path const &p) {
|
|
|
|
struct stat sb;
|
|
|
|
return status_get(p, lstat(p.string().data(), &sb), sb);
|
|
|
|
}
|
|
|
|
|
|
|
|
OSTD_EXPORT file_mode mode(path const &p) {
|
|
|
|
return status(p).mode();
|
|
|
|
}
|
|
|
|
|
|
|
|
OSTD_EXPORT file_mode symlink_mode(path const &p) {
|
|
|
|
return symlink_status(p).mode();
|
|
|
|
}
|
|
|
|
|
|
|
|
OSTD_EXPORT file_time_t last_write_time(path const &p) {
|
|
|
|
return status(p).last_write_time();
|
|
|
|
}
|
|
|
|
|
|
|
|
OSTD_EXPORT std::uintmax_t file_size(path const &p) {
|
|
|
|
return status(p).size();
|
|
|
|
}
|
|
|
|
|
|
|
|
OSTD_EXPORT std::uintmax_t hard_link_count(path const &p) {
|
|
|
|
return status(p).hard_link_count();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO: somehow feature-test for utimensat and fallback to utimes */
|
|
|
|
OSTD_EXPORT void last_write_time(path const &p, file_time_t new_time) {
|
|
|
|
auto d = new_time.time_since_epoch();
|
|
|
|
auto sec = std::chrono::floor<std::chrono::seconds>(d);
|
|
|
|
auto nsec = std::chrono::duration_cast<std::chrono::nanoseconds>(d - sec);
|
|
|
|
struct timespec times[2] = {
|
|
|
|
{0, UTIME_OMIT}, {time_t(sec.count()), long(nsec.count())}
|
|
|
|
};
|
|
|
|
if (utimensat(0, p.string().data(), times, 0)) {
|
|
|
|
throw fs_error{"utimensat failure", p, errno_ec()};
|
2018-04-16 01:10:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} /* namespace fs */
|
|
|
|
} /* namespace ostd */
|
|
|
|
|
2018-04-15 21:23:19 +00:00
|
|
|
namespace ostd {
|
|
|
|
namespace fs {
|
2018-04-15 23:58:59 +00:00
|
|
|
namespace detail {
|
2018-04-15 21:23:19 +00:00
|
|
|
|
2018-04-17 21:13:46 +00:00
|
|
|
static void dir_read_next(
|
|
|
|
DIR *dh, path &cur, file_mode &tp, path const &base
|
|
|
|
) {
|
2018-04-15 22:19:48 +00:00
|
|
|
struct dirent d;
|
|
|
|
struct dirent *o;
|
|
|
|
for (;;) {
|
|
|
|
if (readdir_r(dh, &d, &o)) {
|
2018-04-18 08:55:56 +00:00
|
|
|
throw fs_error{"readdir_r failure", base, errno_ec()};
|
2018-04-15 22:19:48 +00:00
|
|
|
}
|
|
|
|
if (!o) {
|
2018-04-17 21:13:46 +00:00
|
|
|
cur = path{};
|
2018-04-15 22:19:48 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
string_range nm{static_cast<char const *>(o->d_name)};
|
|
|
|
if ((nm == ".") || (nm == "..")) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
path p{base};
|
|
|
|
p.append(nm);
|
2018-04-17 21:13:46 +00:00
|
|
|
#ifdef DT_UNKNOWN
|
|
|
|
/* most systems have d_type */
|
|
|
|
file_mode md{mode_to_type(DTTOIF(o->d_type))};
|
|
|
|
#else
|
|
|
|
/* fallback mainly for legacy */
|
|
|
|
file_mode md = symlink_mode(p);
|
|
|
|
#endif
|
|
|
|
tp = md;
|
2018-04-17 01:05:00 +00:00
|
|
|
cur = std::move(p);
|
2018-04-15 22:19:48 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* dir range */
|
|
|
|
|
2018-04-15 23:58:59 +00:00
|
|
|
OSTD_EXPORT void dir_range_impl::open(path const &p) {
|
2018-04-15 21:23:19 +00:00
|
|
|
DIR *d = opendir(p.string().data());
|
|
|
|
if (!d) {
|
2018-04-18 08:55:56 +00:00
|
|
|
throw fs_error{"opendir failure", p, errno_ec()};
|
2018-04-15 21:23:19 +00:00
|
|
|
}
|
|
|
|
p_dir = p;
|
|
|
|
p_handle = d;
|
|
|
|
read_next();
|
|
|
|
}
|
|
|
|
|
2018-04-15 23:58:59 +00:00
|
|
|
OSTD_EXPORT void dir_range_impl::close() noexcept {
|
2018-04-15 21:23:19 +00:00
|
|
|
closedir(static_cast<DIR *>(p_handle));
|
|
|
|
}
|
|
|
|
|
2018-04-15 23:58:59 +00:00
|
|
|
OSTD_EXPORT void dir_range_impl::read_next() {
|
2018-04-17 21:13:46 +00:00
|
|
|
path cur;
|
|
|
|
file_mode tp;
|
|
|
|
dir_read_next(static_cast<DIR *>(p_handle), cur, tp, p_dir);
|
|
|
|
p_current = directory_entry{std::move(cur), tp};
|
2018-04-15 22:19:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* recursive dir range */
|
|
|
|
|
2018-04-15 23:58:59 +00:00
|
|
|
OSTD_EXPORT void rdir_range_impl::open(path const &p) {
|
2018-04-15 22:19:48 +00:00
|
|
|
DIR *d = opendir(p.string().data());
|
|
|
|
if (!d) {
|
2018-04-18 08:55:56 +00:00
|
|
|
throw fs_error{"opendir failure", p, errno_ec()};
|
2018-04-15 22:19:48 +00:00
|
|
|
}
|
|
|
|
p_dir = p;
|
2018-04-15 23:58:59 +00:00
|
|
|
p_handles.push(d);
|
2018-04-15 22:19:48 +00:00
|
|
|
read_next();
|
|
|
|
}
|
|
|
|
|
2018-04-15 23:58:59 +00:00
|
|
|
OSTD_EXPORT void rdir_range_impl::close() noexcept {
|
2018-04-15 22:19:48 +00:00
|
|
|
while (!p_handles.empty()) {
|
|
|
|
closedir(static_cast<DIR *>(p_handles.top()));
|
|
|
|
p_handles.pop();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-15 23:58:59 +00:00
|
|
|
OSTD_EXPORT void rdir_range_impl::read_next() {
|
|
|
|
if (p_handles.empty()) {
|
2018-04-15 22:19:48 +00:00
|
|
|
return;
|
|
|
|
}
|
2018-04-17 21:13:46 +00:00
|
|
|
path curd;
|
|
|
|
file_mode tp;
|
2018-04-16 01:20:23 +00:00
|
|
|
/* can't reuse info from dirent because we need to expand symlinks */
|
2018-04-17 21:13:46 +00:00
|
|
|
if (p_current.is_directory()) {
|
2018-04-15 23:10:03 +00:00
|
|
|
/* directory, recurse into it and if it contains stuff, return */
|
|
|
|
DIR *nd = opendir(p_current.path().string().data());
|
|
|
|
if (!nd) {
|
2018-04-18 08:55:56 +00:00
|
|
|
throw fs_error{"opendir failure", p_current, errno_ec()};
|
2018-04-15 21:23:19 +00:00
|
|
|
}
|
2018-04-17 21:13:46 +00:00
|
|
|
directory_entry based = p_current;
|
|
|
|
dir_read_next(nd, curd, tp, based);
|
|
|
|
if (!curd.empty()) {
|
2018-04-15 23:10:03 +00:00
|
|
|
p_dir = based;
|
2018-04-15 23:58:59 +00:00
|
|
|
p_handles.push(nd);
|
2018-04-17 21:13:46 +00:00
|
|
|
p_current = directory_entry{std::move(curd), tp};
|
2018-04-15 21:23:19 +00:00
|
|
|
return;
|
2018-04-15 23:10:03 +00:00
|
|
|
} else {
|
|
|
|
closedir(nd);
|
2018-04-15 21:23:19 +00:00
|
|
|
}
|
2018-04-15 22:19:48 +00:00
|
|
|
}
|
2018-04-15 23:10:03 +00:00
|
|
|
/* didn't recurse into a directory, go to next file */
|
2018-04-17 21:13:46 +00:00
|
|
|
dir_read_next(static_cast<DIR *>(p_handles.top()), curd, tp, p_dir);
|
|
|
|
p_current = directory_entry{std::move(curd), tp};
|
2018-04-15 23:10:03 +00:00
|
|
|
/* end of dir, pop while at it */
|
|
|
|
if (p_current.path().empty()) {
|
2018-04-15 23:58:59 +00:00
|
|
|
closedir(static_cast<DIR *>(p_handles.top()));
|
|
|
|
p_handles.pop();
|
|
|
|
if (!p_handles.empty()) {
|
2018-04-15 23:10:03 +00:00
|
|
|
/* got back to another dir, read next so it's valid */
|
|
|
|
p_dir.remove_name();
|
2018-04-17 21:13:46 +00:00
|
|
|
dir_read_next(static_cast<DIR *>(p_handles.top()), curd, tp, p_dir);
|
|
|
|
p_current = directory_entry{std::move(curd), tp};
|
2018-04-15 23:10:03 +00:00
|
|
|
}
|
2018-04-15 21:23:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-15 23:58:59 +00:00
|
|
|
} /* namespace detail */
|
2018-04-15 21:23:19 +00:00
|
|
|
} /* namesapce fs */
|
|
|
|
} /* namespace ostd */
|
2018-04-17 00:26:23 +00:00
|
|
|
|
|
|
|
namespace ostd {
|
|
|
|
namespace fs {
|
|
|
|
|
|
|
|
OSTD_EXPORT path current_path() {
|
|
|
|
auto pmax = pathconf(".", _PC_PATH_MAX);
|
|
|
|
std::vector<char> rbuf;
|
|
|
|
if (pmax > 0) {
|
|
|
|
rbuf.reserve((pmax > 1024) ? 1024 : pmax);
|
|
|
|
}
|
|
|
|
for (;;) {
|
|
|
|
auto p = getcwd(rbuf.data(), rbuf.capacity());
|
|
|
|
if (!p) {
|
|
|
|
if (errno != ERANGE) {
|
2018-04-18 08:55:56 +00:00
|
|
|
throw fs_error{"getcwd failure", errno_ec()};
|
2018-04-17 00:26:23 +00:00
|
|
|
}
|
|
|
|
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<char> 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) {
|
2018-04-18 08:55:56 +00:00
|
|
|
throw fs_error{"getpwuid_r failure", errno_ec()};
|
2018-04-17 00:26:23 +00:00
|
|
|
}
|
|
|
|
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"};
|
|
|
|
}
|
|
|
|
|
2018-04-18 01:40:03 +00:00
|
|
|
OSTD_EXPORT void current_path(path const &p) {
|
|
|
|
if (chdir(p.string().data())) {
|
2018-04-18 08:55:56 +00:00
|
|
|
throw fs_error{"chdir failure", p, errno_ec()};
|
2018-04-18 01:40:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-17 00:26:23 +00:00
|
|
|
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) {
|
2018-04-18 08:55:56 +00:00
|
|
|
throw fs_error{"realpath failure", p, errno_ec()};
|
2018-04-17 00:26:23 +00:00
|
|
|
}
|
|
|
|
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)) {
|
2018-04-18 08:55:56 +00:00
|
|
|
throw fs_error{"access failure", p, errno_ec()};
|
2018-04-17 00:26:23 +00:00
|
|
|
}
|
|
|
|
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);
|
2018-04-18 16:40:45 +00:00
|
|
|
string_range pstr = p;
|
2018-04-17 00:26:23 +00:00
|
|
|
/*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) {
|
2018-04-18 08:55:56 +00:00
|
|
|
throw fs_error{"stat failure", p1, p2, errno_ec()};
|
2018-04-17 00:26:23 +00:00
|
|
|
}
|
|
|
|
auto stdev = sb.st_dev;
|
|
|
|
auto stino = sb.st_ino;
|
|
|
|
if (stat(p2.string().data(), &sb) < 0) {
|
2018-04-18 08:55:56 +00:00
|
|
|
throw fs_error{"stat failure", p1, p2, errno_ec()};
|
2018-04-17 00:26:23 +00:00
|
|
|
}
|
|
|
|
return ((sb.st_dev == stdev) && (sb.st_ino == stino));
|
|
|
|
}
|
|
|
|
|
2018-04-18 01:40:03 +00:00
|
|
|
static bool mkdir_p(path const &p, mode_t mode) {
|
|
|
|
if (mkdir(p.string().data(), mode)) {
|
|
|
|
if (errno != EEXIST) {
|
2018-04-18 08:55:56 +00:00
|
|
|
throw fs_error{"mkdir failure", p, errno_ec()};
|
2018-04-18 01:40:03 +00:00
|
|
|
}
|
2018-04-18 08:55:56 +00:00
|
|
|
int eno = errno;
|
2018-04-18 01:40:03 +00:00
|
|
|
auto tp = fs::mode(p);
|
|
|
|
if (tp.type() != file_type::directory) {
|
2018-04-18 08:55:56 +00:00
|
|
|
throw fs_error{"mkdir failure", p, ec_from_int(eno)};
|
2018-04-18 01:40:03 +00:00
|
|
|
abort();
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
OSTD_EXPORT bool create_directory(path const &p) {
|
|
|
|
return mkdir_p(p, 0777);
|
|
|
|
}
|
|
|
|
|
|
|
|
OSTD_EXPORT bool create_directory(path const &p, path const &ep) {
|
|
|
|
return mkdir_p(p, mode_t(fs::mode(ep).permissions()));
|
|
|
|
}
|
|
|
|
|
|
|
|
OSTD_EXPORT bool create_directories(path const &p) {
|
|
|
|
if (p.has_parent()) {
|
|
|
|
create_directories(p.parent());
|
|
|
|
}
|
|
|
|
return create_directory(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
OSTD_EXPORT bool remove(path const &p) {
|
|
|
|
if (!exists(p)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (::remove(p.string().data())) {
|
2018-04-18 08:55:56 +00:00
|
|
|
throw fs_error{"remove failure", p, errno_ec()};
|
2018-04-18 01:40:03 +00:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
OSTD_EXPORT std::uintmax_t remove_all(path const &p) {
|
|
|
|
std::uintmax_t ret = 0;
|
|
|
|
if (is_directory(p)) {
|
|
|
|
fs::directory_range ds{p};
|
|
|
|
for (auto &v: ds) {
|
|
|
|
ret += remove_all(v.path());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ret += remove(p);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
OSTD_EXPORT void rename(path const &op, path const &np) {
|
|
|
|
if (::rename(op.string().data(), np.string().data())) {
|
2018-04-18 08:55:56 +00:00
|
|
|
throw fs_error{"rename failure", op, np, errno_ec()};
|
2018-04-18 01:40:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-17 00:26:23 +00:00
|
|
|
} /* namespace fs */
|
|
|
|
} /* namespace ostd */
|