// file : butl/filesystem.cxx -*- C++ -*- // copyright : Copyright (c) 2014-2015 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file #include #include // stat, rmdir(), unlink() #include // stat #include // stat, lstat(), S_IS*, mkdir() #include // unique_ptr #include using namespace std; namespace butl { // Figuring out whether we have the nanoseconds in some form. // template inline constexpr auto nsec (const S* s) -> decltype(s->st_mtim.tv_nsec) { return s->st_mtim.tv_nsec; // POSIX (GNU/Linux, Solaris). } template inline constexpr auto nsec (const S* s) -> decltype(s->st_mtimespec.tv_nsec) { return s->st_mtimespec.tv_nsec; // MacOS X. } template inline constexpr auto nsec (const S* s) -> decltype(s->st_mtime_n) { return s->st_mtime_n; // AIX 5.2 and later. } template inline constexpr int nsec (...) {return 0;} timestamp file_mtime (const path& p) { struct stat s; if (::stat (p.string ().c_str (), &s) != 0) { if (errno == ENOENT || errno == ENOTDIR) return timestamp_nonexistent; else throw system_error (errno, system_category ()); } return S_ISREG (s.st_mode) ? system_clock::from_time_t (s.st_mtime) + chrono::duration_cast ( chrono::nanoseconds (nsec (&s))) : timestamp_nonexistent; } bool dir_exists (const path& p) { struct stat s; if (::stat (p.string ().c_str (), &s) != 0) { if (errno == ENOENT || errno == ENOTDIR) return false; else throw system_error (errno, system_category ()); } return S_ISDIR (s.st_mode); } bool file_exists (const path& p) { struct stat s; if (::stat (p.string ().c_str (), &s) != 0) { if (errno == ENOENT || errno == ENOTDIR) return false; else throw system_error (errno, system_category ()); } return S_ISREG (s.st_mode); } mkdir_status try_mkdir (const path& p, mode_t m) { mkdir_status r (mkdir_status::success); if (::mkdir (p.string ().c_str (), m) != 0) { int e (errno); // EEXIST means the path already exists but not necessarily as // a directory. // if (e == EEXIST && dir_exists (p)) return mkdir_status::already_exists; else throw system_error (e, system_category ()); } return r; } mkdir_status try_mkdir_p (const path& p, mode_t m) { if (!p.root ()) { path d (p.directory ()); if (!dir_exists (d)) try_mkdir_p (d, m); } return try_mkdir (p, m); } rmdir_status try_rmdir (const path& p) { rmdir_status r (rmdir_status::success); if (::rmdir (p.string ().c_str ()) != 0) { if (errno == ENOENT) r = rmdir_status::not_exist; else if (errno == ENOTEMPTY || errno == EEXIST) r = rmdir_status::not_empty; else throw system_error (errno, system_category ()); } return r; } rmfile_status try_rmfile (const path& p) { rmfile_status r (rmfile_status::success); if (::unlink (p.string ().c_str ()) != 0) { if (errno == ENOENT || errno == ENOTDIR) r = rmfile_status::not_exist; else throw system_error (errno, system_category ()); } return r; } #ifndef _WIN32 // dir_entry // entry_type dir_entry:: type (bool link) const { path_type p (b_ / p_); struct stat s; if ((link ? ::stat (p.string ().c_str (), &s) : ::lstat (p.string ().c_str (), &s)) != 0) { throw system_error (errno, system_category ()); } entry_type r; if (S_ISREG (s.st_mode)) r = entry_type::regular; else if (S_ISDIR (s.st_mode)) r = entry_type::directory; else if (S_ISLNK (s.st_mode)) r = entry_type::symlink; else r = entry_type::other; return r; } // dir_iterator // struct dir_deleter { void operator() (DIR* p) const {if (p != nullptr) ::closedir (p);} }; dir_iterator:: dir_iterator (const dir_path& d) { unique_ptr h (::opendir (d.string ().c_str ())); h_ = h.get (); if (h_ == nullptr) throw system_error (errno, system_category ()); next (); if (h_ != nullptr) e_.b_ = d; h.release (); } template inline /*constexpr*/ entry_type d_type (const D* d, decltype(d->d_type)*) { switch (d->d_type) { #ifdef DT_DIR case DT_DIR: return entry_type::directory; #endif #ifdef DT_REG case DT_REG: return entry_type::regular; #endif #ifdef DT_LNK case DT_LNK: return entry_type::symlink; #endif #ifdef DT_BLK case DT_BLK: #endif #ifdef DT_CHR case DT_CHR: #endif #ifdef DT_FIFO case DT_FIFO: #endif #ifdef DT_SOCK case DT_SOCK: #endif return entry_type::other; default: return entry_type::unknown; } } template inline constexpr entry_type d_type (...) {return entry_type::unknown;} void dir_iterator:: next () { for (;;) { errno = 0; if (struct dirent* de = ::readdir (h_)) { const char* n (de->d_name); // Skip '.' and '..'. // if (n[0] == '.' && (n[1] == '\0' || (n[1] == '.' && n[2] == '\0'))) continue; e_.p_ = path (n); e_.t_ = d_type (de, nullptr); e_.lt_ = entry_type::unknown; } else if (errno == 0) { // End of stream. // ::closedir (h_); h_ = nullptr; } else throw system_error (errno, system_category ()); break; } } #else #endif }