};
typedef struct linespec *linespec_p;
+/* A canonical linespec represented as a symtab-related string.
+
+ Each entry represents the "SYMTAB:SUFFIX" linespec string.
+ SYMTAB can be converted for example by symtab_to_fullname or
+ symtab_to_filename_for_display as needed. */
+
+struct linespec_canonical_name
+{
+ /* Remaining text part of the linespec string. */
+ char *suffix;
+
+ /* If NULL then SUFFIX is the whole linespec string. */
+ struct symtab *symtab;
+};
+
/* An instance of this is used to keep all state while linespec
operates. This instance is passed around as a 'this' pointer to
the various implementation methods. */
struct linespec_result *canonical;
/* Canonical strings that mirror the symtabs_and_lines result. */
- char **canonical_names;
+ struct linespec_canonical_name *canonical_names;
/* This is a set of address_entry objects which is used to prevent
duplicate symbols from being entered into the result. */
if (self->canonical)
{
- char *canonical_name = NULL;
+ struct linespec_canonical_name *canonical;
self->canonical_names = xrealloc (self->canonical_names,
- sals->nelts * sizeof (char *));
+ (sals->nelts
+ * sizeof (*self->canonical_names)));
+ canonical = &self->canonical_names[sals->nelts - 1];
if (!literal_canonical && sal->symtab)
{
const char *fullname = symtab_to_fullname (sal->symtab);
the time being. */
if (symname != NULL && sal->line != 0
&& self->language->la_language == language_ada)
- canonical_name = xstrprintf ("%s:%s:%d", fullname, symname,
- sal->line);
+ canonical->suffix = xstrprintf ("%s:%d", symname, sal->line);
else if (symname != NULL)
- canonical_name = xstrprintf ("%s:%s", fullname, symname);
+ canonical->suffix = xstrdup (symname);
else
- canonical_name = xstrprintf ("%s:%d", fullname, sal->line);
+ canonical->suffix = xstrprintf ("%d", sal->line);
+ canonical->symtab = sal->symtab;
+ }
+ else
+ {
+ if (symname != NULL)
+ canonical->suffix = xstrdup (symname);
+ else
+ canonical->suffix = NULL;
+ canonical->symtab = NULL;
}
- else if (symname != NULL)
- canonical_name = xstrdup (symname);
-
- self->canonical_names[sals->nelts - 1] = canonical_name;
}
}
return NULL;
}
+/* Convert CANONICAL to its string representation using
+ symtab_to_fullname for SYMTAB. The caller must xfree the result. */
+
+static char *
+canonical_to_fullform (const struct linespec_canonical_name *canonical)
+{
+ if (canonical->symtab == NULL)
+ return xstrdup (canonical->suffix);
+ else
+ return xstrprintf ("%s:%s", symtab_to_fullname (canonical->symtab),
+ canonical->suffix);
+}
+
/* Given FILTERS, a list of canonical names, filter the sals in RESULT
and store the result in SELF->CANONICAL. */
for (j = 0; j < result->nelts; ++j)
{
- if (strcmp (name, self->canonical_names[j]) == 0)
+ const struct linespec_canonical_name *canonical;
+ char *fullform;
+ struct cleanup *cleanup;
+
+ canonical = &self->canonical_names[j];
+ fullform = canonical_to_fullform (canonical);
+ cleanup = make_cleanup (xfree, fullform);
+
+ if (strcmp (name, fullform) == 0)
add_sal_to_sals_basic (&lsal.sals, &result->sals[j]);
+
+ do_cleanups (cleanup);
}
if (lsal.sals.nelts > 0)
VEC_safe_push (linespec_sals, self->canonical->sals, &lsal);
}
+/* A structure that contains two string representations of a struct
+ linespec_canonical_name:
+ - one where the the symtab's fullname is used;
+ - one where the filename followed the "set filename-display"
+ setting. */
+
+struct decode_line_2_item
+{
+ /* The form using symtab_to_fullname.
+ It must be xfree'ed after use. */
+ char *fullform;
+
+ /* The form using symtab_to_filename_for_display.
+ It must be xfree'ed after use. */
+ char *displayform;
+
+ /* Field is initialized to zero and it is set to one if the user
+ requested breakpoint for this entry. */
+ unsigned int selected : 1;
+};
+
+/* Helper for qsort to sort decode_line_2_item entries by DISPLAYFORM and
+ secondarily by FULLFORM. */
+
+static int
+decode_line_2_compare_items (const void *ap, const void *bp)
+{
+ const struct decode_line_2_item *a = ap;
+ const struct decode_line_2_item *b = bp;
+ int retval;
+
+ retval = strcmp (a->displayform, b->displayform);
+ if (retval != 0)
+ return retval;
+
+ return strcmp (a->fullform, b->fullform);
+}
+
/* Handle multiple results in RESULT depending on SELECT_MODE. This
will either return normally, throw an exception on multiple
results, or present a menu to the user. On return, the SALS vector
struct symtabs_and_lines *result,
const char *select_mode)
{
- const char *iter;
char *args, *prompt;
int i;
struct cleanup *old_chain;
- VEC (const_char_ptr) *item_names = NULL, *filters = NULL;
+ VEC (const_char_ptr) *filters = NULL;
struct get_number_or_range_state state;
+ struct decode_line_2_item *items;
+ int items_count;
gdb_assert (select_mode != multiple_symbols_all);
gdb_assert (self->canonical != NULL);
+ gdb_assert (result->nelts >= 1);
+
+ old_chain = make_cleanup (VEC_cleanup (const_char_ptr), &filters);
- old_chain = make_cleanup (VEC_cleanup (const_char_ptr), &item_names);
- make_cleanup (VEC_cleanup (const_char_ptr), &filters);
- for (i = 0; i < result->nelts; ++i)
+ /* Prepare ITEMS array. */
+ items_count = result->nelts;
+ items = xmalloc (sizeof (*items) * items_count);
+ make_cleanup (xfree, items);
+ for (i = 0; i < items_count; ++i)
{
- int j, found = 0;
- const char *iter;
+ const struct linespec_canonical_name *canonical;
+ struct decode_line_2_item *item;
+
+ canonical = &self->canonical_names[i];
+ gdb_assert (canonical->suffix != NULL);
+ item = &items[i];
- gdb_assert (self->canonical_names[i] != NULL);
- for (j = 0; VEC_iterate (const_char_ptr, item_names, j, iter); ++j)
+ item->fullform = canonical_to_fullform (canonical);
+ make_cleanup (xfree, item->fullform);
+
+ if (canonical->symtab == NULL)
+ item->displayform = canonical->suffix;
+ else
{
- if (strcmp (iter, self->canonical_names[i]) == 0)
- {
- found = 1;
- break;
- }
+ const char *fn_for_display;
+
+ fn_for_display = symtab_to_filename_for_display (canonical->symtab);
+ item->displayform = xstrprintf ("%s:%s", fn_for_display,
+ canonical->suffix);
+ make_cleanup (xfree, item->displayform);
}
- if (!found)
- VEC_safe_push (const_char_ptr, item_names, self->canonical_names[i]);
+ item->selected = 0;
}
- if (select_mode == multiple_symbols_cancel
- && VEC_length (const_char_ptr, item_names) > 1)
+ /* Sort the list of method names. */
+ qsort (items, items_count, sizeof (*items), decode_line_2_compare_items);
+
+ /* Remove entries with the same FULLFORM. */
+ if (items_count >= 2)
+ {
+ struct decode_line_2_item *dst, *src;
+
+ dst = items;
+ for (src = &items[1]; src < &items[items_count]; src++)
+ if (strcmp (src->fullform, dst->fullform) != 0)
+ *++dst = *src;
+ items_count = dst + 1 - items;
+ }
+
+ if (select_mode == multiple_symbols_cancel && items_count > 1)
error (_("canceled because the command is ambiguous\n"
"See set/show multiple-symbol."));
- if (select_mode == multiple_symbols_all
- || VEC_length (const_char_ptr, item_names) == 1)
+ if (select_mode == multiple_symbols_all || items_count == 1)
{
do_cleanups (old_chain);
convert_results_to_lsals (self, result);
return;
}
- /* Sort the list of method names alphabetically. */
- qsort (VEC_address (const_char_ptr, item_names),
- VEC_length (const_char_ptr, item_names),
- sizeof (const_char_ptr), compare_strings);
-
printf_unfiltered (_("[0] cancel\n[1] all\n"));
- for (i = 0; VEC_iterate (const_char_ptr, item_names, i, iter); ++i)
- printf_unfiltered ("[%d] %s\n", i + 2, iter);
+ for (i = 0; i < items_count; i++)
+ printf_unfiltered ("[%d] %s\n", i + 2, items[i].displayform);
prompt = getenv ("PS2");
if (prompt == NULL)
}
num -= 2;
- if (num >= VEC_length (const_char_ptr, item_names))
+ if (num >= items_count)
printf_unfiltered (_("No choice number %d.\n"), num);
else
{
- const char *elt = VEC_index (const_char_ptr, item_names, num);
+ struct decode_line_2_item *item = &items[num];
- if (elt != NULL)
+ if (!item->selected)
{
- VEC_safe_push (const_char_ptr, filters, elt);
- VEC_replace (const_char_ptr, item_names, num, NULL);
+ VEC_safe_push (const_char_ptr, filters, item->fullform);
+ item->selected = 1;
}
else
{
make_cleanup (xfree, state->canonical_names);
for (i = 0; i < result.nelts; ++i)
{
- gdb_assert (state->canonical_names[i] != NULL);
- make_cleanup (xfree, state->canonical_names[i]);
+ gdb_assert (state->canonical_names[i].suffix != NULL);
+ make_cleanup (xfree, state->canonical_names[i].suffix);
}
}
--- /dev/null
+# Copyright 2013 Free Software Foundation, Inc.
+
+# 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/>.
+
+standard_testfile lspec.cc
+
+if {[skip_cplus_tests]} {
+ unsupported ${testfile}.exp
+ return
+}
+
+set opts {debug c++}
+set objfile1 [standard_output_file ${testfile}one.o]
+set objfile2 [standard_output_file ${testfile}two.o]
+
+if { [file pathtype $objdir] == "relative" } {
+ untested "objdir $objdir should be absolute"
+ return
+}
+set saved_pwd [pwd]
+cd $srcdir/${subdir}/base/one
+set err1 [gdb_compile "thefile.cc" $objfile1 object $opts]
+cd $saved_pwd
+cd $srcdir/${subdir}/base/two
+set err2 [gdb_compile "thefile.cc" $objfile2 object $opts]
+cd $saved_pwd
+if { $err1 != "" || $err2 != "" } {
+ untested "compilation failed"
+ return -1
+}
+
+if { [gdb_compile "$srcdir/${subdir}/$srcfile $objfile1 $objfile2" \
+ $binfile executable $opts] != "" } {
+ return -1
+}
+
+clean_restart ${testfile}
+
+gdb_test_no_output "set multiple-symbols ask"
+
+gdb_test_no_output "set filename-display absolute"
+set cmd "break twodup"
+set test "break twodup absolute"
+gdb_test_multiple $cmd $test {
+ -re "^$cmd\r\n\\\[0\\\] cancel\r\n\\\[1\\\] all\r\n\\\[2\\\] \[^\r\n\]+/base/one/thefile\\.cc:twodup\\\(\\\)\r\n\\\[3\\\] \[^\r\n\]+/base/two/thefile\\.cc:twodup\\\(\\\)\r\n> $" {
+ pass $test
+ }
+}
+gdb_test "0" "canceled"
+
+gdb_test_no_output "set filename-display relative"
+
+set cmd "break twodup"
+set test "break twodup relative"
+gdb_test_multiple $cmd $test {
+ -re "^$cmd\r\n\\\[0\\\] cancel\r\n\\\[1\\\] all\r\n\\\[2\\\] thefile\\.cc:twodup\\\(\\\)\r\n\\\[3\\\] thefile\\.cc:twodup\\\(\\\)\r\n> $" {
+ pass $test
+ }
+}
+gdb_test "2" "^2\r\nBreakpoint \[0-9\]+ at 0x\[0-9a-f\]+: file thefile\\.cc, line \[0-9a-f\]+\\."
+
+gdb_breakpoint "body_elsewhere"
+
+gdb_run_cmd
+gdb_test "" "Breakpoint \[0-9\]+, twodup \\(\\) at thefile.cc:\[0-9\]+\r\n.*" "expect breakpoint"
+
+gdb_test "info source" "\r\nLocated in \[^\r\n\]+/base/one/thefile\\.cc\r\n.*"
+
+gdb_continue_to_breakpoint "body_elsewhere" ".* body_elsewhere marker .*"
+
+delete_breakpoints
+
+set cmd "break twodup"
+set test "break twodup relative other"
+gdb_test_multiple $cmd $test {
+ -re "^$cmd\r\n\\\[0\\\] cancel\r\n\\\[1\\\] all\r\n\\\[2\\\] thefile\\.cc:twodup\\\(\\\)\r\n\\\[3\\\] thefile\\.cc:twodup\\\(\\\)\r\n> $" {
+ pass $test
+ }
+}
+gdb_test "3" "^3\r\nBreakpoint \[0-9\]+ at 0x\[0-9a-f\]+: file thefile\\.cc, line \[0-9a-f\]+\\."
+
+gdb_breakpoint "body_elsewhere"
+
+gdb_run_cmd
+gdb_test "" "Breakpoint \[0-9\]+, twodup \\(\\) at thefile.cc:\[0-9\]+\r\n.*" "expect breakpoint other"
+
+gdb_test "info source" "\r\nLocated in \[^\r\n\]+/base/two/thefile\\.cc\r\n.*" "info source other"
+
+gdb_continue_to_breakpoint "body_elsewhere other" ".* body_elsewhere marker .*"