From 547a401ac73d21b769939a3244f991112dd9d2f7 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Fri, 22 Oct 2010 11:19:52 +0200 Subject: Add additional path functions These include: absolute(), relative(), current(), complete(), and normalize(). --- cutl/fs/path.cxx | 46 +++++++++++++++++++++++++ cutl/fs/path.hxx | 90 +++++++++++++++++++++++++++++++++++++----------- cutl/fs/path.ixx | 63 ++++++++++++++++++++++++++++++++++ cutl/fs/path.txx | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 279 insertions(+), 22 deletions(-) create mode 100644 cutl/fs/path.ixx (limited to 'cutl') diff --git a/cutl/fs/path.cxx b/cutl/fs/path.cxx index 578cc5b..5aaf2a9 100644 --- a/cutl/fs/path.cxx +++ b/cutl/fs/path.cxx @@ -3,6 +3,14 @@ // copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC // license : MIT; see accompanying LICENSE file +#ifdef _WIN32 +# include // _getcwd, _wgetcwd, _MAX_PATH +#else +# include // mbstowcs +# include // PATH_MAX +# include // getcwd +#endif + #include namespace cutl @@ -14,5 +22,43 @@ namespace cutl { return "invalid filesystem path"; } + + template <> + basic_path basic_path:: + current () + { +#ifdef _WIN32 + char cwd[_MAX_PATH]; + if(_getcwd(cwd, _MAX_PATH) == 0) + throw invalid_basic_path ("."); +#else + char cwd[PATH_MAX]; + if (getcwd (cwd, PATH_MAX) == 0) + throw invalid_basic_path ("."); +#endif + + return basic_path (cwd); + } + + template <> + basic_path basic_path:: + current () + { +#ifdef _WIN32 + wchar_t wcwd[_MAX_PATH]; + if(_wgetcwd(wcwd, _MAX_PATH) == 0) + throw invalid_basic_path ("."); +#else + char cwd[PATH_MAX]; + if (getcwd (cwd, PATH_MAX) == 0) + throw invalid_basic_path (L"."); + + wchar_t wcwd[PATH_MAX]; + if (mbstowcs (wcwd, cwd, PATH_MAX) == size_type (-1)) + throw invalid_basic_path (L"."); +#endif + + return basic_path (wcwd); + } } } diff --git a/cutl/fs/path.hxx b/cutl/fs/path.hxx index 9fed07a..9bef17f 100644 --- a/cutl/fs/path.hxx +++ b/cutl/fs/path.hxx @@ -29,11 +29,11 @@ namespace cutl // Canonical directory and path seperators. // #ifdef _WIN32 - static char const directory_separator = '\\'; - static char const path_separator = ';'; + static C const directory_separator = '\\'; + static C const path_separator = ';'; #else - static char const directory_separator = '/'; - static char const path_separator = ':'; + static C const directory_separator = '/'; + static C const path_separator = ':'; #endif // Directory separator tests. On some platforms there @@ -52,24 +52,29 @@ namespace cutl } static size_type - find_separator (string_type const& s) + find_separator (string_type const& s, size_type pos = 0) { - for (size_type i (0), n (s.size ()); i < n; ++i) + for (size_type n (s.size ()); pos < n; ++pos) { - if (is_separator (s[i])) - return i; + if (is_separator (s[pos])) + return pos; } return string_type::npos; } static size_type - rfind_separator (string_type const& s) + rfind_separator (string_type const& s, size_type pos = string_type::npos) { - for (size_type i (s.size ()) ; i > 0; --i) + if (pos == string_type::npos) + pos = s.size (); + else + pos++; + + for (; pos > 0; --pos) { - if (is_separator (s[i - 1])) - return i - 1; + if (is_separator (s[pos - 1])) + return pos - 1; } return string_type::npos; @@ -150,6 +155,34 @@ namespace cutl init (); } + void + swap (basic_path& p) + { + path_.swap (p.path_); + } + + static basic_path + current (); + + public: + bool + empty () const + { + return path_.empty (); + } + + bool + absolute () const; + + bool + relative () const + { + return !absolute (); + } + + bool + root () const; + public: // Return the path without the directory part. // @@ -168,6 +201,23 @@ namespace cutl base () const; public: + // Normalize the path. This includes collapsing the '.' and '..' + // directories if possible, collapsing multiple directory + // separators, converting all directory separators to the + // canonical form, and making the path lower-case if the + // filesystem is not case-sensitive (e.g., Windows). Returns + // *this. + // + basic_path& + normalize (); + + // Make the path absolute using the current directory unless + // it is already absolute. + // + basic_path& + complete (); + + public: basic_path operator/ (basic_path const& x) { @@ -204,13 +254,13 @@ namespace cutl return !(*this == x); } - public: bool - empty () const + operator< (basic_path const& x) const { - return path_.empty (); + return path_ < x.path_; } + public: string_type string () const { @@ -221,11 +271,10 @@ namespace cutl void init (); - bool - root () const - { - return path_.size () == 1 && traits::is_separator (path_[0]); - } +#ifdef _WIN32 + static C + tolower (C); +#endif private: string_type path_; @@ -240,6 +289,7 @@ namespace cutl } } +#include #include #endif // CUTL_FS_PATH_HXX diff --git a/cutl/fs/path.ixx b/cutl/fs/path.ixx new file mode 100644 index 0000000..7a92110 --- /dev/null +++ b/cutl/fs/path.ixx @@ -0,0 +1,63 @@ +// file : cutl/fs/path.ixx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC +// license : MIT; see accompanying LICENSE file + +#ifdef _WIN32 +# include // std::tolower +# include // std::towlower +#endif + +namespace cutl +{ + namespace fs + { + template + inline bool basic_path:: + absolute () const + { +#ifdef _WIN32 + return path_.size () > 1 && path_[1] == ':'; +#else + return !path_.empty () && traits::is_separator (path_[0]); +#endif + } + + template + inline bool basic_path:: + root () const + { +#ifdef _WIN32 + return path_.size () == 2 && path_[1] == ':'; +#else + return path_.size () == 1 && traits::is_separator (path_[0]); +#endif + } + + template + inline basic_path& basic_path:: + complete () + { + if (relative ()) + *this = current () / *this; + + return *this; + } + +#ifdef _WIN32 + template <> + inline char basic_path:: + tolower (char c) + { + return std::tolower (c); + } + + template <> + inline wchar_t basic_path:: + tolower (wchar_t c) + { + return std::towlower (c); + } +#endif + } +} diff --git a/cutl/fs/path.txx b/cutl/fs/path.txx index e955928..f4bbb10 100644 --- a/cutl/fs/path.txx +++ b/cutl/fs/path.txx @@ -3,6 +3,8 @@ // copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC // license : MIT; see accompanying LICENSE file +#include + namespace cutl { namespace fs @@ -67,7 +69,7 @@ namespace cutl basic_path& basic_path:: operator/= (basic_path const& r) { - if (r.root ()) + if (r.absolute ()) throw invalid_basic_path (r.path_); if (path_.empty () || r.path_.empty ()) @@ -76,7 +78,7 @@ namespace cutl return *this; } - if (!root ()) + if (!traits::is_separator (path_[path_.size () - 1])) path_ += traits::directory_separator; path_ += r.path_; @@ -85,6 +87,102 @@ namespace cutl } template + basic_path& basic_path:: + normalize () + { + if (empty ()) + return *this; + + bool abs (absolute ()); + + typedef std::vector paths; + paths ps; + + for (size_type b (0), e (traits::find_separator (path_)), + n (path_.size ());; + e = traits::find_separator (path_, b)) + { + string_type s (path_, b, e == string_type::npos ? e : e - b); + ps.push_back (s); + + if (e == string_type::npos) + break; + + ++e; + + while (e < n && traits::is_separator (path_[e])) + ++e; + + if (e == n) + break; + + b = e; + } + + // First collapse '.' and '..'. + // + paths r; + + for (typename paths::const_iterator i (ps.begin ()), e (ps.end ()); + i != e; ++i) + { + string_type const& s (*i); + size_type n (s.size ()); + + if (n == 1 && s[0] == '.') + continue; + + if (n == 2 && s[0] == '.' && s[1] == '.') + { + // Pop the last directory from r unless it is '..'. + // + if (!r.empty ()) + { + string_type const& s1 (r.back ()); + + if (!(s1.size () == 2 && s1[0] == '.' && s1[1] == '.')) + { + // Cannot go past the root directory. + // + if (abs && r.size () == 1) + throw invalid_basic_path (path_); + + r.pop_back (); + continue; + } + } + } + + r.push_back (s); + } + + // Reassemble the path. + // + string_type p; + + for (typename paths::const_iterator i (r.begin ()), e (r.end ()); + i != e;) + { +#ifdef _WIN32 + for (size_type j (0), n (i->size ()); j < n; ++j) + p += tolower ((*i)[j]); +#else + p += *i; +#endif + ++i; + + if (i != e) + p += traits::directory_separator; + } + + if (p.empty () && !r.empty ()) + p += traits::directory_separator; // Root directory. + + path_.swap (p); + return *this; + } + + template void basic_path:: init () { -- cgit v1.1