aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2014-12-08 08:25:29 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2014-12-08 08:25:29 +0200
commit20e3aedeb7df742c38276fb41cae8f3eb027b6dd (patch)
tree3219377439099027c27b14bd8638d62ae7bc6db9
parent985e8f5f28da87be779b80942577f088321024af (diff)
Add filesystem path class
g++-4.9 -std=c++11 -I.. -o bd bd.cxx process.cxx timestamp.cxx path.cxx
-rw-r--r--build/path327
-rw-r--r--build/path.cxx114
-rw-r--r--build/path.ixx68
-rw-r--r--build/path.txx207
4 files changed, 716 insertions, 0 deletions
diff --git a/build/path b/build/path
new file mode 100644
index 0000000..97fb154
--- /dev/null
+++ b/build/path
@@ -0,0 +1,327 @@
+// file : cutl/path -*- C++ -*-
+// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC
+// license : MIT; see accompanying LICENSE file
+
+#ifndef BUILD_PATH
+#define BUILD_PATH
+
+#include <string>
+#include <ostream>
+#include <exception>
+
+namespace build
+{
+ template <typename C>
+ class basic_path;
+
+ template <typename C>
+ struct path_traits
+ {
+ typedef std::basic_string<C> string_type;
+ typedef typename string_type::size_type size_type;
+
+ // Canonical directory and path seperators.
+ //
+#ifdef _WIN32
+ static C const directory_separator = '\\';
+ static C const path_separator = ';';
+#else
+ static C const directory_separator = '/';
+ static C const path_separator = ':';
+#endif
+
+ // Directory separator tests. On some platforms there
+ // could be multiple seperators. For example, on Windows
+ // we check for both '/' and '\'.
+ //
+ static bool
+ is_separator (C c)
+ {
+#ifdef _WIN32
+ return c == '\\' || c == '/';
+#else
+ return c == '/';
+#endif
+ }
+
+ static size_type
+ find_separator (string_type const& s, size_type pos = 0)
+ {
+ for (size_type n (s.size ()); pos < n; ++pos)
+ {
+ if (is_separator (s[pos]))
+ return pos;
+ }
+
+ return string_type::npos;
+ }
+
+ static size_type
+ rfind_separator (string_type const& s, size_type pos = string_type::npos)
+ {
+ if (pos == string_type::npos)
+ pos = s.size ();
+ else
+ pos++;
+
+ for (; pos > 0; --pos)
+ {
+ if (is_separator (s[pos - 1]))
+ return pos - 1;
+ }
+
+ return string_type::npos;
+ }
+
+ static int
+ compare (string_type const& l, string_type const& r)
+ {
+ size_type ln (l.size ()), rn (r.size ()), n (ln < rn ? ln : rn);
+ for (size_type i (0); i != n; ++i)
+ {
+#ifdef _WIN32
+ C lc (tolower (l[i])), rc (tolower (r[i]));
+#else
+ C lc (l[i]), rc (r[i]);
+#endif
+ if (is_separator (lc) && is_separator (rc))
+ continue;
+
+ if (lc < rc) return -1;
+ if (lc > rc) return 1;
+ }
+
+ return ln < rn ? -1 : (ln > rn ? 1 : 0);
+ }
+
+ private:
+#ifdef _WIN32
+ static C
+ tolower (C);
+#endif
+ };
+
+ template <typename C>
+ class invalid_basic_path;
+
+ typedef basic_path<char> path;
+ typedef invalid_basic_path<char> invalid_path;
+
+ typedef basic_path<wchar_t> wpath;
+ typedef invalid_basic_path<wchar_t> invalid_wpath;
+
+ //
+ //
+ class invalid_path_base: std::exception
+ {
+ public:
+ virtual char const*
+ what () const throw ();
+ };
+
+ template <typename C>
+ class invalid_basic_path: public invalid_path_base
+ {
+ public:
+ typedef std::basic_string<C> string_type;
+
+ invalid_basic_path (C const* p): path_ (p) {}
+ invalid_basic_path (string_type const& p): path_ (p) {}
+ ~invalid_basic_path () throw () {}
+
+ string_type const&
+ path () const
+ {
+ return path_;
+ }
+
+ private:
+ string_type path_;
+ };
+
+ template <typename C>
+ class basic_path
+ {
+ public:
+ typedef std::basic_string<C> string_type;
+ typedef typename string_type::size_type size_type;
+
+ typedef path_traits<C> traits;
+
+ // Construct special empty path.
+ //
+ basic_path ()
+ {
+ }
+
+ explicit
+ basic_path (C const* s)
+ : path_ (s)
+ {
+ init ();
+ }
+
+ basic_path (C const* s, size_type n)
+ : path_ (s, n)
+ {
+ init ();
+ }
+
+ explicit
+ basic_path (string_type const& s)
+ : path_ (s)
+ {
+ init ();
+ }
+
+ void
+ swap (basic_path& p)
+ {
+ path_.swap (p.path_);
+ }
+
+ void
+ clear ()
+ {
+ path_.clear ();
+ }
+
+ static basic_path
+ current ();
+
+ static void
+ current (basic_path const&);
+
+ 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.
+ //
+ basic_path
+ leaf () const;
+
+ // Return the directory part of the path or empty path if
+ // there is no directory.
+ //
+ basic_path
+ directory () const;
+
+ // Return the path without the extension, if any.
+ //
+ basic_path
+ base () const;
+
+ public:
+ // Normalize the path. This includes collapsing the '.' and '..'
+ // directories if possible, collapsing multiple directory
+ // separators, and converting all directory separators to the
+ // canonical form. 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) const
+ {
+ basic_path r (*this);
+ r /= x;
+ return r;
+ }
+
+ basic_path&
+ operator/= (basic_path const&);
+
+ basic_path
+ operator+ (string_type const& s) const
+ {
+ return basic_path (path_ + s);
+ }
+
+ basic_path&
+ operator+= (string_type const& s)
+ {
+ path_ += s;
+ return *this;
+ }
+
+ // Note that comparison is case-insensitive if the filesystem is
+ // not case-sensitive (e.g., Windows).
+ //
+ bool
+ operator== (basic_path const& x) const
+ {
+ return traits::compare (path_, x.path_) == 0;
+ }
+
+ bool
+ operator!= (basic_path const& x) const
+ {
+ return !(*this == x);
+ }
+
+ bool
+ operator< (basic_path const& x) const
+ {
+ return traits::compare (path_, x.path_) < 0;
+ }
+
+ public:
+ string_type
+ string () const
+ {
+ return path_;
+ }
+
+ // If possible, return a POSIX representation of the path. For example,
+ // for a Windows path in the form foo\bar this function will return
+ // foo/bar. If it is not possible to create a POSIX representation for
+ // this path (e.g., c:\foo), this function will throw the invalid_path
+ // exception.
+ //
+ string_type
+ posix_string () const;
+
+ private:
+ void
+ init ();
+
+ private:
+ string_type path_;
+ };
+
+ template <typename C>
+ inline std::basic_ostream<C>&
+ operator<< (std::basic_ostream<C>& os, basic_path<C> const& p)
+ {
+ return os << p.string ();
+ }
+}
+
+#include <build/path.ixx>
+#include <build/path.txx>
+
+#endif // BUILD_PATH
diff --git a/build/path.cxx b/build/path.cxx
new file mode 100644
index 0000000..efd0532
--- /dev/null
+++ b/build/path.cxx
@@ -0,0 +1,114 @@
+// file : build/path.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC
+// license : MIT; see accompanying LICENSE file
+
+#include <build/path>
+
+#ifdef _WIN32
+# include <stdlib.h> // _MAX_PATH
+# include <direct.h> // _[w]getcwd, _[w]chdir
+#else
+# include <stdlib.h> // mbstowcs, wcstombs
+# include <limits.h> // PATH_MAX
+# include <unistd.h> // getcwd, chdir
+#endif
+
+namespace build
+{
+ char const* invalid_path_base::
+ what () const throw ()
+ {
+ return "invalid filesystem path";
+ }
+
+ //
+ // char
+ //
+
+ template <>
+ basic_path<char> basic_path<char>::
+ current ()
+ {
+ // @@ throw system_error (and in the other current() versions).
+
+#ifdef _WIN32
+ char cwd[_MAX_PATH];
+ if(_getcwd(cwd, _MAX_PATH) == 0)
+ throw invalid_basic_path<char> (".");
+#else
+ char cwd[PATH_MAX];
+ if (getcwd (cwd, PATH_MAX) == 0)
+ throw invalid_basic_path<char> (".");
+#endif
+
+ return basic_path<char> (cwd);
+ }
+
+ template <>
+ void basic_path<char>::
+ current (basic_path const& p)
+ {
+ string_type const& s (p.string ());
+
+ if (p.empty ())
+ throw invalid_basic_path<char> (s);
+
+#ifdef _WIN32
+ if(_chdir(s.c_str ()) != 0)
+ throw invalid_basic_path<char> (s);
+#else
+ if (chdir (s.c_str ()) != 0)
+ throw invalid_basic_path<char> (s);
+#endif
+ }
+
+ //
+ // wchar_t
+ //
+
+ template <>
+ basic_path<wchar_t> basic_path<wchar_t>::
+ current ()
+ {
+#ifdef _WIN32
+ wchar_t wcwd[_MAX_PATH];
+ if(_wgetcwd(wcwd, _MAX_PATH) == 0)
+ throw invalid_basic_path<wchar_t> (L".");
+#else
+ char cwd[PATH_MAX];
+ if (getcwd (cwd, PATH_MAX) == 0)
+ throw invalid_basic_path<wchar_t> (L".");
+
+ wchar_t wcwd[PATH_MAX];
+ if (mbstowcs (wcwd, cwd, PATH_MAX) == size_type (-1))
+ throw invalid_basic_path<wchar_t> (L".");
+#endif
+
+ return basic_path<wchar_t> (wcwd);
+ }
+
+ template <>
+ void basic_path<wchar_t>::
+ current (basic_path const& p)
+ {
+ string_type const& s (p.string ());
+
+ if (p.empty ())
+ throw invalid_basic_path<wchar_t> (s);
+
+#ifdef _WIN32
+ if(_wchdir(s.c_str ()) != 0)
+ throw invalid_basic_path<wchar_t> (s);
+#else
+ char ns[PATH_MAX + 1];
+
+ if (wcstombs (ns, s.c_str (), PATH_MAX) == size_type (-1))
+ throw invalid_basic_path<wchar_t> (s);
+
+ ns[PATH_MAX] = '\0';
+
+ if (chdir (ns) != 0)
+ throw invalid_basic_path<wchar_t> (s);
+#endif
+ }
+}
diff --git a/build/path.ixx b/build/path.ixx
new file mode 100644
index 0000000..f742060
--- /dev/null
+++ b/build/path.ixx
@@ -0,0 +1,68 @@
+// file : build/path.ixx -*- C++ -*-
+// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC
+// license : MIT; see accompanying LICENSE file
+
+#ifdef _WIN32
+# include <cctype> // std::tolower
+# include <cwctype> // std::towlower
+#endif
+
+namespace build
+{
+#ifdef _WIN32
+ template <>
+ inline char path_traits<char>::
+ tolower (char c)
+ {
+ return std::tolower (c);
+ }
+
+ template <>
+ inline wchar_t path_traits<wchar_t>::
+ tolower (wchar_t c)
+ {
+ return std::towlower (c);
+ }
+#endif
+
+ template <typename C>
+ inline bool basic_path<C>::
+ absolute () const
+ {
+#ifdef _WIN32
+ return path_.size () > 1 && path_[1] == ':';
+#else
+ return !path_.empty () && traits::is_separator (path_[0]);
+#endif
+ }
+
+ template <typename C>
+ inline bool basic_path<C>::
+ root () const
+ {
+#ifdef _WIN32
+ return path_.size () == 2 && path_[1] == ':';
+#else
+ return path_.size () == 1 && traits::is_separator (path_[0]);
+#endif
+ }
+
+ template <typename C>
+ inline basic_path<C>& basic_path<C>::
+ complete ()
+ {
+ if (relative ())
+ *this = current () / *this;
+
+ return *this;
+ }
+
+#ifndef _WIN32
+ template <typename C>
+ inline typename basic_path<C>::string_type basic_path<C>::
+ posix_string () const
+ {
+ return string ();
+ }
+#endif
+}
diff --git a/build/path.txx b/build/path.txx
new file mode 100644
index 0000000..1502071
--- /dev/null
+++ b/build/path.txx
@@ -0,0 +1,207 @@
+// file : build/path.txx -*- C++ -*-
+// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC
+// license : MIT; see accompanying LICENSE file
+
+#include <vector>
+
+namespace build
+{
+ template <typename C>
+ basic_path<C> basic_path<C>::
+ leaf () const
+ {
+ size_type p (traits::rfind_separator (path_));
+
+ return p != string_type::npos
+ ? basic_path (path_.c_str () + p + 1, path_.size () - p - 1)
+ : *this;
+ }
+
+ template <typename C>
+ basic_path<C> basic_path<C>::
+ directory () const
+ {
+ if (root ())
+ return basic_path ();
+
+ size_type p (traits::rfind_separator (path_));
+
+ // Include the trailing slash so that we get correct behavior
+ // if directory is root.
+ //
+ return p != string_type::npos
+ ? basic_path (path_.c_str (), p + 1)
+ : basic_path ();
+ }
+
+ template <typename C>
+ basic_path<C> basic_path<C>::
+ base () const
+ {
+ size_type i (path_.size ());
+
+ for (; i > 0; --i)
+ {
+ if (path_[i - 1] == '.')
+ break;
+
+ if (traits::is_separator (path_[i - 1]))
+ {
+ i = 0;
+ break;
+ }
+ }
+
+ // Weed out paths like ".txt" and "/.txt"
+ //
+ if (i > 1 && !traits::is_separator (path_[i - 2]))
+ {
+ return basic_path (path_.c_str (), i - 1);
+ }
+ else
+ return *this;
+ }
+
+#ifdef _WIN32
+ template <typename C>
+ typename basic_path<C>::string_type basic_path<C>::
+ posix_string () const
+ {
+ if (absolute ())
+ throw invalid_basic_path<C> (path_);
+
+ string_type r (path_);
+
+ // Translate Windows-style separators to the POSIX ones.
+ //
+ for (size_type i (0), n (r.size ()); i != n; ++i)
+ if (r[i] == '\\')
+ r[i] = '/';
+
+ return r;
+ }
+#endif
+
+ template <typename C>
+ basic_path<C>& basic_path<C>::
+ operator/= (basic_path<C> const& r)
+ {
+ if (r.absolute ())
+ throw invalid_basic_path<C> (r.path_);
+
+ if (path_.empty () || r.path_.empty ())
+ {
+ path_ += r.path_;
+ return *this;
+ }
+
+ if (!traits::is_separator (path_[path_.size () - 1]))
+ path_ += traits::directory_separator;
+
+ path_ += r.path_;
+
+ return *this;
+ }
+
+ template <typename C>
+ basic_path<C>& basic_path<C>::
+ normalize ()
+ {
+ if (empty ())
+ return *this;
+
+ bool abs (absolute ());
+
+ typedef std::vector<string_type> 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<C> (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;)
+ {
+ p += *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 <typename C>
+ void basic_path<C>::
+ init ()
+ {
+ // Strip trailing slashes except for the case where the single
+ // slash represents the root directory.
+ //
+ size_type n (path_.size ());
+ for (; n > 1 && traits::is_separator (path_[n - 1]); --n) ;
+ path_.resize (n);
+ }
+}