aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2017-02-10 16:10:33 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2017-02-13 12:55:34 +0300
commit31e91691e815074ebdb49d258967e2b2a0bfc965 (patch)
tree1739e815c312b40fa9a8c64b7b144cf60d4c28bb
parent66ca47f856cc04774cbe07dc67e2e099e5d527f3 (diff)
Add path_entry(), fixes for path
-rw-r--r--butl/filesystem37
-rw-r--r--butl/filesystem.cxx115
-rw-r--r--butl/path16
-rw-r--r--butl/path.cxx23
-rw-r--r--butl/path.ixx8
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 <cstddef> // ptrdiff_t
-#include <cstdint> // uint16_t
-#include <utility> // move()
+#include <cstddef> // ptrdiff_t
+#include <cstdint> // uint16_t
+#include <utility> // move(), pair
#include <iterator>
#include <butl/export>
@@ -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<bool, entry_type>
+ path_entry (const char*, bool follow_symlinks = false);
+
+ inline std::pair<bool, entry_type>
+ 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<bool, entry_type>
+ 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<bool, entry_type>
+ 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<char>::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<C, K>::
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 <typename C, typename K>