From 31e91691e815074ebdb49d258967e2b2a0bfc965 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Fri, 10 Feb 2017 16:10:33 +0300 Subject: Add path_entry(), fixes for path --- butl/filesystem | 37 +++++++++++------ butl/filesystem.cxx | 115 +++++++++++++++++++++++++++++++--------------------- butl/path | 16 ++++++++ butl/path.cxx | 23 ++++++++++- butl/path.ixx | 8 +--- 5 files changed, 132 insertions(+), 67 deletions(-) diff --git a/butl/filesystem b/butl/filesystem index c030ecd..ef715c6 100644 --- a/butl/filesystem +++ b/butl/filesystem @@ -20,9 +20,9 @@ typedef int mode_t; #endif -#include // ptrdiff_t -#include // uint16_t -#include // move() +#include // ptrdiff_t +#include // uint16_t +#include // move(), pair #include #include @@ -61,6 +61,28 @@ namespace butl entry_exists (const path& p, bool fs = false) { return entry_exists (p.string ().c_str (), fs);} + // Filesystem entry type. + // + enum class entry_type + { + unknown, + regular, + directory, + symlink, + other + }; + + // Return a flag indicating if the path is to an existing file system entry + // and its type if so. Note that by default this function doesn't follow + // symlinks. + // + LIBBUTL_EXPORT std::pair + path_entry (const char*, bool follow_symlinks = false); + + inline std::pair + path_entry (const path& p, bool fs = false) { + return path_entry (p.string ().c_str (), fs);} + // Return true if the directory is empty. Note that the path must exist // and be a directory. // @@ -279,15 +301,6 @@ namespace butl // Directory entry iteration. // - enum class entry_type - { - unknown, - regular, - directory, - symlink, - other - }; - class LIBBUTL_EXPORT dir_entry { public: diff --git a/butl/filesystem.cxx b/butl/filesystem.cxx index d4bb487..e7e54a8 100644 --- a/butl/filesystem.cxx +++ b/butl/filesystem.cxx @@ -34,78 +34,101 @@ using namespace std; namespace butl { -#ifndef _WIN32 bool file_exists (const char* p, bool fl) { - struct stat s; - if ((fl ? stat (p, &s) : lstat (p, &s)) != 0) - { - if (errno == ENOENT || errno == ENOTDIR) - return false; - else - throw system_error (errno, system_category ()); - } + auto pe (path_entry (p, fl)); + return pe.first && (pe.second == entry_type::regular || + (!fl && pe.second == entry_type::symlink)); + } - return S_ISREG (s.st_mode) || (!fl && S_ISLNK (s.st_mode)); + bool + entry_exists (const char* p, bool fl) + { + return path_entry (p, fl).first; } -#else + bool - file_exists (const char* p, bool) + dir_exists (const char* p) { - struct _stat s; - if (_stat (p, &s) != 0) + auto pe (path_entry (p, true)); + return pe.first && pe.second == entry_type::directory; + } + +#ifndef _WIN32 + pair + path_entry (const char* p, bool fl) + { + struct stat s; + if ((fl ? stat (p, &s) : lstat (p, &s)) != 0) { if (errno == ENOENT || errno == ENOTDIR) - return false; + return make_pair (false, entry_type::unknown); else throw system_error (errno, system_category ()); } - return S_ISREG (s.st_mode); - } -#endif + auto m (s.st_mode); + entry_type t (entry_type::unknown); -#ifndef _WIN32 - bool - entry_exists (const char* p, bool fl) - { - struct stat s; - if ((fl ? stat (p, &s) : lstat (p, &s)) == 0) + if (S_ISREG (m)) + t = entry_type::regular; + else if (S_ISDIR (m)) + t = entry_type::directory; + else if (S_ISLNK (m)) + t = entry_type::symlink; + else if (S_ISBLK (m) || S_ISCHR (m) || S_ISFIFO (m) || S_ISSOCK (m)) + t = entry_type::other; + + return make_pair (true, t); + } #else - bool - entry_exists (const char* p, bool) + pair + path_entry (const char* p, bool) { + int r; struct _stat s; - if (_stat (p, &s) == 0) -#endif - return true; - - if (errno == ENOENT || errno == ENOTDIR) - return false; - throw system_error (errno, system_category ()); - } + // A path like 'C:', while being a root path in our terminology, is not as + // such for Windows, that maintains current directory for each drive, and + // so C: means the current directory on the drive C. This is not what we + // mean here, so need to append the trailing directory separator in such a + // case. + // + if (!path::traits::root (p, string::traits_type::length (p))) + r = _stat (p, &s); + else + { + string d (p); + d += path::traits::directory_separator; + r = _stat (d.c_str (), &s); + } - bool - dir_exists (const char* p) - { -#ifndef _WIN32 - struct stat s; - if (stat (p, &s) != 0) -#else - struct _stat s; - if (_stat (p, &s) != 0) -#endif + if (r != 0) { if (errno == ENOENT || errno == ENOTDIR) - return false; + return make_pair (false, entry_type::unknown); else throw system_error (errno, system_category ()); } - return S_ISDIR (s.st_mode); + auto m (s.st_mode); + entry_type t (entry_type::unknown); + + if (S_ISREG (m)) + t = entry_type::regular; + else if (S_ISDIR (m)) + t = entry_type::directory; + // + // S_ISLNK/S_IFDIR are not defined for Win32 but it does have + // symlinks. + // + //else if (S_ISLNK (m)) + // t = entry_type::symlink; + + return make_pair (true, t); } +#endif mkdir_status #ifndef _WIN32 diff --git a/butl/path b/butl/path index da36770..a81848b 100644 --- a/butl/path +++ b/butl/path @@ -145,6 +145,22 @@ namespace butl return n == 2 && s[0] == '.' && s[1] == '.'; } + static bool + root (const string_type& s) + { + return root (s.c_str (), s.size ()); + } + + static bool + root (const C* s, size_type n) + { +#ifdef _WIN32 + return n == 2 && s[1] == ':'; +#else + return n == 1 && is_separator (s[0]); +#endif + } + static size_type find_separator (string_type const& s, size_type pos = 0, diff --git a/butl/path.cxx b/butl/path.cxx index bfae61b..9947bbe 100644 --- a/butl/path.cxx +++ b/butl/path.cxx @@ -81,7 +81,18 @@ namespace butl current_directory (string_type const& s) { #ifdef _WIN32 - if (_chdir (s.c_str ()) != 0) + // A path like 'C:', while being a root path in our terminology, is not as + // such for Windows, that maintains current directory for each drive, and + // so "change current directory to C:" means "change the process current + // directory to current directory on the C drive". Changing it to the root + // one of the drive requires the trailing directory separator to be + // present. + // + string_type const& d (!root (s) + ? s + : string_type (s + directory_separator)); + + if (_chdir (d.c_str ()) != 0) throw system_error (errno, system_category ()); #else if (chdir (s.c_str ()) != 0) @@ -248,7 +259,15 @@ namespace butl current_directory (string_type const& s) { #ifdef _WIN32 - if (_wchdir (s.c_str ()) != 0) + // Append the trailing directory separator for the root directory (read + // the comment in path_traits::current_directory() for + // justification). + // + string_type const& d (!root (s) + ? s + : string_type (s + directory_separator)); + + if (_wchdir (d.c_str ()) != 0) throw system_error (errno, system_category ()); #else char ns[PATH_MAX + 1]; diff --git a/butl/path.ixx b/butl/path.ixx index 4a5d992..b60f206 100644 --- a/butl/path.ixx +++ b/butl/path.ixx @@ -104,13 +104,7 @@ namespace butl inline bool basic_path:: root () const { - const string_type& s (this->path_); - -#ifdef _WIN32 - return s.size () == 2 && s[1] == ':'; -#else - return s.size () == 1 && traits::is_separator (s[0]); -#endif + return traits::root (this->path_); } template -- cgit v1.1