Hi Pedro, On Tue, 30 May 2017 10:00:07 +0200 Philipp Rudo wrote: [...] > > I still think that if we're adding some utility for building > > paths from dir components, that it'd be preferred to model > > the API on (a small subset of) std::experimental::filesystem::path, > > since in a few years that's the API that everyone learning C++ will > > be learning. > > Also true, let me see if I can hack something today. Currently I don't like > to do what I should do and there are many meeting today anyway. So this > looks like a perfect task for today ;) I looked into it a "little" and it got a "little" out of hand... I'm going on vacation, starting tomorrow until next Wednesday, and didn't have the time to include it in GDB yet. So you find the code attached in two files as separate "programm". My plan is once I'm back (and finished the work I should have done this week (sorry Yao and Omair)) to move gdb_path.h to common/gdb_path.h and expand gdb_path-selftest.c with some static_asserts. While working on it I noticed one detail in std::filesystem::path which could be problematic for GDB. The dir separator it uses (preferred_separator) is 'static constexpr char'. So for cross debugging, with different dir separators, it doesn't allow us to distinguish between host and target paths. That's why my implementation uses a simple "char" such that the dir separator can be changed anytime. Otherwise the implementation should be compatible with std::filesystem::path http://en.cppreference.com/w/cpp/filesystem/path Any comment is welcome. Philipp ---------- To be added as binutils-gdb/gdb/common/gdb_path.h gdb_path.h | 542 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 542 insertions(+) ---------- /* 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__ */