/* Filesystem path handling for GDB. Copyright (C) 2017 Free Software Foundation, Inc. This file is part of GDB. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef __COMMON_GDB_PATH__ #define __COMMON_GDB_PATH__ #include #include #include #include namespace gdb { namespace filesystem { /* Stub implementation of C++17 std::filesystem::path. */ class path { public: path () noexcept {} template path (const T &s); /* Replace this path by OTHER. */ template path &operator= (const T &other); /* Append OTHER to this path. */ template path &operator/= (const T &other); /* Same as operator/=. */ template path &append (const T &other) { return *this /= other; } /* Append RHS to LHS and return the resulting path. */ template friend path operator/ (path lhs, const T &rhs); /* Add OTHER to this path without adding a dir seperator. */ template path &operator+= (const T &other); /* Same as operator+=. */ template path &concat (const T &other) { return *this += other; } /* Send this path to an ostream. */ friend std::ostream &operator<< (std::ostream &os, const path &p); /* Directory seperator. */ char preferred_seperator = '/'; /* Erase the content of this path. */ void clear (); /* Concatenate the path and return it as a std::string. */ std::string string () const; /* Same as .string but returns const char *. */ const char * c_str () { return string ().c_str (); } /* Return the root_name of path, e.g. on DOS-like systems the drive name. */ path root_name () const; /* Return the root directory if it exists. */ path root_directory () const; /* Return the root path, i.e. root_name + root_directory. */ path root_path () const; /* Return the relative path from root_path. */ path relative_path () const; /* Return the parent path of this path. */ path parent_path () const; /* Return the filename component of this path. */ path filename () const; /* Return the stem of filename component of this path, i.e. the filename without extension. */ path stem () const; /* Return the extension of filename component of this path. */ path extension () const; /* Check if this path is empty. */ bool empty () const noexcept; protected: /* Root of the filesystem, e.g. "C:" on dos-like filesystems. */ std::string m_root_name = ""; /* Is this path absolute? I.e. do we need to add a dir_sep at the beginning? */ bool m_absolute = false; /* Components of the path relative to root_path. */ std::list m_path = {}; /* Helper function. */ /* Does the given string sart with a root_name? Needed for constructor. */ bool has_root_name (const std::string &s) const; /* Is the substring of S starting at position POS a dir_seperator? */ bool is_dirsep (const std::string &s, const size_t pos) const; /* Calculate the size (i.e. number of characters) of the total path including dir_sep's. */ size_t path_size () const; /* Append path component COMP to m_path. */ void append_component (std::string &comp); }; /* See declaration. */ inline bool path::has_root_name (const std::string &s) const { //#if defined (HAVE_DOS_BASED_FILESYSTEM) /* Assume 'C:'-like root_names. */ return s.size () >= 2 && (std::isalpha (s[0]) && s[1] == ':'); //#else return false; //#endif /* HAVE_DOS_BASED_FILESYSTEM */ } /* See declaration. */ inline bool path::is_dirsep (const std::string &s, const size_t pos) const { return s[pos] == preferred_seperator; } /* See declaration. */ size_t path::path_size () const { size_t size = 0; for (auto &p : m_path) size += p.size () + 1; return size; } /* See declaration. */ void path::append_component (std::string &comp) { if (comp == ".") return; if (comp == ".." && !m_path.empty ()) { m_path.pop_back (); return; } m_path.push_back (comp); } /* Constructors. */ template path::path (const T &s) : path (std::string (s)) {} template<> path::path (const std::string &s) { size_t pos = 0; if (has_root_name (s)) { /* Assume 'C:'-like root_names. */ m_root_name = s.substr (pos, 2); pos += 2; } if (is_dirsep (s, pos)) { m_absolute = true; pos++; } do { /* Remove duplicate dir_seps. */ while (s[pos] == preferred_seperator) pos++; size_t last_pos = pos; pos = s.find (preferred_seperator, pos); std::string comp (s.substr (last_pos, pos - last_pos)); append_component (comp); } while (pos != std::string::npos); } template<> path::path (const path &other) { *this = other; } /* See declaration. */ std::ostream & operator<< (std::ostream &os, const path &p) { os << std::quoted (p.string ()); return os; } /* See declaration. */ template path & path::operator= (const T &other) { return *this = path (other); } /* See declaration. */ template<> path & path::operator= (const path &other) { preferred_seperator = other.preferred_seperator; m_root_name = other.m_root_name; m_absolute = other.m_absolute; m_path = other.m_path; return *this; } /* See declaration. */ template<> path & path::operator/= (const path &other) { for (auto comp : other.m_path) append_component (comp); return *this; } /* See declaration. */ template path & path::operator/= (const T &other) { return *this /= path (other); } /* See declaration. */ template path operator/ (path lhs, const T &rhs) { return lhs /= rhs; } /* See declaration. */ template<> path & path::operator+= (const path &other) { /* Ignore a possible root_name in other. */ m_path.back () += other.m_path.front (); m_path.insert (m_path.end (), ++other.m_path.begin (), other.m_path.end ()); return *this; } /* See declaration. */ template path & path::operator+= (const T &other) { return *this += path (other); } /* See declaration. */ void path::clear () { m_root_name.clear (); m_path.clear (); m_absolute = false; } /* See declaration. */ std::string path::string () const { std::string ret; if (empty ()) return ""; ret.reserve (path_size ()); ret += m_root_name; if (m_absolute) ret += preferred_seperator; for (auto p = m_path.begin (); p != m_path.end (); p++) ret += *p + preferred_seperator; /* Remove trailing dir_sep. */ ret.pop_back (); return ret; } /* See declaration. */ path path::root_name () const { return empty () ? path () : path (m_root_name); } /* See declaration. */ path path::root_directory () const { return m_absolute ? path (&preferred_seperator) : path (); } /* See declaration. */ path path::root_path () const { return root_name () / root_directory (); } /* See declaration. */ path path::relative_path () const { if (empty ()) return path (); path ret (*this); ret.m_root_name = ""; ret.m_absolute = false; return ret; } /* See declaration. */ path path::parent_path () const { if (empty ()) return path (); path ret (*this); ret.m_path.pop_back (); return ret; } /* See declaration. */ path path::filename () const { if (empty ()) return path (); return path (m_path.back ()); } /* See declaration. */ path path::stem () const { if (empty ()) return path (); auto pos = m_path.back ().rfind ('.'); if (pos == 0) return path (m_path.back ()); return path (m_path.back ().substr (0, pos)); } /* See declaration. */ path path::extension () const { if (empty ()) return path (); auto pos = m_path.back ().rfind ('.'); if (pos == 0 || pos == std::string::npos) return path (); return path (m_path.back ().substr (pos)); } /* See declaration. */ bool path::empty () const noexcept { return m_path.empty () && m_root_name.empty () && !m_absolute; } } /* namespace filesystem */ enum path_type { HOST_PATH, TARGET_PATH, }; class path : public gdb::filesystem::path { public: path () noexcept {}; template path (const T& s, enum path_type _type = HOST_PATH) : gdb::filesystem::path (s), type (_type) {} /* Overload .string method to prepend target_prefix. */ std::string string () const; /* Substitute all occurrences of FROM to TO in this path. */ path &substitute (const std::string &from, const std::string &to); /* Type of this path (host or target). */ enum path_type type; private: /* Prefix to be prepended to target paths. */ const std::string m_target_prefix = "target:"; }; /* See declaration. */ std::string path::string () const { std::string ret; if (type == TARGET_PATH) { ret.reserve (path_size () + m_target_prefix.size ()); ret = m_target_prefix + gdb::filesystem::path::string (); } else { ret = gdb::filesystem::path::string (); } return ret; } /* See declaration. */ path & path::substitute (const std::string &from, const std::string &to) { if (from.empty ()) return *this; /* Use TO == "" to remove the component completely. */ if (to.empty ()) { m_path.remove (from); return *this; } for (auto it = m_path.begin (); it != m_path.end (); ++it) { if (*it != from) continue; *it = to; } return *this; } } /* namespace gdb */ #endif /*__COMMON_GDB_PATH__ */