/* 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__ */