// 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 #include #include // move #include namespace build { template class basic_path; template struct path_traits { typedef std::basic_string 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; } // Return the position of '.' or npos if there is no extension. // static size_type find_extension (string_type const& s) { size_type i (s.size ()); for (; i > 0; --i) { C c (s[i - 1]); if (c == '.') break; if (is_separator (c)) { i = 0; break; } } // Weed out paths like ".txt" (and "/.txt") and "txt.". // if (i > 1 && !is_separator (s[i - 2]) && i != s.size ()) return i - 1; else 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 class invalid_basic_path; typedef basic_path path; typedef invalid_basic_path invalid_path; typedef basic_path wpath; typedef invalid_basic_path invalid_wpath; // // class invalid_path_base: std::exception { public: virtual char const* what () const throw (); }; template class invalid_basic_path: public invalid_path_base { public: typedef std::basic_string 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 class basic_path { public: typedef std::basic_string string_type; typedef typename string_type::size_type size_type; typedef path_traits 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 s) : path_ (std::move (s)) { init (); } basic_path (const string_type& s, size_type n) : path_ (s, 0, n) { init (); } basic_path (const string_type& s, size_type p, size_type n) : path_ (s, p, n) { 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; // Return true if *this is a sub-path of the specified path (i.e., // the specified path is a prefix). Expects both paths to be // normalized. Note that this function returns true if the paths // are equal. // bool sub (const basic_path& p) const { return path_.compare (0, p.path_.size (), p.path_) == 0; } public: // Return the path without the directory part. // basic_path leaf () const; // Return the path without the specified directory part. Throws // invalid_path if the directory is not a prefix of *this. Expects // both paths to be normalized. // basic_path leaf (basic_path const&) const; // Return the directory part of the path or empty path if // there is no directory. // basic_path directory () const; // Return the directory part of the path without the specified // leaf part. Throws invalid_path if the leaf is not a suffix of // *this. Expects both paths to be normalized. // basic_path directory (basic_path const&) const; // Return the root directory of the path or empty path if // the directory is not absolute. // basic_path root_directory () const; // Return the path without the extension, if any. // basic_path base () const; // Return the extension or NULL if not present. // const C* extension () const; // Return a path relative to the specified path that is equivalent // to *this. Throws invalid_path if a relative path cannot be derived // (e.g., paths are on different drives on Windows). // basic_path relative (basic_path) 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+ (C c) const { return basic_path (path_ + c); } basic_path& operator+= (string_type const& s) { path_ += s; return *this; } basic_path& operator+= (C c) { path_ += c; 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: const 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 inline std::basic_ostream& operator<< (std::basic_ostream& os, basic_path const& p) { return os << p.string (); } */ } namespace std { template struct hash>: hash> { size_t operator() (const build::basic_path& p) const noexcept { return hash>::operator() (p.string ()); } }; } #include #include #endif // BUILD_PATH