gdb: make struct output_source_filename_data more C++ like

In a future commit I'm going to be making some changes to the 'info
sources' command.  While looking at the code I noticed that things
could be improved by making struct output_source_filename_data more
C++ like (private member variables, and more member functions).
That's what this commit does.

The 'info sources' filename filtering is split out into a separate
class in this commit.  In a future commit this new filter
class (info_sources_filter) will move into the header file and be used
from the MI code.

There should be no user visible changes after this commit.

gdb/ChangeLog:

	* symtab.c (struct info_sources_filter): New.
	(info_sources_filter::info_sources_filter): New function.
	(info_sources_filter::matches): New function.
	(info_sources_filter::print): New function.
	(struct filename_partial_match_opts): Moved to later in the file
	and update the comment.
	(struct output_source_filename_data)
	<output_source_filename_data>: New constructor.  <regexp>: Delete,
	this is now in info_sources_filter.  <c_regexp>: Delete, this is
	now in info_sources_filter.  <reset_output>: New member function.
	<filename_seen_cache>: Rename to m_filename_seen_cache, change
	from being a pointer, to being an actual object.  <first>: Rename
	to m_first.  <print_header>: New member function. <partial_match>:
	Delete.
	(output_source_filename_data::output): Update now
	m_filename_seen_cache is no longer a pointer, and for other member
	variable name changes. Add a header comment.
	(print_info_sources_header): Renamed to...
	(output_source_filename_data::print_header): ...this.  Update now
	it's a member function and to take account of member variable
	renaming.
	(info_sources_command): Add a header comment, delete stack local
	filename_seen_cache, initialization of output_source_filename_data
	is now done by the constructor.  Call print_header member function
	instead of print_info_sources_header, call reset_output member
	function instead of manually performing the reset.
This commit is contained in:
Andrew Burgess 2021-04-01 14:51:24 +01:00
parent fc4d5ebf8f
commit 4a0788e08c
2 changed files with 248 additions and 107 deletions

View file

@ -1,3 +1,32 @@
2021-06-25 Andrew Burgess <andrew.burgess@embecosm.com>
* symtab.c (struct info_sources_filter): New.
(info_sources_filter::info_sources_filter): New function.
(info_sources_filter::matches): New function.
(info_sources_filter::print): New function.
(struct filename_partial_match_opts): Moved to later in the file
and update the comment.
(struct output_source_filename_data)
<output_source_filename_data>: New constructor. <regexp>: Delete,
this is now in info_sources_filter. <c_regexp>: Delete, this is
now in info_sources_filter. <reset_output>: New member function.
<filename_seen_cache>: Rename to m_filename_seen_cache, change
from being a pointer, to being an actual object. <first>: Rename
to m_first. <print_header>: New member function. <partial_match>:
Delete.
(output_source_filename_data::output): Update now
m_filename_seen_cache is no longer a pointer, and for other member
variable name changes. Add a header comment.
(print_info_sources_header): Renamed to...
(output_source_filename_data::print_header): ...this. Update now
it's a member function and to take account of member variable
renaming.
(info_sources_command): Add a header comment, delete stack local
filename_seen_cache, initialization of output_source_filename_data
is now done by the constructor. Call print_header member function
instead of print_info_sources_header, call reset_output member
function instead of manually performing the reset.
2021-06-25 Andrew Burgess <andrew.burgess@embecosm.com>
* dwarf2/read.c (struct dwarf2_base_index_functions)

View file

@ -4200,46 +4200,190 @@ operator_chars (const char *p, const char **end)
}
/* What part to match in a file name. */
struct filename_partial_match_opts
/* Class used to encapsulate the filename filtering for the "info sources"
command. */
struct info_sources_filter
{
/* Only match the directory name part. */
bool dirname = false;
/* If filename filtering is being used (see M_C_REGEXP) then which part
of the filename is being filtered against? */
enum class match_on
{
/* Match against the full filename. */
FULLNAME,
/* Only match the basename part. */
bool basename = false;
/* Match only against the directory part of the full filename. */
DIRNAME,
/* Match only against the basename part of the full filename. */
BASENAME
};
/* Create a filter of MATCH_TYPE using regular expression REGEXP. If
REGEXP is nullptr then all files will match the filter and MATCH_TYPE
is ignored.
The string pointed too by REGEXP must remain live and unchanged for
this lifetime of this object as the object only retains a copy of the
pointer. */
info_sources_filter (match_on match_type, const char *regexp);
DISABLE_COPY_AND_ASSIGN (info_sources_filter);
/* Does FULLNAME match the filter defined by this object, return true if
it does, otherwise, return false. If there is no filtering defined
then this function will always return true. */
bool matches (const char *fullname) const;
/* Print a single line describing this filter, used as part of the "info
sources" command output. If there is no filter in place then nothing
is printed. */
void print () const;
private:
/* The type of filtering in place. */
match_on m_match_type;
/* Points to the original regexp used to create this filter. */
const char *m_regexp;
/* A compiled version of M_REGEXP. This object is only given a value if
M_REGEXP is not nullptr and is not the empty string. */
gdb::optional<compiled_regex> m_c_regexp;
};
/* Data structure to maintain printing state for output_source_filename. */
/* See class declaration. */
info_sources_filter::info_sources_filter (match_on match_type,
const char *regexp)
: m_match_type (match_type),
m_regexp (regexp)
{
/* Setup the compiled regular expression M_C_REGEXP based on M_REGEXP. */
if (m_regexp != nullptr && *m_regexp != '\0')
{
gdb_assert (m_regexp != nullptr);
int cflags = REG_NOSUB;
#ifdef HAVE_CASE_INSENSITIVE_FILE_SYSTEM
cflags |= REG_ICASE;
#endif
m_c_regexp.emplace (m_regexp, cflags, _("Invalid regexp"));
}
}
/* See class declaration. */
bool
info_sources_filter::matches (const char *fullname) const
{
/* Does it match regexp? */
if (m_c_regexp.has_value ())
{
const char *to_match;
std::string dirname;
switch (m_match_type)
{
case match_on::DIRNAME:
dirname = ldirname (fullname);
to_match = dirname.c_str ();
break;
case match_on::BASENAME:
to_match = lbasename (fullname);
break;
case match_on::FULLNAME:
to_match = fullname;
break;
}
if (m_c_regexp->exec (to_match, 0, NULL, 0) != 0)
return false;
}
return true;
}
/* See class declaration. */
void
info_sources_filter::print () const
{
if (m_c_regexp.has_value ())
{
gdb_assert (m_regexp != nullptr);
switch (m_match_type)
{
case match_on::DIRNAME:
printf_filtered (_("(dirname matching regular expression \"%s\")"),
m_regexp);
break;
case match_on::BASENAME:
printf_filtered (_("(basename matching regular expression \"%s\")"),
m_regexp);
break;
case match_on::FULLNAME:
printf_filtered (_("(filename matching regular expression \"%s\")"),
m_regexp);
break;
}
}
}
/* Data structure to maintain the state used for printing the results of
the 'info sources' command. */
struct output_source_filename_data
{
/* Output only filenames matching REGEXP. */
std::string regexp;
gdb::optional<compiled_regex> c_regexp;
/* Possibly only match a part of the filename. */
filename_partial_match_opts partial_match;
/* Create an object for displaying the results of the 'info sources'
command. FILTER must remain valid and unchanged for the lifetime of
this object as this object retains a reference to FILTER. */
output_source_filename_data (const info_sources_filter &filter)
: m_filter (filter)
{ /* Nothing. */ }
DISABLE_COPY_AND_ASSIGN (output_source_filename_data);
/* Cache of what we've seen so far. */
struct filename_seen_cache *filename_seen_cache;
/* Reset enough state of this object so we can match against a new set of
files. The existing regular expression is retained though. */
void reset_output ()
{
m_first = true;
m_filename_seen_cache.clear ();
}
/* Flag of whether we're printing the first one. */
int first;
/* Worker for sources_info. Force line breaks at ,'s.
NAME is the name to print. */
/* Worker for sources_info. Force line breaks at ,'s. NAME is the name
to print. */
void output (const char *name);
/* Prints the header messages for the source files that will be printed
with the matching info present in the current object state.
SYMBOL_MSG is a message that describes what will or has been done with
the symbols of the matching source files. */
void print_header (const char *symbol_msg);
/* An overload suitable for use as a callback to
quick_symbol_functions::map_symbol_filenames. */
void operator() (const char *filename, const char *fullname)
{
output (fullname != nullptr ? fullname : filename);
}
private:
/* Flag of whether we're printing the first one. */
bool m_first = true;
/* Cache of what we've seen so far. */
filename_seen_cache m_filename_seen_cache;
/* How source filename should be filtered. */
const info_sources_filter &m_filter;
};
/* See comment in class declaration above. */
void
output_source_filename_data::output (const char *name)
{
@ -4252,42 +4396,45 @@ output_source_filename_data::output (const char *name)
situation. I'm not sure whether this can also happen for
symtabs; it doesn't hurt to check. */
/* Was NAME already seen? */
if (filename_seen_cache->seen (name))
{
/* Yes; don't print it again. */
return;
}
/* Was NAME already seen? If so, then don't print it again. */
if (m_filename_seen_cache.seen (name))
return;
/* Does it match regexp? */
if (c_regexp.has_value ())
{
const char *to_match;
std::string dirname;
if (partial_match.dirname)
{
dirname = ldirname (name);
to_match = dirname.c_str ();
}
else if (partial_match.basename)
to_match = lbasename (name);
else
to_match = name;
if (c_regexp->exec (to_match, 0, NULL, 0) != 0)
return;
}
/* If the filter rejects this file then don't print it. */
if (!m_filter.matches (name))
return;
/* Print it and reset *FIRST. */
if (! first)
if (!m_first)
printf_filtered (", ");
first = 0;
m_first = false;
wrap_here ("");
fputs_styled (name, file_name_style.style (), gdb_stdout);
}
/* See comment is class declaration above. */
void
output_source_filename_data::print_header (const char *symbol_msg)
{
puts_filtered (symbol_msg);
m_filter.print ();
puts_filtered ("\n");
}
/* For the 'info sources' command, what part of the file names should we be
matching the user supplied regular expression against? */
struct filename_partial_match_opts
{
/* Only match the directory name part. */
bool dirname = false;
/* Only match the basename part. */
bool basename = false;
};
using isrc_flag_option_def
= gdb::option::flag_option_def<filename_partial_match_opts>;
@ -4316,31 +4463,6 @@ make_info_sources_options_def_group (filename_partial_match_opts *isrc_opts)
return {{info_sources_option_defs}, isrc_opts};
}
/* Prints the header message for the source files that will be printed
with the matching info present in DATA. SYMBOL_MSG is a message
that tells what will or has been done with the symbols of the
matching source files. */
static void
print_info_sources_header (const char *symbol_msg,
const struct output_source_filename_data *data)
{
puts_filtered (symbol_msg);
if (!data->regexp.empty ())
{
if (data->partial_match.dirname)
printf_filtered (_("(dirname matching regular expression \"%s\")"),
data->regexp.c_str ());
else if (data->partial_match.basename)
printf_filtered (_("(basename matching regular expression \"%s\")"),
data->regexp.c_str ());
else
printf_filtered (_("(filename matching regular expression \"%s\")"),
data->regexp.c_str ());
}
puts_filtered ("\n");
}
/* Completer for "info sources". */
static void
@ -4354,49 +4476,41 @@ info_sources_command_completer (cmd_list_element *ignore,
return;
}
/* Implement the 'info sources' command. */
static void
info_sources_command (const char *args, int from_tty)
{
struct output_source_filename_data data;
if (!have_full_symbols () && !have_partial_symbols ())
{
error (_("No symbol table is loaded. Use the \"file\" command."));
}
filename_seen_cache filenames_seen;
auto group = make_info_sources_options_def_group (&data.partial_match);
error (_("No symbol table is loaded. Use the \"file\" command."));
filename_partial_match_opts match_opts;
auto group = make_info_sources_options_def_group (&match_opts);
gdb::option::process_options
(&args, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_ERROR, group);
if (args != NULL && *args != '\000')
data.regexp = args;
data.filename_seen_cache = &filenames_seen;
data.first = 1;
if (data.partial_match.dirname && data.partial_match.basename)
if (match_opts.dirname && match_opts.basename)
error (_("You cannot give both -basename and -dirname to 'info sources'."));
if ((data.partial_match.dirname || data.partial_match.basename)
&& data.regexp.empty ())
error (_("Missing REGEXP for 'info sources'."));
if (data.regexp.empty ())
data.c_regexp.reset ();
const char *regex = nullptr;
if (args != nullptr && *args != '\000')
regex = args;
if ((match_opts.dirname || match_opts.basename) && regex == nullptr)
error (_("Missing REGEXP for 'info sources'."));
info_sources_filter::match_on match_type;
if (match_opts.dirname)
match_type = info_sources_filter::match_on::DIRNAME;
else if (match_opts.basename)
match_type = info_sources_filter::match_on::BASENAME;
else
{
int cflags = REG_NOSUB;
#ifdef HAVE_CASE_INSENSITIVE_FILE_SYSTEM
cflags |= REG_ICASE;
#endif
data.c_regexp.emplace (data.regexp.c_str (), cflags,
_("Invalid regexp"));
}
match_type = info_sources_filter::match_on::FULLNAME;
print_info_sources_header
(_("Source files for which symbols have been read in:\n"), &data);
info_sources_filter filter (match_type, regex);
output_source_filename_data data (filter);
data.print_header (_("Source files for which symbols have been read in:\n"));
for (objfile *objfile : current_program_space->objfiles ())
{
@ -4412,11 +4526,9 @@ info_sources_command (const char *args, int from_tty)
}
printf_filtered ("\n\n");
print_info_sources_header
(_("Source files for which symbols will be read in on demand:\n"), &data);
data.print_header (_("Source files for which symbols will be read in on demand:\n"));
filenames_seen.clear ();
data.first = 1;
data.reset_output ();
map_symbol_filenames (data, true /*need_fullname*/);
printf_filtered ("\n");
}