aboutsummaryrefslogtreecommitdiff
path: root/cutl
diff options
context:
space:
mode:
Diffstat (limited to 'cutl')
-rw-r--r--cutl/fs/path.hxx123
-rw-r--r--cutl/fs/path.txx65
2 files changed, 147 insertions, 41 deletions
diff --git a/cutl/fs/path.hxx b/cutl/fs/path.hxx
index aee91b9..fbf179e 100644
--- a/cutl/fs/path.hxx
+++ b/cutl/fs/path.hxx
@@ -19,6 +19,63 @@ namespace cutl
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 char const directory_separator = '\\';
+ static char const path_separator = ';';
+#else
+ static char const directory_separator = '/';
+ static char 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)
+ {
+ for (size_type i (0), n (s.size ()); i < n; ++i)
+ {
+ if (is_separator (s[i]))
+ return i;
+ }
+
+ return string_type::npos;
+ }
+
+ static size_type
+ rfind_separator (string_type const& s)
+ {
+ for (size_type i (s.size ()) ; i > 0; --i)
+ {
+ if (is_separator (s[i - 1]))
+ return i - 1;
+ }
+
+ return string_type::npos;
+ }
+ };
+
+
+ template <typename C>
class invalid_basic_path;
typedef basic_path<char> path;
@@ -63,33 +120,75 @@ namespace cutl
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 (false);
+ init ();
+ }
+
+ basic_path (C const* s, size_type n)
+ : path_ (s, n)
+ {
+ init ();
}
explicit
basic_path (string_type const& s)
: path_ (s)
{
- init (false);
+ init ();
}
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:
basic_path
- operator/ (basic_path const&);
+ operator/ (basic_path const& x)
+ {
+ basic_path r (*this);
+ r /= x;
+ return r;
+ }
+
+ basic_path&
+ operator/= (basic_path const&);
+
+ basic_path
+ operator+ (string_type const& s)
+ {
+ return basic_path (path_ + s);
+ }
+
+ basic_path&
+ operator+= (string_type const& s)
+ {
+ path_ += s;
+ return *this;
+ }
bool
operator== (basic_path const& x) const
@@ -104,22 +203,26 @@ namespace cutl
}
public:
+ bool
+ empty () const
+ {
+ return path_.empty ();
+ }
+
string_type
string () const
{
- return path_.empty () ? string_type (1, '/') : path_;
+ return path_;
}
private:
void
- init (bool internal);
+ init ();
- // Assume internal format.
- //
- basic_path (C const* s, size_type n)
- : path_ (s, n)
+ bool
+ root () const
{
- init (true);
+ return path_.size () == 1 && traits::is_separator (path_[0]);
}
private:
diff --git a/cutl/fs/path.txx b/cutl/fs/path.txx
index 44064ca..e955928 100644
--- a/cutl/fs/path.txx
+++ b/cutl/fs/path.txx
@@ -11,30 +11,28 @@ namespace cutl
basic_path<C> basic_path<C>::
leaf () const
{
- size_type n (path_.size ()), i (n);
+ size_type p (traits::rfind_separator (path_));
- for (; i > 0; --i)
- {
- if (path_[i - 1] == '/' || path_[i - 1] == '\\')
- break;
- }
-
- return i != 0 ? basic_path (path_.c_str () + i, n - i) : *this;
+ 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
{
- size_type i (path_.size ());
+ if (root ())
+ return basic_path ();
- for (; i > 0; --i)
- {
- if (path_[i - 1] == '/' || path_[i - 1] == '\\')
- break;
- }
+ size_type p (traits::rfind_separator (path_));
- return i != 0 ? basic_path (path_.c_str (), i - 1) : *this;
+ // 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>
@@ -48,7 +46,7 @@ namespace cutl
if (path_[i - 1] == '.')
break;
- if (path_[i - 1] == '/' || path_[i - 1] == '\\')
+ if (traits::is_separator (path_[i - 1]))
{
i = 0;
break;
@@ -57,7 +55,7 @@ namespace cutl
// Weed out paths like ".txt" and "/.txt"
//
- if (i > 1 && path_[i - 2] != '/' && path_[i - 2] != '\\')
+ if (i > 1 && !traits::is_separator (path_[i - 2]))
{
return basic_path (path_.c_str (), i - 1);
}
@@ -66,30 +64,35 @@ namespace cutl
}
template <typename C>
- basic_path<C> basic_path<C>::
- operator/ (basic_path<C> const& r)
+ basic_path<C>& basic_path<C>::
+ operator/= (basic_path<C> const& r)
{
- if (r.path_.empty ())
+ if (r.root ())
throw invalid_basic_path<C> (r.path_);
- basic_path<C> x (*this);
- x.path_ += '/';
- x.path_ += r.path_;
- return x;
+ if (path_.empty () || r.path_.empty ())
+ {
+ path_ += r.path_;
+ return *this;
+ }
+
+ if (!root ())
+ path_ += traits::directory_separator;
+
+ path_ += r.path_;
+
+ return *this;
}
template <typename C>
void basic_path<C>::
- init (bool internal)
+ init ()
{
- if (!internal && path_.empty ())
- throw invalid_basic_path<C> (path_);
-
- // Strip trailing slashes. This way empty string represents
- // root directory.
+ // Strip trailing slashes except for the case where the single
+ // slash represents the root directory.
//
size_type n (path_.size ());
- for (; n > 0 && (path_[n - 1] == '/' || path_[n - 1] == '\\'); --n) ;
+ for (; n > 1 && traits::is_separator (path_[n - 1]); --n) ;
path_.resize (n);
}
}