From efd154a6af61e80be1b0c46642cefd73cc83d7ed Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 29 Sep 2015 13:04:29 +0200 Subject: Add auto_rmfile and auto_rmdir --- butl/filesystem | 48 ++++++++++++++++++++++++++++++++++++++++-------- butl/filesystem.cxx | 8 ++++---- butl/filesystem.ixx | 35 +++++++++++++++++++++++++++++++++-- 3 files changed, 77 insertions(+), 14 deletions(-) diff --git a/butl/filesystem b/butl/filesystem index 5b1c656..c3340d7 100644 --- a/butl/filesystem +++ b/butl/filesystem @@ -55,20 +55,20 @@ namespace butl mkdir_status try_mkdir_p (const dir_path&, mode_t = 0777); - // Try to remove the directory returning not_exist if it does not - // exist and not_empty if it is not empty. All other errors are - // reported by throwing std::system_error. + // Try to remove the directory returning not_exist if it does not exist + // and not_empty if it is not empty. Unless ignore_error is true, all + // other errors are reported by throwing std::system_error. // enum class rmdir_status {success, not_exist, not_empty}; rmdir_status - try_rmdir (const dir_path&); + try_rmdir (const dir_path&, bool ignore_error = false); // The '-r' (recursive) version of the above. Note that it will // never return not_empty. // rmdir_status - try_rmdir_r (const dir_path&); + try_rmdir_r (const dir_path&, bool ignore_error = false); // As above but throws rather than returns not_exist if the directory // does not exist, so check before calling. If the second argument is @@ -78,13 +78,45 @@ namespace butl rmdir_r (const dir_path&, bool dir = true); // Try to remove the file (or symlinks) returning not_exist if - // it does not exist. All other errors are reported by throwing - // std::system_error. + // it does not exist. Unless ignore_error is true, all other + // errors are reported by throwing std::system_error. // enum class rmfile_status {success, not_exist}; rmfile_status - try_rmfile (const path&); + try_rmfile (const path&, bool ignore_error = false); + + // Automatically try to remove the path on destruction unless cancelled. + // Since the non-cancelled destruction will normally happen as a result + // of an exception, the failure to remove the path is silently ignored. + // + template + struct auto_rm + { + explicit + auto_rm (P p = P ()): path_ (std::move (p)) {} + + void + cancel () {path_ = P ();} + + const P& + path () {return path_;} + + // Movable-only type. Move-assignment cancels the lhs object. + // + auto_rm (auto_rm&&); + auto_rm& operator= (auto_rm&&); + auto_rm (const auto_rm&) = delete; + auto_rm& operator= (const auto_rm&) = delete; + + ~auto_rm (); + + private: + P path_; + }; + + using auto_rmfile = auto_rm; + using auto_rmdir = auto_rm; // Note: recursive (rm_r). // Return timestamp_nonexistent if the entry at the specified path // does not exist or is not a path. All other errors are reported diff --git a/butl/filesystem.cxx b/butl/filesystem.cxx index f1341ee..5a43cd2 100644 --- a/butl/filesystem.cxx +++ b/butl/filesystem.cxx @@ -81,7 +81,7 @@ namespace butl } rmdir_status - try_rmdir (const dir_path& p) + try_rmdir (const dir_path& p, bool ignore_error) { rmdir_status r (rmdir_status::success); @@ -91,7 +91,7 @@ namespace butl r = rmdir_status::not_exist; else if (errno == ENOTEMPTY || errno == EEXIST) r = rmdir_status::not_empty; - else + else if (!ignore_error) throw system_error (errno, system_category ()); } @@ -125,7 +125,7 @@ namespace butl } rmfile_status - try_rmfile (const path& p) + try_rmfile (const path& p, bool ignore_error) { rmfile_status r (rmfile_status::success); @@ -133,7 +133,7 @@ namespace butl { if (errno == ENOENT || errno == ENOTDIR) r = rmfile_status::not_exist; - else + else if (!ignore_error) throw system_error (errno, system_category ()); } diff --git a/butl/filesystem.ixx b/butl/filesystem.ixx index 256cbba..3b72b4a 100644 --- a/butl/filesystem.ixx +++ b/butl/filesystem.ixx @@ -5,16 +5,47 @@ namespace butl { inline rmdir_status - try_rmdir_r (const dir_path& p) + try_rmdir_r (const dir_path& p, bool ignore_error) { bool e (dir_exists (p)); //@@ What if it exists but is not a directory? if (e) - rmdir_r (p); + rmdir_r (p, ignore_error); return e ? rmdir_status::success : rmdir_status::not_exist; } + // auto_rm + // + template + inline auto_rm

:: + auto_rm (auto_rm&& x) + : path_ (std::move (x.path_)) + { + x.cancel (); + } + + template + inline auto_rm

& auto_rm

:: + operator= (auto_rm&& x) + { + if (this != &x) + { + path_ = std::move (x.path_); + x.cancel (); + } + + return *this; + } + + template <> + inline auto_rm:: + ~auto_rm () {if (!path_.empty ()) try_rmfile (path_, true);} + + template <> + inline auto_rm:: + ~auto_rm () {if (!path_.empty ()) try_rmdir_r (path_, true);} + // permissions // inline permissions operator& (permissions x, permissions y) {return x &= y;} -- cgit v1.1