+ return parser->lexer.current;
+ }
+
+ /* Handle other tokens. */
+ switch (*PARSER_STREAM (parser))
+ {
+ case 0:
+ parser->lexer.current.type = LSTOKEN_EOI;
+ break;
+
+ case '+': case '-':
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ if (!linespec_lexer_lex_number (parser, &(parser->lexer.current)))
+ parser->lexer.current = linespec_lexer_lex_string (parser);
+ break;
+
+ case ':':
+ /* If we have a scope operator, lex the input as a string.
+ Otherwise, return LSTOKEN_COLON. */
+ if (PARSER_STREAM (parser)[1] == ':')
+ parser->lexer.current = linespec_lexer_lex_string (parser);
+ else
+ {
+ parser->lexer.current.type = LSTOKEN_COLON;
+ ++(PARSER_STREAM (parser));
+ }
+ break;
+
+ case '\'': case '\"':
+ /* Special case: permit quote-enclosed linespecs. */
+ if (parser->is_quote_enclosed
+ && is_closing_quote_enclosed (PARSER_STREAM (parser)))
+ {
+ ++(PARSER_STREAM (parser));
+ parser->lexer.current.type = LSTOKEN_EOI;
+ }
+ else
+ parser->lexer.current = linespec_lexer_lex_string (parser);
+ break;
+
+ case ',':
+ parser->lexer.current.type = LSTOKEN_COMMA;
+ LS_TOKEN_STOKEN (parser->lexer.current).ptr
+ = PARSER_STREAM (parser);
+ LS_TOKEN_STOKEN (parser->lexer.current).length = 1;
+ ++(PARSER_STREAM (parser));
+ break;
+
+ default:
+ /* If the input is not a number, it must be a string.
+ [Keywords were already considered above.] */
+ parser->lexer.current = linespec_lexer_lex_string (parser);
+ break;
+ }
+ }
+
+ return parser->lexer.current;
+}
+
+/* Consume the current token and return the next token in PARSER's
+ input stream. Also advance the completion word for completion
+ mode. */
+
+static linespec_token
+linespec_lexer_consume_token (linespec_parser *parser)
+{
+ gdb_assert (parser->lexer.current.type != LSTOKEN_EOI);
+
+ bool advance_word = (parser->lexer.current.type != LSTOKEN_STRING
+ || *PARSER_STREAM (parser) != '\0');
+
+ /* If we're moving past a string to some other token, it must be the
+ quote was terminated. */
+ if (parser->completion_quote_char)
+ {
+ gdb_assert (parser->lexer.current.type == LSTOKEN_STRING);
+
+ /* If the string was the last (non-EOI) token, we're past the
+ quote, but remember that for later. */
+ if (*PARSER_STREAM (parser) != '\0')
+ {
+ parser->completion_quote_char = '\0';
+ parser->completion_quote_end = NULL;;
+ }
+ }
+
+ parser->lexer.current.type = LSTOKEN_CONSUMED;
+ linespec_lexer_lex_one (parser);
+
+ if (parser->lexer.current.type == LSTOKEN_STRING)
+ {
+ /* Advance the completion word past a potential initial
+ quote-char. */
+ parser->completion_word = LS_TOKEN_STOKEN (parser->lexer.current).ptr;
+ }
+ else if (advance_word)
+ {
+ /* Advance the completion word past any whitespace. */
+ parser->completion_word = PARSER_STREAM (parser);
+ }
+
+ return parser->lexer.current;
+}
+
+/* Return the next token without consuming the current token. */
+
+static linespec_token
+linespec_lexer_peek_token (linespec_parser *parser)
+{
+ linespec_token next;
+ const char *saved_stream = PARSER_STREAM (parser);
+ linespec_token saved_token = parser->lexer.current;
+ int saved_completion_quote_char = parser->completion_quote_char;
+ const char *saved_completion_quote_end = parser->completion_quote_end;
+ const char *saved_completion_word = parser->completion_word;
+
+ next = linespec_lexer_consume_token (parser);
+ PARSER_STREAM (parser) = saved_stream;
+ parser->lexer.current = saved_token;
+ parser->completion_quote_char = saved_completion_quote_char;
+ parser->completion_quote_end = saved_completion_quote_end;
+ parser->completion_word = saved_completion_word;
+ return next;
+}
+
+/* Helper functions. */
+
+/* Add SAL to SALS, and also update SELF->CANONICAL_NAMES to reflect
+ the new sal, if needed. If not NULL, SYMNAME is the name of the
+ symbol to use when constructing the new canonical name.
+
+ If LITERAL_CANONICAL is non-zero, SYMNAME will be used as the
+ canonical name for the SAL. */
+
+static void
+add_sal_to_sals (struct linespec_state *self,
+ std::vector<symtab_and_line> *sals,
+ struct symtab_and_line *sal,
+ const char *symname, int literal_canonical)
+{
+ sals->push_back (*sal);
+
+ if (self->canonical)
+ {
+ struct linespec_canonical_name *canonical;
+
+ self->canonical_names = XRESIZEVEC (struct linespec_canonical_name,
+ self->canonical_names,
+ sals->size ());
+ canonical = &self->canonical_names[sals->size () - 1];
+ if (!literal_canonical && sal->symtab)
+ {
+ symtab_to_fullname (sal->symtab);
+
+ /* Note that the filter doesn't have to be a valid linespec
+ input. We only apply the ":LINE" treatment to Ada for
+ the time being. */
+ if (symname != NULL && sal->line != 0
+ && self->language->la_language == language_ada)
+ canonical->suffix = xstrprintf ("%s:%d", symname, sal->line);
+ else if (symname != NULL)
+ canonical->suffix = xstrdup (symname);
+ else
+ canonical->suffix = xstrprintf ("%d", sal->line);
+ canonical->symtab = sal->symtab;
+ }
+ else
+ {
+ if (symname != NULL)
+ canonical->suffix = xstrdup (symname);
+ else
+ canonical->suffix = xstrdup ("<unknown>");
+ canonical->symtab = NULL;
+ }
+ }
+}
+
+/* A hash function for address_entry. */
+
+static hashval_t
+hash_address_entry (const void *p)
+{
+ const struct address_entry *aep = (const struct address_entry *) p;
+ hashval_t hash;
+
+ hash = iterative_hash_object (aep->pspace, 0);
+ return iterative_hash_object (aep->addr, hash);
+}
+
+/* An equality function for address_entry. */
+
+static int
+eq_address_entry (const void *a, const void *b)
+{
+ const struct address_entry *aea = (const struct address_entry *) a;
+ const struct address_entry *aeb = (const struct address_entry *) b;
+
+ return aea->pspace == aeb->pspace && aea->addr == aeb->addr;
+}
+
+/* Check whether the address, represented by PSPACE and ADDR, is
+ already in the set. If so, return 0. Otherwise, add it and return
+ 1. */
+
+static int
+maybe_add_address (htab_t set, struct program_space *pspace, CORE_ADDR addr)
+{
+ struct address_entry e, *p;
+ void **slot;
+
+ e.pspace = pspace;
+ e.addr = addr;
+ slot = htab_find_slot (set, &e, INSERT);
+ if (*slot)
+ return 0;
+
+ p = XNEW (struct address_entry);
+ memcpy (p, &e, sizeof (struct address_entry));
+ *slot = p;
+
+ return 1;
+}
+
+/* A helper that walks over all matching symtabs in all objfiles and
+ calls CALLBACK for each symbol matching NAME. If SEARCH_PSPACE is
+ not NULL, then the search is restricted to just that program
+ space. If INCLUDE_INLINE is true then symbols representing
+ inlined instances of functions will be included in the result. */
+
+static void
+iterate_over_all_matching_symtabs
+ (struct linespec_state *state,
+ const lookup_name_info &lookup_name,
+ const domain_enum name_domain,
+ enum search_domain search_domain,
+ struct program_space *search_pspace, bool include_inline,
+ gdb::function_view<symbol_found_callback_ftype> callback)
+{
+ struct program_space *pspace;
+
+ ALL_PSPACES (pspace)
+ {
+ if (search_pspace != NULL && search_pspace != pspace)
+ continue;
+ if (pspace->executing_startup)
+ continue;
+
+ set_current_program_space (pspace);
+
+ for (objfile *objfile : all_objfiles (current_program_space))
+ {
+ if (objfile->sf)
+ objfile->sf->qf->expand_symtabs_matching (objfile,
+ NULL,
+ lookup_name,
+ NULL, NULL,
+ search_domain);
+
+ for (compunit_symtab *cu : objfile_compunits (objfile))
+ {
+ struct symtab *symtab = COMPUNIT_FILETABS (cu);
+
+ iterate_over_file_blocks (symtab, lookup_name, name_domain,
+ callback);
+
+ if (include_inline)
+ {
+ struct block *block;
+ int i;
+
+ for (i = FIRST_LOCAL_BLOCK;
+ i < BLOCKVECTOR_NBLOCKS (SYMTAB_BLOCKVECTOR (symtab));
+ i++)
+ {
+ block = BLOCKVECTOR_BLOCK (SYMTAB_BLOCKVECTOR (symtab), i);
+ state->language->la_iterate_over_symbols
+ (block, lookup_name, name_domain,
+ [&] (block_symbol *bsym)
+ {
+ /* Restrict calls to CALLBACK to symbols
+ representing inline symbols only. */
+ if (SYMBOL_INLINED (bsym->symbol))
+ return callback (bsym);
+ return true;
+ });
+ }
+ }
+ }
+ }
+ }
+}
+
+/* Returns the block to be used for symbol searches from
+ the current location. */
+
+static const struct block *
+get_current_search_block (void)
+{
+ const struct block *block;
+ enum language save_language;
+
+ /* get_selected_block can change the current language when there is
+ no selected frame yet. */
+ save_language = current_language->la_language;
+ block = get_selected_block (0);
+ set_language (save_language);
+
+ return block;
+}
+
+/* Iterate over static and global blocks. */
+
+static void
+iterate_over_file_blocks
+ (struct symtab *symtab, const lookup_name_info &name,
+ domain_enum domain, gdb::function_view<symbol_found_callback_ftype> callback)
+{
+ struct block *block;
+
+ for (block = BLOCKVECTOR_BLOCK (SYMTAB_BLOCKVECTOR (symtab), STATIC_BLOCK);
+ block != NULL;
+ block = BLOCK_SUPERBLOCK (block))
+ LA_ITERATE_OVER_SYMBOLS (block, name, domain, callback);
+}
+
+/* A helper for find_method. This finds all methods in type T of
+ language T_LANG which match NAME. It adds matching symbol names to
+ RESULT_NAMES, and adds T's direct superclasses to SUPERCLASSES. */
+
+static void
+find_methods (struct type *t, enum language t_lang, const char *name,
+ std::vector<const char *> *result_names,
+ std::vector<struct type *> *superclasses)
+{
+ int ibase;
+ const char *class_name = TYPE_NAME (t);
+
+ /* Ignore this class if it doesn't have a name. This is ugly, but
+ unless we figure out how to get the physname without the name of
+ the class, then the loop can't do any good. */
+ if (class_name)
+ {
+ int method_counter;
+ lookup_name_info lookup_name (name, symbol_name_match_type::FULL);
+ symbol_name_matcher_ftype *symbol_name_compare
+ = get_symbol_name_matcher (language_def (t_lang), lookup_name);
+
+ t = check_typedef (t);
+
+ /* Loop over each method name. At this level, all overloads of a name
+ are counted as a single name. There is an inner loop which loops over
+ each overload. */
+
+ for (method_counter = TYPE_NFN_FIELDS (t) - 1;
+ method_counter >= 0;
+ --method_counter)
+ {
+ const char *method_name = TYPE_FN_FIELDLIST_NAME (t, method_counter);
+
+ if (symbol_name_compare (method_name, lookup_name, NULL))
+ {
+ int field_counter;
+
+ for (field_counter = (TYPE_FN_FIELDLIST_LENGTH (t, method_counter)
+ - 1);
+ field_counter >= 0;
+ --field_counter)
+ {
+ struct fn_field *f;
+ const char *phys_name;
+
+ f = TYPE_FN_FIELDLIST1 (t, method_counter);
+ if (TYPE_FN_FIELD_STUB (f, field_counter))
+ continue;
+ phys_name = TYPE_FN_FIELD_PHYSNAME (f, field_counter);
+ result_names->push_back (phys_name);
+ }
+ }
+ }
+ }
+
+ for (ibase = 0; ibase < TYPE_N_BASECLASSES (t); ibase++)
+ superclasses->push_back (TYPE_BASECLASS (t, ibase));
+}
+
+/* Find an instance of the character C in the string S that is outside
+ of all parenthesis pairs, single-quoted strings, and double-quoted
+ strings. Also, ignore the char within a template name, like a ','
+ within foo<int, int>, while considering C++ operator</operator<<. */
+
+const char *
+find_toplevel_char (const char *s, char c)
+{
+ int quoted = 0; /* zero if we're not in quotes;
+ '"' if we're in a double-quoted string;
+ '\'' if we're in a single-quoted string. */
+ int depth = 0; /* Number of unclosed parens we've seen. */
+ const char *scan;
+
+ for (scan = s; *scan; scan++)
+ {
+ if (quoted)
+ {
+ if (*scan == quoted)
+ quoted = 0;
+ else if (*scan == '\\' && *(scan + 1))
+ scan++;
+ }
+ else if (*scan == c && ! quoted && depth == 0)
+ return scan;
+ else if (*scan == '"' || *scan == '\'')
+ quoted = *scan;
+ else if (*scan == '(' || *scan == '<')
+ depth++;
+ else if ((*scan == ')' || *scan == '>') && depth > 0)
+ depth--;
+ else if (*scan == 'o' && !quoted && depth == 0)
+ {
+ /* Handle C++ operator names. */
+ if (strncmp (scan, CP_OPERATOR_STR, CP_OPERATOR_LEN) == 0)
+ {
+ scan += CP_OPERATOR_LEN;
+ if (*scan == c)
+ return scan;
+ while (isspace (*scan))
+ {
+ ++scan;
+ if (*scan == c)
+ return scan;
+ }
+ if (*scan == '\0')
+ break;
+
+ switch (*scan)
+ {
+ /* Skip over one less than the appropriate number of
+ characters: the for loop will skip over the last
+ one. */
+ case '<':
+ if (scan[1] == '<')
+ {
+ scan++;
+ if (*scan == c)
+ return scan;
+ }
+ break;
+ case '>':
+ if (scan[1] == '>')
+ {
+ scan++;
+ if (*scan == c)
+ return scan;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* The string equivalent of find_toplevel_char. Returns a pointer
+ to the location of NEEDLE in HAYSTACK, ignoring any occurrences
+ inside "()" and "<>". Returns NULL if NEEDLE was not found. */
+
+static const char *
+find_toplevel_string (const char *haystack, const char *needle)
+{
+ const char *s = haystack;
+
+ do
+ {
+ s = find_toplevel_char (s, *needle);
+
+ if (s != NULL)
+ {
+ /* Found first char in HAYSTACK; check rest of string. */
+ if (startswith (s, needle))
+ return s;
+
+ /* Didn't find it; loop over HAYSTACK, looking for the next
+ instance of the first character of NEEDLE. */
+ ++s;
+ }
+ }
+ while (s != NULL && *s != '\0');
+
+ /* NEEDLE was not found in HAYSTACK. */
+ return NULL;
+}
+
+/* Convert CANONICAL to its string representation using
+ symtab_to_fullname for SYMTAB. */
+
+static std::string
+canonical_to_fullform (const struct linespec_canonical_name *canonical)
+{
+ if (canonical->symtab == NULL)
+ return canonical->suffix;
+ else
+ return string_printf ("%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. */
+
+static void
+filter_results (struct linespec_state *self,
+ std::vector<symtab_and_line> *result,
+ const std::vector<const char *> &filters)
+{
+ for (const char *name : filters)
+ {
+ 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. */
+
+ gdb::def_vector<int> filter (intermediate_results.size ());
+ gdb::def_vector<const block *> blocks (intermediate_results.size ());
+
+ for (i = 0; i < intermediate_results.size (); ++i)
+ {
+ set_current_program_space (intermediate_results[i].pspace);
+
+ filter[i] = 1;
+ blocks[i] = block_for_pc_sect (intermediate_results[i].pc,
+ intermediate_results[i].section);
+ }
+
+ for (i = 0; i < intermediate_results.size (); ++i)
+ {
+ if (blocks[i] != NULL)
+ for (j = i + 1; j < intermediate_results.size (); ++j)
+ {
+ if (blocks[j] == blocks[i])
+ {
+ filter[j] = 0;
+ break;
+ }
+ }
+ }
+
+ for (i = 0; i < intermediate_results.size (); ++i)
+ if (filter[i])
+ {
+ struct symbol *sym = (blocks[i]
+ ? block_containing_function (blocks[i])
+ : NULL);
+
+ if (self->funfirstline)
+ skip_prologue_sal (&intermediate_results[i]);
+ intermediate_results[i].symbol = sym;
+ add_sal_to_sals (self, &values, &intermediate_results[i],
+ sym ? SYMBOL_NATURAL_NAME (sym) : NULL, 0);
+ }
+ }
+
+ if (values.empty ())
+ {
+ if (ls->explicit_loc.source_filename)
+ throw_error (NOT_FOUND_ERROR, _("No line %d in file \"%s\"."),
+ val.line, ls->explicit_loc.source_filename);
+ else
+ throw_error (NOT_FOUND_ERROR, _("No line %d in the current file."),
+ val.line);
+ }
+
+ return values;
+}
+
+/* Convert the given ADDRESS into SaLs. */
+
+static std::vector<symtab_and_line>
+convert_address_location_to_sals (struct linespec_state *self,
+ CORE_ADDR address)
+{
+ symtab_and_line sal = find_pc_line (address, 0);
+ sal.pc = address;
+ sal.section = find_pc_overlay (address);
+ sal.explicit_pc = 1;
+ sal.symbol = find_pc_sect_containing_function (sal.pc, sal.section);
+
+ std::vector<symtab_and_line> sals;
+ add_sal_to_sals (self, &sals, &sal, core_addr_to_string (address), 1);
+
+ return sals;
+}
+
+/* Create and return SALs from the linespec LS. */
+
+static std::vector<symtab_and_line>
+convert_linespec_to_sals (struct linespec_state *state, linespec_p ls)
+{
+ std::vector<symtab_and_line> sals;
+
+ if (ls->labels.label_symbols != NULL)
+ {
+ /* We have just a bunch of functions/methods or labels. */
+ struct symtab_and_line sal;
+
+ for (const auto &sym : *ls->labels.label_symbols)
+ {
+ struct program_space *pspace
+ = SYMTAB_PSPACE (symbol_symtab (sym.symbol));
+
+ if (symbol_to_sal (&sal, state->funfirstline, sym.symbol)
+ && maybe_add_address (state->addr_set, pspace, sal.pc))
+ add_sal_to_sals (state, &sals, &sal,
+ SYMBOL_NATURAL_NAME (sym.symbol), 0);
+ }
+ }
+ else if (ls->function_symbols != NULL || ls->minimal_symbols != NULL)
+ {
+ /* We have just a bunch of functions and/or methods. */
+ if (ls->function_symbols != NULL)
+ {
+ /* Sort symbols so that symbols with the same program space are next
+ to each other. */
+ std::sort (ls->function_symbols->begin (),
+ ls->function_symbols->end (),
+ compare_symbols);
+
+ for (const auto &sym : *ls->function_symbols)
+ {
+ program_space *pspace
+ = SYMTAB_PSPACE (symbol_symtab (sym.symbol));
+ set_current_program_space (pspace);
+
+ /* Don't skip to the first line of the function if we
+ had found an ifunc minimal symbol for this function,
+ because that means that this function is an ifunc
+ resolver with the same name as the ifunc itself. */
+ bool found_ifunc = false;
+
+ if (state->funfirstline
+ && ls->minimal_symbols != NULL
+ && SYMBOL_CLASS (sym.symbol) == LOC_BLOCK)
+ {
+ const CORE_ADDR addr
+ = BLOCK_ENTRY_PC (SYMBOL_BLOCK_VALUE (sym.symbol));
+
+ for (const auto &elem : *ls->minimal_symbols)
+ {
+ if (MSYMBOL_TYPE (elem.minsym) == mst_text_gnu_ifunc
+ || MSYMBOL_TYPE (elem.minsym) == mst_data_gnu_ifunc)
+ {
+ CORE_ADDR msym_addr = BMSYMBOL_VALUE_ADDRESS (elem);
+ if (MSYMBOL_TYPE (elem.minsym) == mst_data_gnu_ifunc)
+ {
+ struct gdbarch *gdbarch
+ = get_objfile_arch (elem.objfile);
+ msym_addr
+ = (gdbarch_convert_from_func_ptr_addr
+ (gdbarch,
+ msym_addr,
+ current_top_target ()));
+ }
+
+ if (msym_addr == addr)
+ {
+ found_ifunc = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!found_ifunc)
+ {
+ symtab_and_line sal;
+ if (symbol_to_sal (&sal, state->funfirstline, sym.symbol)
+ && maybe_add_address (state->addr_set, pspace, sal.pc))
+ add_sal_to_sals (state, &sals, &sal,
+ SYMBOL_NATURAL_NAME (sym.symbol), 0);
+ }
+ }
+ }
+
+ if (ls->minimal_symbols != NULL)
+ {
+ /* Sort minimal symbols by program space, too */
+ std::sort (ls->minimal_symbols->begin (),
+ ls->minimal_symbols->end (),
+ compare_msymbols);
+
+ for (const auto &elem : *ls->minimal_symbols)
+ {
+ program_space *pspace = elem.objfile->pspace;
+ set_current_program_space (pspace);
+ minsym_found (state, elem.objfile, elem.minsym, &sals);
+ }
+ }
+ }
+ else if (ls->explicit_loc.line_offset.sign != LINE_OFFSET_UNKNOWN)
+ {
+ /* Only an offset was specified. */
+ sals = create_sals_line_offset (state, ls);
+
+ /* Make sure we have a filename for canonicalization. */
+ if (ls->explicit_loc.source_filename == NULL)
+ {
+ const char *fullname = symtab_to_fullname (state->default_symtab);
+
+ /* It may be more appropriate to keep DEFAULT_SYMTAB in its symtab
+ form so that displaying SOURCE_FILENAME can follow the current
+ FILENAME_DISPLAY_STRING setting. But as it is used only rarely
+ it has been kept for code simplicity only in absolute form. */
+ ls->explicit_loc.source_filename = xstrdup (fullname);
+ }
+ }
+ else
+ {
+ /* We haven't found any results... */
+ return sals;
+ }
+
+ canonicalize_linespec (state, ls);
+
+ if (!sals.empty () && state->canonical != NULL)
+ state->canonical->pre_expanded = 1;
+
+ return sals;
+}
+
+/* Build RESULT from the explicit location components SOURCE_FILENAME,
+ FUNCTION_NAME, LABEL_NAME and LINE_OFFSET. */
+
+static void
+convert_explicit_location_to_linespec (struct linespec_state *self,
+ linespec_p result,
+ const char *source_filename,
+ const char *function_name,
+ symbol_name_match_type fname_match_type,
+ const char *label_name,
+ struct line_offset line_offset)
+{
+ std::vector<block_symbol> symbols;
+ std::vector<block_symbol> *labels;
+ std::vector<bound_minimal_symbol> minimal_symbols;
+
+ result->explicit_loc.func_name_match_type = fname_match_type;
+
+ if (source_filename != NULL)
+ {
+ TRY
+ {
+ *result->file_symtabs
+ = symtabs_from_filename (source_filename, self->search_pspace);
+ }
+ CATCH (except, RETURN_MASK_ERROR)
+ {
+ source_file_not_found_error (source_filename);
+ }
+ END_CATCH
+ result->explicit_loc.source_filename = xstrdup (source_filename);
+ }
+ else
+ {
+ /* A NULL entry means to use the default symtab. */
+ result->file_symtabs->push_back (nullptr);
+ }
+
+ if (function_name != NULL)
+ {
+ find_linespec_symbols (self, result->file_symtabs,
+ function_name, fname_match_type,
+ &symbols, &minimal_symbols);
+
+ if (symbols.empty () && minimal_symbols.empty ())
+ symbol_not_found_error (function_name,
+ result->explicit_loc.source_filename);
+
+ result->explicit_loc.function_name = xstrdup (function_name);
+ result->function_symbols
+ = new std::vector<block_symbol> (std::move (symbols));
+ result->minimal_symbols
+ = new std::vector<bound_minimal_symbol> (std::move (minimal_symbols));
+ }
+
+ if (label_name != NULL)
+ {
+ labels = find_label_symbols (self, result->function_symbols,
+ &symbols, label_name);
+
+ if (labels == NULL)
+ undefined_label_error (result->explicit_loc.function_name,
+ label_name);
+
+ result->explicit_loc.label_name = xstrdup (label_name);
+ result->labels.label_symbols = labels;
+ result->labels.function_symbols
+ = new std::vector<block_symbol> (std::move (symbols));
+ }
+
+ if (line_offset.sign != LINE_OFFSET_UNKNOWN)
+ result->explicit_loc.line_offset = line_offset;
+}
+
+/* Convert the explicit location EXPLICIT_LOC into SaLs. */
+
+static std::vector<symtab_and_line>
+convert_explicit_location_to_sals (struct linespec_state *self,
+ linespec_p result,
+ const struct explicit_location *explicit_loc)
+{
+ convert_explicit_location_to_linespec (self, result,
+ explicit_loc->source_filename,
+ explicit_loc->function_name,
+ explicit_loc->func_name_match_type,
+ explicit_loc->label_name,
+ explicit_loc->line_offset);
+ return convert_linespec_to_sals (self, result);
+}
+
+/* Parse a string that specifies a linespec.
+
+ The basic grammar of linespecs:
+
+ linespec -> var_spec | basic_spec
+ var_spec -> '$' (STRING | NUMBER)
+
+ basic_spec -> file_offset_spec | function_spec | label_spec
+ file_offset_spec -> opt_file_spec offset_spec
+ function_spec -> opt_file_spec function_name_spec opt_label_spec
+ label_spec -> label_name_spec
+
+ opt_file_spec -> "" | file_name_spec ':'
+ opt_label_spec -> "" | ':' label_name_spec
+
+ file_name_spec -> STRING
+ function_name_spec -> STRING
+ label_name_spec -> STRING
+ function_name_spec -> STRING
+ offset_spec -> NUMBER
+ -> '+' NUMBER
+ -> '-' NUMBER
+
+ This may all be followed by several keywords such as "if EXPR",
+ which we ignore.
+
+ A comma will terminate parsing.
+
+ The function may be an undebuggable function found in minimal symbol table.
+
+ If the argument FUNFIRSTLINE is nonzero, we want the first line
+ of real code inside a function when a function is specified, and it is
+ not OK to specify a variable or type to get its line number.
+
+ DEFAULT_SYMTAB specifies the file to use if none is specified.
+ It defaults to current_source_symtab.
+ DEFAULT_LINE specifies the line number to use for relative
+ line numbers (that start with signs). Defaults to current_source_line.
+ If CANONICAL is non-NULL, store an array of strings containing the canonical
+ line specs there if necessary. Currently overloaded member functions and
+ line numbers or static functions without a filename yield a canonical
+ line spec. The array and the line spec strings are allocated on the heap,
+ it is the callers responsibility to free them.
+
+ Note that it is possible to return zero for the symtab
+ if no file is validly specified. Callers must check that.
+ Also, the line number returned may be invalid. */
+
+/* Parse the linespec in ARG. MATCH_TYPE indicates how function names
+ should be matched. */
+
+static std::vector<symtab_and_line>
+parse_linespec (linespec_parser *parser, const char *arg,
+ symbol_name_match_type match_type)
+{
+ linespec_token token;
+ struct gdb_exception file_exception = exception_none;
+
+ /* A special case to start. It has become quite popular for
+ IDEs to work around bugs in the previous parser by quoting
+ the entire linespec, so we attempt to deal with this nicely. */
+ parser->is_quote_enclosed = 0;
+ if (parser->completion_tracker == NULL
+ && !is_ada_operator (arg)
+ && strchr (linespec_quote_characters, *arg) != NULL)
+ {
+ const char *end;
+
+ end = skip_quote_char (arg + 1, *arg);
+ if (end != NULL && is_closing_quote_enclosed (end))
+ {
+ /* Here's the special case. Skip ARG past the initial
+ quote. */
+ ++arg;
+ parser->is_quote_enclosed = 1;
+ }
+ }
+
+ parser->lexer.saved_arg = arg;
+ parser->lexer.stream = arg;
+ parser->completion_word = arg;
+ parser->complete_what = linespec_complete_what::FUNCTION;
+ PARSER_EXPLICIT (parser)->func_name_match_type = match_type;
+
+ /* Initialize the default symtab and line offset. */
+ initialize_defaults (&PARSER_STATE (parser)->default_symtab,
+ &PARSER_STATE (parser)->default_line);
+
+ /* Objective-C shortcut. */
+ if (parser->completion_tracker == NULL)
+ {
+ std::vector<symtab_and_line> values
+ = decode_objc (PARSER_STATE (parser), PARSER_RESULT (parser), arg);
+ if (!values.empty ())
+ return values;
+ }
+ else
+ {
+ /* "-"/"+" is either an objc selector, or a number. There's
+ nothing to complete the latter to, so just let the caller
+ complete on functions, which finds objc selectors, if there's
+ any. */
+ if ((arg[0] == '-' || arg[0] == '+') && arg[1] == '\0')
+ return {};
+ }
+
+ /* Start parsing. */
+
+ /* Get the first token. */
+ token = linespec_lexer_consume_token (parser);
+
+ /* It must be either LSTOKEN_STRING or LSTOKEN_NUMBER. */
+ if (token.type == LSTOKEN_STRING && *LS_TOKEN_STOKEN (token).ptr == '$')
+ {
+ /* A NULL entry means to use GLOBAL_DEFAULT_SYMTAB. */
+ if (parser->completion_tracker == NULL)
+ PARSER_RESULT (parser)->file_symtabs->push_back (nullptr);
+
+ /* User specified a convenience variable or history value. */
+ gdb::unique_xmalloc_ptr<char> var = copy_token_string (token);
+ PARSER_EXPLICIT (parser)->line_offset
+ = linespec_parse_variable (PARSER_STATE (parser), var.get ());
+
+ /* If a line_offset wasn't found (VAR is the name of a user
+ variable/function), then skip to normal symbol processing. */
+ if (PARSER_EXPLICIT (parser)->line_offset.sign != LINE_OFFSET_UNKNOWN)
+ {
+ /* Consume this token. */
+ linespec_lexer_consume_token (parser);
+
+ goto convert_to_sals;
+ }
+ }
+ else if (token.type == LSTOKEN_EOI && parser->completion_tracker != NULL)
+ {
+ /* Let the default linespec_complete_what::FUNCTION kick in. */
+ unexpected_linespec_error (parser);
+ }
+ else if (token.type != LSTOKEN_STRING && token.type != LSTOKEN_NUMBER)
+ {
+ parser->complete_what = linespec_complete_what::NOTHING;
+ unexpected_linespec_error (parser);
+ }
+
+ /* Shortcut: If the next token is not LSTOKEN_COLON, we know that
+ this token cannot represent a filename. */
+ token = linespec_lexer_peek_token (parser);
+
+ if (token.type == LSTOKEN_COLON)
+ {
+ /* Get the current token again and extract the filename. */
+ token = linespec_lexer_lex_one (parser);
+ gdb::unique_xmalloc_ptr<char> user_filename = copy_token_string (token);
+
+ /* Check if the input is a filename. */
+ TRY
+ {
+ *PARSER_RESULT (parser)->file_symtabs
+ = symtabs_from_filename (user_filename.get (),
+ PARSER_STATE (parser)->search_pspace);
+ }
+ CATCH (ex, RETURN_MASK_ERROR)
+ {
+ file_exception = ex;
+ }
+ END_CATCH
+
+ if (file_exception.reason >= 0)
+ {
+ /* Symtabs were found for the file. Record the filename. */
+ PARSER_EXPLICIT (parser)->source_filename = user_filename.release ();
+
+ /* Get the next token. */
+ token = linespec_lexer_consume_token (parser);
+
+ /* This is LSTOKEN_COLON; consume it. */
+ linespec_lexer_consume_token (parser);
+ }
+ else
+ {
+ /* A NULL entry means to use GLOBAL_DEFAULT_SYMTAB. */
+ PARSER_RESULT (parser)->file_symtabs->push_back (nullptr);
+ }
+ }
+ /* If the next token is not EOI, KEYWORD, or COMMA, issue an error. */
+ else if (parser->completion_tracker == NULL
+ && (token.type != LSTOKEN_EOI && token.type != LSTOKEN_KEYWORD
+ && token.type != LSTOKEN_COMMA))
+ {
+ /* TOKEN is the _next_ token, not the one currently in the parser.
+ Consuming the token will give the correct error message. */
+ linespec_lexer_consume_token (parser);
+ unexpected_linespec_error (parser);
+ }
+ else
+ {
+ /* A NULL entry means to use GLOBAL_DEFAULT_SYMTAB. */
+ PARSER_RESULT (parser)->file_symtabs->push_back (nullptr);
+ }
+
+ /* Parse the rest of the linespec. */
+ linespec_parse_basic (parser);
+
+ if (parser->completion_tracker == NULL
+ && PARSER_RESULT (parser)->function_symbols == NULL
+ && PARSER_RESULT (parser)->labels.label_symbols == NULL
+ && PARSER_EXPLICIT (parser)->line_offset.sign == LINE_OFFSET_UNKNOWN
+ && PARSER_RESULT (parser)->minimal_symbols == NULL)
+ {
+ /* The linespec didn't parse. Re-throw the file exception if
+ there was one. */
+ if (file_exception.reason < 0)
+ throw_exception (file_exception);
+
+ /* Otherwise, the symbol is not found. */
+ symbol_not_found_error (PARSER_EXPLICIT (parser)->function_name,
+ PARSER_EXPLICIT (parser)->source_filename);
+ }
+
+ convert_to_sals:
+
+ /* Get the last token and record how much of the input was parsed,
+ if necessary. */
+ token = linespec_lexer_lex_one (parser);
+ if (token.type != LSTOKEN_EOI && token.type != LSTOKEN_KEYWORD)
+ unexpected_linespec_error (parser);
+ else if (token.type == LSTOKEN_KEYWORD)
+ {
+ /* Setup the completion word past the keyword. Lexing never
+ advances past a keyword automatically, so skip it
+ manually. */
+ parser->completion_word
+ = skip_spaces (skip_to_space (PARSER_STREAM (parser)));
+ parser->complete_what = linespec_complete_what::EXPRESSION;
+ }
+
+ /* Convert the data in PARSER_RESULT to SALs. */
+ if (parser->completion_tracker == NULL)
+ return convert_linespec_to_sals (PARSER_STATE (parser),
+ PARSER_RESULT (parser));
+
+ return {};
+}
+
+
+/* A constructor for linespec_state. */
+
+static void
+linespec_state_constructor (struct linespec_state *self,
+ int flags, const struct language_defn *language,
+ struct program_space *search_pspace,
+ struct symtab *default_symtab,
+ int default_line,
+ struct linespec_result *canonical)
+{
+ memset (self, 0, sizeof (*self));
+ self->language = language;
+ self->funfirstline = (flags & DECODE_LINE_FUNFIRSTLINE) ? 1 : 0;
+ self->list_mode = (flags & DECODE_LINE_LIST_MODE) ? 1 : 0;
+ self->search_pspace = search_pspace;
+ self->default_symtab = default_symtab;
+ self->default_line = default_line;
+ self->canonical = canonical;
+ self->program_space = current_program_space;
+ self->addr_set = htab_create_alloc (10, hash_address_entry, eq_address_entry,
+ xfree, xcalloc, xfree);
+ self->is_linespec = 0;
+}
+
+/* Initialize a new linespec parser. */
+
+linespec_parser::linespec_parser (int flags,
+ const struct language_defn *language,
+ struct program_space *search_pspace,
+ struct symtab *default_symtab,
+ int default_line,
+ struct linespec_result *canonical)
+{
+ lexer.current.type = LSTOKEN_CONSUMED;
+ PARSER_RESULT (this)->file_symtabs = new std::vector<symtab *> ();
+ PARSER_EXPLICIT (this)->func_name_match_type
+ = symbol_name_match_type::WILD;
+ PARSER_EXPLICIT (this)->line_offset.sign = LINE_OFFSET_UNKNOWN;
+ linespec_state_constructor (PARSER_STATE (this), flags, language,
+ search_pspace,
+ default_symtab, default_line, canonical);
+}
+
+/* A destructor for linespec_state. */
+
+static void
+linespec_state_destructor (struct linespec_state *self)
+{
+ htab_delete (self->addr_set);
+ xfree (self->canonical_names);
+}
+
+/* Delete a linespec parser. */
+
+linespec_parser::~linespec_parser ()
+{
+ xfree (PARSER_EXPLICIT (this)->source_filename);
+ xfree (PARSER_EXPLICIT (this)->label_name);
+ xfree (PARSER_EXPLICIT (this)->function_name);
+
+ delete PARSER_RESULT (this)->file_symtabs;
+ delete PARSER_RESULT (this)->function_symbols;
+ delete PARSER_RESULT (this)->minimal_symbols;
+ delete PARSER_RESULT (this)->labels.label_symbols;
+ delete PARSER_RESULT (this)->labels.function_symbols;
+
+ linespec_state_destructor (PARSER_STATE (this));
+}
+
+/* See description in linespec.h. */
+
+void
+linespec_lex_to_end (const char **stringp)
+{
+ linespec_token token;
+ const char *orig;