This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [RFC 2/7] Add unit test to builtin tdesc generated by xml


Hi Pedro,

On Tue, 30 May 2017 10:00:07 +0200
Philipp Rudo <prudo@linux.vnet.ibm.com> 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 <http://www.gnu.org/licenses/>.  */

#ifndef __COMMON_GDB_PATH__
#define __COMMON_GDB_PATH__

#include <iomanip>
#include <iostream>
#include <list>
#include <string>

namespace gdb {
namespace filesystem {

/* Stub implementation of C++17 std::filesystem::path.  */

class path
{
public:
  path () noexcept {}

  template<class T>
  path (const T &s);

  /* Replace this path by OTHER.  */
  template<class T>
  path &operator= (const T &other);

  /* Append OTHER to this path.  */
  template<class T>
  path &operator/= (const T &other);

  /* Same as operator/=.  */
  template<class T>
  path &append (const T &other)
    {
      return *this /= other;
    }

  /* Append RHS to LHS and return the resulting path.  */
  template<class T>
  friend path operator/ (path lhs, const T &rhs);

  /* Add OTHER to this path without adding a dir seperator.  */
  template<class T>
  path &operator+= (const T &other);

  /* Same as operator+=.  */
  template<class T>
  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 <std::string> 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<class T>
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<class T>
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<class T>
path &
path::operator/= (const T &other)
{
  return *this /= path (other);
}

/* See declaration.  */

template<class T>
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<class T>
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<class T>
  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__ */
/* 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 <http://www.gnu.org/licenses/>.  */

#ifndef __COMMON_GDB_PATH__
#define __COMMON_GDB_PATH__

#include <iomanip>
#include <iostream>
#include <list>
#include <string>

namespace gdb {
namespace filesystem {

/* Stub implementation of C++17 std::filesystem::path.  */

class path
{
public:
  path () noexcept {}

  template<class T>
  path (const T &s);

  /* Replace this path by OTHER.  */
  template<class T>
  path &operator= (const T &other);

  /* Append OTHER to this path.  */
  template<class T>
  path &operator/= (const T &other);

  /* Same as operator/=.  */
  template<class T>
  path &append (const T &other)
    {
      return *this /= other;
    }

  /* Append RHS to LHS and return the resulting path.  */
  template<class T>
  friend path operator/ (path lhs, const T &rhs);

  /* Add OTHER to this path without adding a dir seperator.  */
  template<class T>
  path &operator+= (const T &other);

  /* Same as operator+=.  */
  template<class T>
  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 <std::string> 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<class T>
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<class T>
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<class T>
path &
path::operator/= (const T &other)
{
  return *this /= path (other);
}

/* See declaration.  */

template<class T>
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<class T>
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<class T>
  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__ */
/* Self tests for 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 <http://www.gnu.org/licenses/>.  */

#include <iostream>

#include "gdb_path.h"

//namespace selftest {

namespace fs = gdb::filesystem;
static int i = 0;

template<class T>
void
print_path (T &p)
{
  std::cout << i++ << ":\n";

  std::cout << "string =        " << p.string () << "\n";
  std::cout << "c_str =         " << p.c_str () << "\n";

  std::cout << "root_name =     " << p.root_name () << "\n";
  std::cout << "root_dir =      " << p.root_directory () << "\n";
  std::cout << "root_path =     " << p.root_path () << "\n";
  std::cout << "relative_path = " << p.relative_path () << "\n";
  std::cout << "parent_path =   " << p.parent_path () << "\n";
  std::cout << "filename =      " << p.filename () << "\n";
  std::cout << "stem =          " << p.stem () << "\n";
  std::cout << "extension =     " << p.extension () << "\n";

  std::cout << "\n";
}

int main ()
{
  fs::path p ("/");

  print_path (p);

  p.clear ();


  p = "C:1/2///";
  print_path (p);
  p.clear ();


  p = "C:/1/2///";
  print_path (p);
  p.clear ();


  p = fs::path ("/1/x/y/z/../../..///2/.");
  fs::path q  = "3";
  fs::path r  = "4";
  fs::path s;

  s = q / r;
  p /= s;
  p /= std::string ("5");
  p /= "6";
  p.append (".7");

  p /= ".";
  p /= "x";
  p /= "..";

  print_path (p);

  p += fs::path (".foo");

  print_path (p);

  p += ".bar";

  print_path (p);

  fs::path t;
  print_path (t);


  gdb::path gp ("/a/b/c/$x/$y/e/f");
  print_path (gp);

  gp.type = gdb::TARGET_PATH;
  gp.substitute ("$x", "d");
  gp.substitute ("$y", "");
  print_path (gp);

  gp.clear ();
  gp /= "../foo";
  print_path (gp);

}

//} /* namespace selftest */

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]