add status/symlink_status/file_size/hard_link_count
parent
af87ee2a8b
commit
a85cf13c56
84
ostd/path.hh
84
ostd/path.hh
|
@ -1224,6 +1224,9 @@ namespace fs {
|
||||||
* @{
|
* @{
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/** @brief The time point used to represent file access times. */
|
||||||
|
using file_time_t = std::chrono::time_point<std::chrono::system_clock>;
|
||||||
|
|
||||||
/** @brief An exception thrown by filesystem operations.
|
/** @brief An exception thrown by filesystem operations.
|
||||||
*
|
*
|
||||||
* Unlike ostd::path_error, this is thrown by operations doing syscalls.
|
* Unlike ostd::path_error, this is thrown by operations doing syscalls.
|
||||||
|
@ -1361,22 +1364,54 @@ public:
|
||||||
return file_type(p_val >> 16);
|
return file_type(p_val >> 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
void type(file_type type) noexcept {
|
|
||||||
p_val = ((p_val & 0xFFFF) | (UT(type) << 16));
|
|
||||||
}
|
|
||||||
|
|
||||||
perms permissions() const noexcept {
|
perms permissions() const noexcept {
|
||||||
return perms(p_val & 0xFFFF);
|
return perms(p_val & 0xFFFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
void permissions(perms perm) noexcept {
|
|
||||||
p_val = ((p_val & ~UT(0xFFFF)) | UT(perm));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct file_status {
|
||||||
|
file_status(
|
||||||
|
file_mode mode, file_time_t mtime,
|
||||||
|
std::uintmax_t size, std::uintmax_t nlinks
|
||||||
|
):
|
||||||
|
p_links(nlinks), p_size(size), p_mtime(mtime), p_mode(mode)
|
||||||
|
{}
|
||||||
|
|
||||||
|
file_mode mode() const noexcept {
|
||||||
|
return p_mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
file_time_t last_write_time() const noexcept {
|
||||||
|
return p_mtime;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uintmax_t hard_link_count() const noexcept {
|
||||||
|
return p_links;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uintmax_t size() const noexcept {
|
||||||
|
return p_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::uintmax_t p_links;
|
||||||
|
std::uintmax_t p_size;
|
||||||
|
file_time_t p_mtime;
|
||||||
|
file_mode p_mode;
|
||||||
|
};
|
||||||
|
|
||||||
|
OSTD_EXPORT file_status status(path const &p);
|
||||||
|
OSTD_EXPORT file_status symlink_status(path const &p);
|
||||||
|
|
||||||
OSTD_EXPORT file_mode mode(path const &p);
|
OSTD_EXPORT file_mode mode(path const &p);
|
||||||
OSTD_EXPORT file_mode symlink_mode(path const &p);
|
OSTD_EXPORT file_mode symlink_mode(path const &p);
|
||||||
|
|
||||||
|
OSTD_EXPORT std::uintmax_t file_size(path const &p);
|
||||||
|
OSTD_EXPORT std::uintmax_t hard_link_count(path const &p);
|
||||||
|
|
||||||
|
OSTD_EXPORT file_time_t last_write_time(path const &p);
|
||||||
|
OSTD_EXPORT void last_write_time(path const &p, file_time_t new_time);
|
||||||
|
|
||||||
inline bool is_block_file(file_mode st) noexcept {
|
inline bool is_block_file(file_mode st) noexcept {
|
||||||
return st.type() == file_type::block;
|
return st.type() == file_type::block;
|
||||||
}
|
}
|
||||||
|
@ -1449,34 +1484,6 @@ inline bool mode_known(path const &p) {
|
||||||
return mode_known(mode(p));
|
return mode_known(mode(p));
|
||||||
}
|
}
|
||||||
|
|
||||||
struct file_status {
|
|
||||||
file_status() {}
|
|
||||||
file_status(ostd::path const &p): p_path(p) {}
|
|
||||||
|
|
||||||
ostd::path const &path() const noexcept {
|
|
||||||
return p_path;
|
|
||||||
}
|
|
||||||
|
|
||||||
operator ostd::path const &() const noexcept {
|
|
||||||
return p_path;
|
|
||||||
}
|
|
||||||
|
|
||||||
void clear() {
|
|
||||||
p_path.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void assign(ostd::path const &p) {
|
|
||||||
p_path = p;
|
|
||||||
}
|
|
||||||
|
|
||||||
void assign(ostd::path &&p) {
|
|
||||||
p_path = std::move(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
ostd::path p_path{};
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
struct dir_range_impl;
|
struct dir_range_impl;
|
||||||
struct rdir_range_impl;
|
struct rdir_range_impl;
|
||||||
|
@ -1684,11 +1691,6 @@ OSTD_EXPORT std::uintmax_t remove_all(path const &p);
|
||||||
|
|
||||||
OSTD_EXPORT void rename(path const &op, path const &np);
|
OSTD_EXPORT void rename(path const &op, path const &np);
|
||||||
|
|
||||||
using file_time_t = std::chrono::time_point<std::chrono::system_clock>;
|
|
||||||
|
|
||||||
OSTD_EXPORT file_time_t last_write_time(path const &p);
|
|
||||||
OSTD_EXPORT void last_write_time(path const &p, file_time_t new_time);
|
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
OSTD_EXPORT void glob_match_impl(
|
OSTD_EXPORT void glob_match_impl(
|
||||||
void (*out)(path const &, void *),
|
void (*out)(path const &, void *),
|
||||||
|
|
|
@ -9,6 +9,9 @@
|
||||||
#undef _POSIX_C_SOURCE
|
#undef _POSIX_C_SOURCE
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* only because glibc is awful, does not apply to other libcs */
|
||||||
|
#define _FILE_OFFSET_BITS 64
|
||||||
|
|
||||||
#define _POSIX_C_SOURCE 200809L
|
#define _POSIX_C_SOURCE 200809L
|
||||||
|
|
||||||
#ifndef _ATFILE_SOURCE
|
#ifndef _ATFILE_SOURCE
|
||||||
|
@ -51,26 +54,111 @@ static std::error_code ec_from_int(int v) {
|
||||||
return std::error_code(v, std::system_category());
|
return std::error_code(v, std::system_category());
|
||||||
}
|
}
|
||||||
|
|
||||||
OSTD_EXPORT file_mode mode(path const &p) {
|
/* ugly test for whether nanosecond precision is available in stat
|
||||||
struct stat sb;
|
* could check for existence of st_mtime macro, but this is more reliable
|
||||||
if (stat(p.string().data(), &sb) < 0) {
|
*/
|
||||||
|
|
||||||
|
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) {
|
||||||
if (errno == ENOENT) {
|
if (errno == ENOENT) {
|
||||||
return file_mode{file_type::not_found, perms::none};
|
return file_status{
|
||||||
|
file_mode{file_type::not_found, perms::none},
|
||||||
|
file_time_t{}, 0, 0
|
||||||
|
};
|
||||||
}
|
}
|
||||||
throw fs_error{"stat failure", p, errno_ec()};
|
throw fs_error{"stat failure", p, errno_ec()};
|
||||||
}
|
}
|
||||||
return file_mode{mode_to_type(sb.st_mode), perms(sb.st_mode & 07777)};
|
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)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
OSTD_EXPORT file_status status(path const &p) {
|
||||||
|
struct stat sb;
|
||||||
|
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) {
|
OSTD_EXPORT file_mode symlink_mode(path const &p) {
|
||||||
struct stat sb;
|
return symlink_status(p).mode();
|
||||||
if (lstat(p.string().data(), &sb) < 0) {
|
}
|
||||||
if (errno == ENOENT) {
|
|
||||||
return file_mode{file_type::not_found, perms::none};
|
OSTD_EXPORT file_time_t last_write_time(path const &p) {
|
||||||
}
|
return status(p).last_write_time();
|
||||||
throw fs_error{"lstat failure", p, errno_ec()};
|
}
|
||||||
|
|
||||||
|
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()};
|
||||||
}
|
}
|
||||||
return file_mode{mode_to_type(sb.st_mode), perms(sb.st_mode & 07777)};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} /* namespace fs */
|
} /* namespace fs */
|
||||||
|
@ -387,72 +475,5 @@ OSTD_EXPORT void rename(path const &op, path const &np) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 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>;
|
|
||||||
|
|
||||||
OSTD_EXPORT file_time_t last_write_time(path const &p) {
|
|
||||||
struct stat sb;
|
|
||||||
if (stat(p.string().data(), &sb) < 0) {
|
|
||||||
throw fs_error{"stat failure", p, errno_ec()};
|
|
||||||
}
|
|
||||||
return mtime::get(sb);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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()};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} /* namespace fs */
|
} /* namespace fs */
|
||||||
} /* namespace ostd */
|
} /* namespace ostd */
|
||||||
|
|
Loading…
Reference in New Issue