+ linespec_sals lsal;
+
+ for (size_t j = 0; j < result->size (); ++j)
+ {
+ const struct linespec_canonical_name *canonical;
+
+ canonical = &self->canonical_names[j];
+ std::string fullform = canonical_to_fullform (canonical);
+
+ if (name == fullform)
+ lsal.sals.push_back ((*result)[j]);
+ }
+
+ if (!lsal.sals.empty ())
+ {
+ lsal.canonical = xstrdup (name);
+ self->canonical->lsals.push_back (std::move (lsal));
+ }
+ }
+
+ self->canonical->pre_expanded = 0;
+}
+
+/* Store RESULT into SELF->CANONICAL. */
+
+static void
+convert_results_to_lsals (struct linespec_state *self,
+ std::vector<symtab_and_line> *result)
+{
+ struct linespec_sals lsal;
+
+ lsal.canonical = NULL;
+ lsal.sals = std::move (*result);
+ self->canonical->lsals.push_back (std::move (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
+{
+ decode_line_2_item (std::string &&fullform_, std::string &&displayform_,
+ bool selected_)
+ : fullform (std::move (fullform_)),
+ displayform (std::move (displayform_)),
+ selected (selected_)
+ {
+ }
+
+ /* The form using symtab_to_fullname. */
+ std::string fullform;
+
+ /* The form using symtab_to_filename_for_display. */
+ std::string 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 std::sort to sort decode_line_2_item entries by
+ DISPLAYFORM and secondarily by FULLFORM. */
+
+static bool
+decode_line_2_compare_items (const decode_line_2_item &a,
+ const decode_line_2_item &b)
+{
+ if (a.displayform != b.displayform)
+ return a.displayform < b.displayform;
+ return 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
+ in SELF->CANONICAL is set up properly. */
+
+static void
+decode_line_2 (struct linespec_state *self,
+ std::vector<symtab_and_line> *result,
+ const char *select_mode)
+{
+ char *args;
+ const char *prompt;
+ int i;
+ std::vector<const char *> filters;
+ std::vector<struct decode_line_2_item> items;
+
+ gdb_assert (select_mode != multiple_symbols_all);
+ gdb_assert (self->canonical != NULL);
+ gdb_assert (!result->empty ());
+
+ /* Prepare ITEMS array. */
+ for (i = 0; i < result->size (); ++i)
+ {
+ const struct linespec_canonical_name *canonical;
+ std::string displayform;
+
+ canonical = &self->canonical_names[i];
+ gdb_assert (canonical->suffix != NULL);
+
+ std::string fullform = canonical_to_fullform (canonical);
+
+ if (canonical->symtab == NULL)
+ displayform = canonical->suffix;
+ else
+ {
+ const char *fn_for_display;
+
+ fn_for_display = symtab_to_filename_for_display (canonical->symtab);
+ displayform = string_printf ("%s:%s", fn_for_display,
+ canonical->suffix);
+ }
+
+ items.emplace_back (std::move (fullform), std::move (displayform),
+ false);
+ }
+
+ /* Sort the list of method names. */
+ std::sort (items.begin (), items.end (), decode_line_2_compare_items);
+
+ /* Remove entries with the same FULLFORM. */
+ items.erase (std::unique (items.begin (), items.end (),
+ [] (const struct decode_line_2_item &a,
+ const struct decode_line_2_item &b)
+ {
+ return a.fullform == b.fullform;
+ }),
+ items.end ());
+
+ if (select_mode == multiple_symbols_cancel && items.size () > 1)
+ error (_("canceled because the command is ambiguous\n"
+ "See set/show multiple-symbol."));
+
+ if (select_mode == multiple_symbols_all || items.size () == 1)
+ {
+ convert_results_to_lsals (self, result);
+ return;
+ }
+
+ printf_unfiltered (_("[0] cancel\n[1] all\n"));
+ for (i = 0; i < items.size (); i++)
+ printf_unfiltered ("[%d] %s\n", i + 2, items[i].displayform.c_str ());
+
+ prompt = getenv ("PS2");
+ if (prompt == NULL)
+ {
+ prompt = "> ";
+ }
+ args = command_line_input (prompt, "overload-choice");
+
+ if (args == 0 || *args == 0)
+ error_no_arg (_("one or more choice numbers"));
+
+ number_or_range_parser parser (args);
+ while (!parser.finished ())
+ {
+ int num = parser.get_number ();
+
+ if (num == 0)
+ error (_("canceled"));
+ else if (num == 1)
+ {
+ /* We intentionally make this result in a single breakpoint,
+ contrary to what older versions of gdb did. The
+ rationale is that this lets a user get the
+ multiple_symbols_all behavior even with the 'ask'
+ setting; and he can get separate breakpoints by entering
+ "2-57" at the query. */
+ convert_results_to_lsals (self, result);
+ return;
+ }
+
+ num -= 2;
+ if (num >= items.size ())
+ printf_unfiltered (_("No choice number %d.\n"), num);
+ else
+ {
+ struct decode_line_2_item *item = &items[num];
+
+ if (!item->selected)
+ {
+ filters.push_back (item->fullform.c_str ());
+ item->selected = 1;
+ }
+ else
+ {
+ printf_unfiltered (_("duplicate request for %d ignored.\n"),
+ num + 2);
+ }
+ }
+ }
+
+ filter_results (self, result, filters);
+}
+
+\f
+
+/* The parser of linespec itself. */
+
+/* Throw an appropriate error when SYMBOL is not found (optionally in
+ FILENAME). */
+
+static void ATTRIBUTE_NORETURN
+symbol_not_found_error (const char *symbol, const char *filename)
+{
+ if (symbol == NULL)
+ symbol = "";
+
+ if (!have_full_symbols ()
+ && !have_partial_symbols ()
+ && !have_minimal_symbols ())
+ throw_error (NOT_FOUND_ERROR,
+ _("No symbol table is loaded. Use the \"file\" command."));
+
+ /* If SYMBOL starts with '$', the user attempted to either lookup
+ a function/variable in his code starting with '$' or an internal
+ variable of that name. Since we do not know which, be concise and
+ explain both possibilities. */
+ if (*symbol == '$')
+ {
+ if (filename)
+ throw_error (NOT_FOUND_ERROR,
+ _("Undefined convenience variable or function \"%s\" "
+ "not defined in \"%s\"."), symbol, filename);
+ else
+ throw_error (NOT_FOUND_ERROR,
+ _("Undefined convenience variable or function \"%s\" "
+ "not defined."), symbol);
+ }
+ else
+ {
+ if (filename)
+ throw_error (NOT_FOUND_ERROR,
+ _("Function \"%s\" not defined in \"%s\"."),
+ symbol, filename);
+ else
+ throw_error (NOT_FOUND_ERROR,
+ _("Function \"%s\" not defined."), symbol);
+ }
+}
+
+/* Throw an appropriate error when an unexpected token is encountered
+ in the input. */
+
+static void ATTRIBUTE_NORETURN
+unexpected_linespec_error (linespec_parser *parser)
+{
+ linespec_token token;
+ static const char * token_type_strings[]
+ = {"keyword", "colon", "string", "number", "comma", "end of input"};
+
+ /* Get the token that generated the error. */
+ token = linespec_lexer_lex_one (parser);
+
+ /* Finally, throw the error. */
+ if (token.type == LSTOKEN_STRING || token.type == LSTOKEN_NUMBER
+ || token.type == LSTOKEN_KEYWORD)
+ {
+ gdb::unique_xmalloc_ptr<char> string = copy_token_string (token);
+ throw_error (GENERIC_ERROR,
+ _("malformed linespec error: unexpected %s, \"%s\""),
+ token_type_strings[token.type], string.get ());
+ }
+ else
+ throw_error (GENERIC_ERROR,
+ _("malformed linespec error: unexpected %s"),
+ token_type_strings[token.type]);
+}
+
+/* Throw an undefined label error. */
+
+static void ATTRIBUTE_NORETURN
+undefined_label_error (const char *function, const char *label)
+{
+ if (function != NULL)
+ throw_error (NOT_FOUND_ERROR,
+ _("No label \"%s\" defined in function \"%s\"."),
+ label, function);
+ else
+ throw_error (NOT_FOUND_ERROR,
+ _("No label \"%s\" defined in current function."),
+ label);
+}
+
+/* Throw a source file not found error. */
+
+static void ATTRIBUTE_NORETURN
+source_file_not_found_error (const char *name)
+{
+ throw_error (NOT_FOUND_ERROR, _("No source file named %s."), name);
+}
+
+/* Unless at EIO, save the current stream position as completion word
+ point, and consume the next token. */
+
+static linespec_token
+save_stream_and_consume_token (linespec_parser *parser)
+{
+ if (linespec_lexer_peek_token (parser).type != LSTOKEN_EOI)
+ parser->completion_word = PARSER_STREAM (parser);
+ return linespec_lexer_consume_token (parser);
+}
+
+/* See description in linespec.h. */
+
+struct line_offset
+linespec_parse_line_offset (const char *string)
+{
+ const char *start = string;
+ struct line_offset line_offset = {0, LINE_OFFSET_NONE};
+
+ if (*string == '+')
+ {
+ line_offset.sign = LINE_OFFSET_PLUS;
+ ++string;
+ }
+ else if (*string == '-')
+ {
+ line_offset.sign = LINE_OFFSET_MINUS;
+ ++string;
+ }
+
+ if (*string != '\0' && !isdigit (*string))
+ error (_("malformed line offset: \"%s\""), start);
+
+ /* Right now, we only allow base 10 for offsets. */
+ line_offset.offset = atoi (string);
+ return line_offset;
+}
+
+/* In completion mode, if the user is still typing the number, there's
+ no possible completion to offer. But if there's already input past
+ the number, setup to expect NEXT. */
+
+static void
+set_completion_after_number (linespec_parser *parser,
+ linespec_complete_what next)
+{
+ if (*PARSER_STREAM (parser) == ' ')
+ {
+ parser->completion_word = skip_spaces (PARSER_STREAM (parser) + 1);
+ parser->complete_what = next;
+ }
+ else
+ {
+ parser->completion_word = PARSER_STREAM (parser);
+ parser->complete_what = linespec_complete_what::NOTHING;
+ }
+}
+
+/* Parse the basic_spec in PARSER's input. */
+
+static void
+linespec_parse_basic (linespec_parser *parser)
+{
+ gdb::unique_xmalloc_ptr<char> name;
+ linespec_token token;
+ std::vector<block_symbol> symbols;
+ std::vector<block_symbol> *labels;
+ std::vector<bound_minimal_symbol> minimal_symbols;
+
+ /* Get the next token. */
+ token = linespec_lexer_lex_one (parser);
+
+ /* If it is EOI or KEYWORD, issue an error. */
+ if (token.type == LSTOKEN_KEYWORD)
+ {
+ parser->complete_what = linespec_complete_what::NOTHING;
+ unexpected_linespec_error (parser);
+ }
+ else if (token.type == LSTOKEN_EOI)
+ {
+ unexpected_linespec_error (parser);
+ }
+ /* If it is a LSTOKEN_NUMBER, we have an offset. */
+ else if (token.type == LSTOKEN_NUMBER)
+ {
+ set_completion_after_number (parser, linespec_complete_what::KEYWORD);
+
+ /* Record the line offset and get the next token. */
+ name = copy_token_string (token);
+ PARSER_EXPLICIT (parser)->line_offset
+ = linespec_parse_line_offset (name.get ());
+
+ /* Get the next token. */
+ token = linespec_lexer_consume_token (parser);
+
+ /* If the next token is a comma, stop parsing and return. */
+ if (token.type == LSTOKEN_COMMA)
+ {
+ parser->complete_what = linespec_complete_what::NOTHING;
+ return;
+ }
+
+ /* If the next token is anything but EOI or KEYWORD, issue
+ an error. */
+ if (token.type != LSTOKEN_KEYWORD && token.type != LSTOKEN_EOI)
+ unexpected_linespec_error (parser);
+ }
+
+ if (token.type == LSTOKEN_KEYWORD || token.type == LSTOKEN_EOI)
+ return;
+
+ /* Next token must be LSTOKEN_STRING. */
+ if (token.type != LSTOKEN_STRING)
+ {
+ parser->complete_what = linespec_complete_what::NOTHING;
+ unexpected_linespec_error (parser);
+ }
+
+ /* The current token will contain the name of a function, method,
+ or label. */
+ name = copy_token_string (token);
+
+ if (parser->completion_tracker != NULL)
+ {
+ /* If the function name ends with a ":", then this may be an
+ incomplete "::" scope operator instead of a label separator.
+ E.g.,
+ "b klass:<tab>"
+ which should expand to:
+ "b klass::method()"
+
+ Do a tentative completion assuming the later. If we find
+ completions, advance the stream past the colon token and make
+ it part of the function name/token. */
+
+ if (!parser->completion_quote_char
+ && strcmp (PARSER_STREAM (parser), ":") == 0)
+ {
+ completion_tracker tmp_tracker;
+ const char *source_filename
+ = PARSER_EXPLICIT (parser)->source_filename;
+ symbol_name_match_type match_type
+ = PARSER_EXPLICIT (parser)->func_name_match_type;
+
+ linespec_complete_function (tmp_tracker,
+ parser->completion_word,
+ match_type,
+ source_filename);
+
+ if (tmp_tracker.have_completions ())
+ {
+ PARSER_STREAM (parser)++;
+ LS_TOKEN_STOKEN (token).length++;
+
+ name.reset (savestring (parser->completion_word,
+ (PARSER_STREAM (parser)
+ - parser->completion_word)));
+ }
+ }
+
+ PARSER_EXPLICIT (parser)->function_name = name.release ();
+ }
+ else
+ {
+ /* Try looking it up as a function/method. */
+ find_linespec_symbols (PARSER_STATE (parser),
+ PARSER_RESULT (parser)->file_symtabs, name.get (),
+ PARSER_EXPLICIT (parser)->func_name_match_type,
+ &symbols, &minimal_symbols);
+
+ if (!symbols.empty () || !minimal_symbols.empty ())
+ {
+ PARSER_RESULT (parser)->function_symbols
+ = new std::vector<block_symbol> (std::move (symbols));
+ PARSER_RESULT (parser)->minimal_symbols
+ = new std::vector<bound_minimal_symbol>
+ (std::move (minimal_symbols));
+ PARSER_EXPLICIT (parser)->function_name = name.release ();
+ }
+ else
+ {
+ /* NAME was not a function or a method. So it must be a label
+ name or user specified variable like "break foo.c:$zippo". */
+ labels = find_label_symbols (PARSER_STATE (parser), NULL,
+ &symbols, name.get ());
+ if (labels != NULL)
+ {
+ PARSER_RESULT (parser)->labels.label_symbols = labels;
+ PARSER_RESULT (parser)->labels.function_symbols
+ = new std::vector<block_symbol> (std::move (symbols));
+ PARSER_EXPLICIT (parser)->label_name = name.release ();
+ }
+ else if (token.type == LSTOKEN_STRING
+ && *LS_TOKEN_STOKEN (token).ptr == '$')
+ {
+ /* User specified a convenience variable or history value. */
+ PARSER_EXPLICIT (parser)->line_offset
+ = linespec_parse_variable (PARSER_STATE (parser), name.get ());
+
+ if (PARSER_EXPLICIT (parser)->line_offset.sign == LINE_OFFSET_UNKNOWN)
+ {
+ /* The user-specified variable was not valid. Do not
+ throw an error here. parse_linespec will do it for us. */
+ PARSER_EXPLICIT (parser)->function_name = name.release ();
+ return;
+ }
+ }
+ else
+ {
+ /* The name is also not a label. Abort parsing. Do not throw
+ an error here. parse_linespec will do it for us. */
+
+ /* Save a copy of the name we were trying to lookup. */
+ PARSER_EXPLICIT (parser)->function_name = name.release ();
+ return;
+ }
+ }
+ }
+
+ int previous_qc = parser->completion_quote_char;
+
+ /* Get the next token. */
+ token = linespec_lexer_consume_token (parser);
+
+ if (token.type == LSTOKEN_EOI)
+ {
+ if (previous_qc && !parser->completion_quote_char)
+ parser->complete_what = linespec_complete_what::KEYWORD;
+ }
+ else if (token.type == LSTOKEN_COLON)
+ {
+ /* User specified a label or a lineno. */
+ token = linespec_lexer_consume_token (parser);
+
+ if (token.type == LSTOKEN_NUMBER)
+ {
+ /* User specified an offset. Record the line offset and
+ get the next token. */
+ set_completion_after_number (parser, linespec_complete_what::KEYWORD);
+
+ name = copy_token_string (token);
+ PARSER_EXPLICIT (parser)->line_offset
+ = linespec_parse_line_offset (name.get ());
+
+ /* Get the next token. */
+ token = linespec_lexer_consume_token (parser);
+ }
+ else if (token.type == LSTOKEN_EOI && parser->completion_tracker != NULL)
+ {
+ parser->complete_what = linespec_complete_what::LABEL;
+ }
+ else if (token.type == LSTOKEN_STRING)
+ {
+ parser->complete_what = linespec_complete_what::LABEL;
+
+ /* If we have text after the label separated by whitespace
+ (e.g., "b func():lab i<tab>"), don't consider it part of
+ the label. In completion mode that should complete to
+ "if", in normal mode, the 'i' should be treated as
+ garbage. */
+ if (parser->completion_quote_char == '\0')
+ {
+ const char *ptr = LS_TOKEN_STOKEN (token).ptr;
+ for (size_t i = 0; i < LS_TOKEN_STOKEN (token).length; i++)
+ {
+ if (ptr[i] == ' ')
+ {
+ LS_TOKEN_STOKEN (token).length = i;
+ PARSER_STREAM (parser) = skip_spaces (ptr + i + 1);
+ break;
+ }
+ }
+ }
+
+ if (parser->completion_tracker != NULL)
+ {
+ if (PARSER_STREAM (parser)[-1] == ' ')
+ {
+ parser->completion_word = PARSER_STREAM (parser);
+ parser->complete_what = linespec_complete_what::KEYWORD;
+ }
+ }
+ else
+ {
+ /* Grab a copy of the label's name and look it up. */
+ name = copy_token_string (token);
+ labels
+ = find_label_symbols (PARSER_STATE (parser),
+ PARSER_RESULT (parser)->function_symbols,
+ &symbols, name.get ());
+
+ if (labels != NULL)
+ {
+ PARSER_RESULT (parser)->labels.label_symbols = labels;
+ PARSER_RESULT (parser)->labels.function_symbols
+ = new std::vector<block_symbol> (std::move (symbols));
+ PARSER_EXPLICIT (parser)->label_name = name.release ();
+ }
+ else
+ {
+ /* We don't know what it was, but it isn't a label. */
+ undefined_label_error
+ (PARSER_EXPLICIT (parser)->function_name, name.get ());
+ }
+
+ }
+
+ /* Check for a line offset. */
+ token = save_stream_and_consume_token (parser);
+ if (token.type == LSTOKEN_COLON)
+ {
+ /* Get the next token. */
+ token = linespec_lexer_consume_token (parser);
+
+ /* It must be a line offset. */
+ if (token.type != LSTOKEN_NUMBER)
+ unexpected_linespec_error (parser);
+
+ /* Record the line offset and get the next token. */
+ name = copy_token_string (token);
+
+ PARSER_EXPLICIT (parser)->line_offset
+ = linespec_parse_line_offset (name.get ());
+
+ /* Get the next token. */
+ token = linespec_lexer_consume_token (parser);
+ }
+ }
+ else
+ {
+ /* Trailing ':' in the input. Issue an error. */
+ unexpected_linespec_error (parser);
+ }
+ }
+}
+
+/* Canonicalize the linespec contained in LS. The result is saved into
+ STATE->canonical. This function handles both linespec and explicit
+ locations. */
+
+static void
+canonicalize_linespec (struct linespec_state *state, const linespec_p ls)
+{
+ struct event_location *canon;
+ struct explicit_location *explicit_loc;
+
+ /* If canonicalization was not requested, no need to do anything. */
+ if (!state->canonical)
+ return;
+
+ /* Save everything as an explicit location. */
+ state->canonical->location
+ = new_explicit_location (&ls->explicit_loc);
+ canon = state->canonical->location.get ();
+ explicit_loc = get_explicit_location (canon);
+
+ if (explicit_loc->label_name != NULL)
+ {
+ state->canonical->special_display = 1;
+
+ if (explicit_loc->function_name == NULL)
+ {
+ /* No function was specified, so add the symbol name. */
+ gdb_assert (!ls->labels.function_symbols->empty ()
+ && (ls->labels.function_symbols->size () == 1));
+ block_symbol s = ls->labels.function_symbols->front ();
+ explicit_loc->function_name
+ = xstrdup (SYMBOL_NATURAL_NAME (s.symbol));
+ }
+ }
+
+ /* If this location originally came from a linespec, save a string
+ representation of it for display and saving to file. */
+ if (state->is_linespec)
+ {
+ char *linespec = explicit_location_to_linespec (explicit_loc);
+
+ set_event_location_string (canon, linespec);
+ xfree (linespec);
+ }
+}
+
+/* Given a line offset in LS, construct the relevant SALs. */
+
+static std::vector<symtab_and_line>
+create_sals_line_offset (struct linespec_state *self,
+ linespec_p ls)
+{
+ int use_default = 0;
+
+ /* This is where we need to make sure we have good defaults.
+ We must guarantee that this section of code is never executed
+ when we are called with just a function name, since
+ set_default_source_symtab_and_line uses
+ select_source_symtab that calls us with such an argument. */
+
+ if (ls->file_symtabs->size () == 1
+ && ls->file_symtabs->front () == nullptr)
+ {
+ const char *fullname;
+
+ set_current_program_space (self->program_space);
+
+ /* Make sure we have at least a default source line. */
+ set_default_source_symtab_and_line ();
+ initialize_defaults (&self->default_symtab, &self->default_line);
+ fullname = symtab_to_fullname (self->default_symtab);
+ *ls->file_symtabs
+ = collect_symtabs_from_filename (fullname, self->search_pspace);
+ use_default = 1;
+ }
+
+ symtab_and_line val;
+ val.line = ls->explicit_loc.line_offset.offset;
+ switch (ls->explicit_loc.line_offset.sign)
+ {
+ case LINE_OFFSET_PLUS:
+ if (ls->explicit_loc.line_offset.offset == 0)
+ val.line = 5;
+ if (use_default)
+ val.line = self->default_line + val.line;
+ break;
+
+ case LINE_OFFSET_MINUS:
+ if (ls->explicit_loc.line_offset.offset == 0)
+ val.line = 15;
+ if (use_default)
+ val.line = self->default_line - val.line;
+ else
+ val.line = -val.line;
+ break;
+
+ case LINE_OFFSET_NONE:
+ break; /* No need to adjust val.line. */
+ }
+
+ std::vector<symtab_and_line> values;
+ if (self->list_mode)
+ values = decode_digits_list_mode (self, ls, val);
+ else
+ {
+ struct linetable_entry *best_entry = NULL;
+ int i, j;
+
+ std::vector<symtab_and_line> intermediate_results
+ = decode_digits_ordinary (self, ls, val.line, &best_entry);
+ if (intermediate_results.empty () && best_entry != NULL)
+ intermediate_results = decode_digits_ordinary (self, ls,
+ best_entry->line,
+ &best_entry);
+
+ /* For optimized code, the compiler can scatter one source line
+ across disjoint ranges of PC values, even when no duplicate
+ functions or inline functions are involved. For example,
+ 'for (;;)' inside a non-template, non-inline, and non-ctor-or-dtor
+ function can result in two PC ranges. In this case, we don't
+ want to set a breakpoint on the first PC of each range. To filter
+ such cases, we use containing blocks -- for each PC found
+ above, we see if there are other PCs that are in the same
+ block. If yes, the other PCs are filtered out. */