| /* Data structures and API for location specs in GDB. | 
 |    Copyright (C) 2013-2022 Free Software Foundation, Inc. | 
 |  | 
 |    This file is part of GDB. | 
 |  | 
 |    This program is free software; you can redistribute it and/or modify | 
 |    it under the terms of the GNU General Public License as published by | 
 |    the Free Software Foundation; either version 3 of the License, or | 
 |    (at your option) any later version. | 
 |  | 
 |    This program is distributed in the hope that it will be useful, | 
 |    but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 |    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
 |    GNU General Public License for more details. | 
 |  | 
 |    You should have received a copy of the GNU General Public License | 
 |    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */ | 
 |  | 
 | #include "defs.h" | 
 | #include "gdbsupport/gdb_assert.h" | 
 | #include "gdbsupport/gdb-checked-static-cast.h" | 
 | #include "location.h" | 
 | #include "symtab.h" | 
 | #include "language.h" | 
 | #include "linespec.h" | 
 | #include "cli/cli-utils.h" | 
 | #include "probe.h" | 
 | #include "cp-support.h" | 
 |  | 
 | #include <ctype.h> | 
 | #include <string.h> | 
 |  | 
 | static std::string | 
 |   explicit_to_string_internal (bool as_linespec, | 
 | 			       const explicit_location_spec *explicit_loc); | 
 |  | 
 | /* Return a xstrdup of STR if not NULL, otherwise return NULL.  */ | 
 |  | 
 | static char * | 
 | maybe_xstrdup (const char *str) | 
 | { | 
 |   return (str != nullptr ? xstrdup (str) : nullptr); | 
 | } | 
 |  | 
 | probe_location_spec::probe_location_spec (std::string &&probe) | 
 |   : location_spec (PROBE_LOCATION_SPEC, std::move (probe)) | 
 | { | 
 | } | 
 |  | 
 | location_spec_up | 
 | probe_location_spec::clone () const | 
 | { | 
 |   return location_spec_up (new probe_location_spec (*this)); | 
 | } | 
 |  | 
 | bool | 
 | probe_location_spec::empty_p () const | 
 | { | 
 |   return false; | 
 | } | 
 |  | 
 | std::string probe_location_spec::compute_string () const | 
 | { | 
 |   return std::move (m_as_string); | 
 | } | 
 |  | 
 | /* A "normal" linespec.  */ | 
 | linespec_location_spec::linespec_location_spec | 
 |   (const char **linespec, symbol_name_match_type match_type_) | 
 |   : location_spec (LINESPEC_LOCATION_SPEC), | 
 |     match_type (match_type_) | 
 | { | 
 |   if (*linespec != NULL) | 
 |     { | 
 |       const char *p; | 
 |       const char *orig = *linespec; | 
 |  | 
 |       linespec_lex_to_end (linespec); | 
 |       p = remove_trailing_whitespace (orig, *linespec); | 
 |  | 
 |       /* If there is no valid linespec then this will leave the | 
 | 	 spec_string as nullptr.  This behaviour is relied on in the | 
 | 	 breakpoint setting code, where spec_string being nullptr means | 
 | 	 to use the default breakpoint location.  */ | 
 |       if ((p - orig) > 0) | 
 | 	spec_string = savestring (orig, p - orig); | 
 |     } | 
 | } | 
 |  | 
 | linespec_location_spec::~linespec_location_spec () | 
 | { | 
 |   xfree (spec_string); | 
 | } | 
 |  | 
 | location_spec_up | 
 | linespec_location_spec::clone () const | 
 | { | 
 |   return location_spec_up (new linespec_location_spec (*this)); | 
 | } | 
 |  | 
 | bool | 
 | linespec_location_spec::empty_p () const | 
 | { | 
 |   return false; | 
 | } | 
 |  | 
 | linespec_location_spec::linespec_location_spec | 
 |   (const linespec_location_spec &other) | 
 |   : location_spec (other), | 
 |     match_type (other.match_type), | 
 |     spec_string (maybe_xstrdup (other.spec_string)) | 
 | { | 
 | } | 
 |  | 
 | std::string | 
 | linespec_location_spec::compute_string () const | 
 | { | 
 |   if (spec_string != nullptr) | 
 |     { | 
 |       if (match_type == symbol_name_match_type::FULL) | 
 | 	return std::string ("-qualified ") + spec_string; | 
 |       else | 
 | 	return spec_string; | 
 |     } | 
 |   return {}; | 
 | } | 
 |  | 
 | address_location_spec::address_location_spec (CORE_ADDR addr, | 
 | 					      const char *addr_string, | 
 | 					      int addr_string_len) | 
 |   : location_spec (ADDRESS_LOCATION_SPEC), | 
 |     address (addr) | 
 | { | 
 |   if (addr_string != nullptr) | 
 |     m_as_string = std::string (addr_string, addr_string_len); | 
 | } | 
 |  | 
 | location_spec_up | 
 | address_location_spec::clone () const | 
 | { | 
 |   return location_spec_up (new address_location_spec (*this)); | 
 | } | 
 |  | 
 | bool | 
 | address_location_spec::empty_p () const | 
 | { | 
 |   return false; | 
 | } | 
 |  | 
 | address_location_spec::address_location_spec | 
 |   (const address_location_spec &other) | 
 |   : location_spec (other), | 
 |     address (other.address) | 
 | { | 
 | } | 
 |  | 
 | std::string | 
 | address_location_spec::compute_string () const | 
 | { | 
 |   const char *addr_string = core_addr_to_string (address); | 
 |   return std::string ("*") + addr_string; | 
 | } | 
 |  | 
 | explicit_location_spec::explicit_location_spec () | 
 |   : location_spec (EXPLICIT_LOCATION_SPEC) | 
 | { | 
 | } | 
 |  | 
 | explicit_location_spec::~explicit_location_spec () | 
 | { | 
 |   xfree (source_filename); | 
 |   xfree (function_name); | 
 |   xfree (label_name); | 
 | } | 
 |  | 
 | explicit_location_spec::explicit_location_spec | 
 |   (const explicit_location_spec &other) | 
 |   : location_spec (other), | 
 |     source_filename (maybe_xstrdup (other.source_filename)), | 
 |     function_name (maybe_xstrdup (other.function_name)), | 
 |     func_name_match_type (other.func_name_match_type), | 
 |     label_name (maybe_xstrdup (other.label_name)), | 
 |     line_offset (other.line_offset) | 
 | { | 
 | } | 
 |  | 
 | location_spec_up | 
 | explicit_location_spec::clone () const | 
 | { | 
 |   return location_spec_up (new explicit_location_spec (*this)); | 
 | } | 
 |  | 
 | bool | 
 | explicit_location_spec::empty_p () const | 
 | { | 
 |   return (source_filename == nullptr | 
 | 	  && function_name == nullptr | 
 | 	  && label_name == nullptr | 
 | 	  && line_offset.sign == LINE_OFFSET_UNKNOWN); | 
 | } | 
 |  | 
 | std::string | 
 | explicit_location_spec::compute_string () const | 
 | { | 
 |   return explicit_to_string_internal (false, this); | 
 | } | 
 |  | 
 | /* See description in location.h.  */ | 
 |  | 
 | location_spec_up | 
 | new_linespec_location_spec (const char **linespec, | 
 | 			    symbol_name_match_type match_type) | 
 | { | 
 |   return location_spec_up (new linespec_location_spec (linespec, | 
 | 						       match_type)); | 
 | } | 
 |  | 
 | /* See description in location.h.  */ | 
 |  | 
 | const linespec_location_spec * | 
 | as_linespec_location_spec (const location_spec *locspec) | 
 | { | 
 |   gdb_assert (locspec->type () == LINESPEC_LOCATION_SPEC); | 
 |   return gdb::checked_static_cast<const linespec_location_spec *> (locspec); | 
 | } | 
 |  | 
 | /* See description in location.h.  */ | 
 |  | 
 | location_spec_up | 
 | new_address_location_spec (CORE_ADDR addr, const char *addr_string, | 
 | 			   int addr_string_len) | 
 | { | 
 |   return location_spec_up (new address_location_spec (addr, addr_string, | 
 | 						      addr_string_len)); | 
 | } | 
 |  | 
 | /* See description in location.h.  */ | 
 |  | 
 | const address_location_spec * | 
 | as_address_location_spec (const location_spec *locspec) | 
 | { | 
 |   gdb_assert (locspec->type () == ADDRESS_LOCATION_SPEC); | 
 |   return gdb::checked_static_cast<const address_location_spec *> (locspec); | 
 | } | 
 |  | 
 | /* See description in location.h.  */ | 
 |  | 
 | location_spec_up | 
 | new_probe_location_spec (std::string &&probe) | 
 | { | 
 |   return location_spec_up (new probe_location_spec (std::move (probe))); | 
 | } | 
 |  | 
 | /* See description in location.h.  */ | 
 |  | 
 | const probe_location_spec * | 
 | as_probe_location_spec (const location_spec *locspec) | 
 | { | 
 |   gdb_assert (locspec->type () == PROBE_LOCATION_SPEC); | 
 |   return gdb::checked_static_cast<const probe_location_spec *> (locspec); | 
 | } | 
 |  | 
 | /* See description in location.h.  */ | 
 |  | 
 | const explicit_location_spec * | 
 | as_explicit_location_spec (const location_spec *locspec) | 
 | { | 
 |   gdb_assert (locspec->type () == EXPLICIT_LOCATION_SPEC); | 
 |   return gdb::checked_static_cast<const explicit_location_spec *> (locspec); | 
 | } | 
 |  | 
 | /* See description in location.h.  */ | 
 |  | 
 | explicit_location_spec * | 
 | as_explicit_location_spec (location_spec *locspec) | 
 | { | 
 |   gdb_assert (locspec->type () == EXPLICIT_LOCATION_SPEC); | 
 |   return gdb::checked_static_cast<explicit_location_spec *> (locspec); | 
 | } | 
 |  | 
 | /* Return a string representation of the explicit location spec in | 
 |    EXPLICIT_LOCSPEC. | 
 |  | 
 |    AS_LINESPEC is true if this string should be a linespec.  Otherwise | 
 |    it will be output in explicit form.  */ | 
 |  | 
 | static std::string | 
 | explicit_to_string_internal (bool as_linespec, | 
 | 			     const explicit_location_spec *explicit_loc) | 
 | { | 
 |   bool need_space = false; | 
 |   char space = as_linespec ? ':' : ' '; | 
 |   string_file buf; | 
 |  | 
 |   if (explicit_loc->source_filename != NULL) | 
 |     { | 
 |       if (!as_linespec) | 
 | 	buf.puts ("-source "); | 
 |       buf.puts (explicit_loc->source_filename); | 
 |       need_space = true; | 
 |     } | 
 |  | 
 |   if (explicit_loc->function_name != NULL) | 
 |     { | 
 |       if (need_space) | 
 | 	buf.putc (space); | 
 |       if (explicit_loc->func_name_match_type == symbol_name_match_type::FULL) | 
 | 	buf.puts ("-qualified "); | 
 |       if (!as_linespec) | 
 | 	buf.puts ("-function "); | 
 |       buf.puts (explicit_loc->function_name); | 
 |       need_space = true; | 
 |     } | 
 |  | 
 |   if (explicit_loc->label_name != NULL) | 
 |     { | 
 |       if (need_space) | 
 | 	buf.putc (space); | 
 |       if (!as_linespec) | 
 | 	buf.puts ("-label "); | 
 |       buf.puts (explicit_loc->label_name); | 
 |       need_space = true; | 
 |     } | 
 |  | 
 |   if (explicit_loc->line_offset.sign != LINE_OFFSET_UNKNOWN) | 
 |     { | 
 |       if (need_space) | 
 | 	buf.putc (space); | 
 |       if (!as_linespec) | 
 | 	buf.puts ("-line "); | 
 |       buf.printf ("%s%d", | 
 | 		  (explicit_loc->line_offset.sign == LINE_OFFSET_NONE ? "" | 
 | 		   : (explicit_loc->line_offset.sign | 
 | 		      == LINE_OFFSET_PLUS ? "+" : "-")), | 
 | 		  explicit_loc->line_offset.offset); | 
 |     } | 
 |  | 
 |   return buf.release (); | 
 | } | 
 |  | 
 | /* See description in location.h.  */ | 
 |  | 
 | std::string | 
 | explicit_location_spec::to_linespec () const | 
 | { | 
 |   return explicit_to_string_internal (true, this); | 
 | } | 
 |  | 
 | /* Find an instance of the quote character C in the string S that is | 
 |    outside of all single- and double-quoted strings (i.e., any quoting | 
 |    other than C).  */ | 
 |  | 
 | static const char * | 
 | find_end_quote (const char *s, char end_quote_char) | 
 | { | 
 |   /* zero if we're not in quotes; | 
 |      '"' if we're in a double-quoted string; | 
 |      '\'' if we're in a single-quoted string.  */ | 
 |   char nested_quote_char = '\0'; | 
 |  | 
 |   for (const char *scan = s; *scan != '\0'; scan++) | 
 |     { | 
 |       if (nested_quote_char != '\0') | 
 | 	{ | 
 | 	  if (*scan == nested_quote_char) | 
 | 	    nested_quote_char = '\0'; | 
 | 	  else if (scan[0] == '\\' && *(scan + 1) != '\0') | 
 | 	    scan++; | 
 | 	} | 
 |       else if (*scan == end_quote_char && nested_quote_char == '\0') | 
 | 	return scan; | 
 |       else if (*scan == '"' || *scan == '\'') | 
 | 	nested_quote_char = *scan; | 
 |     } | 
 |  | 
 |   return 0; | 
 | } | 
 |  | 
 | /* A lexer for explicit location specs.  This function will advance | 
 |    INP past any strings that it lexes.  Returns a malloc'd copy of the | 
 |    lexed string or NULL if no lexing was done.  */ | 
 |  | 
 | static gdb::unique_xmalloc_ptr<char> | 
 | explicit_location_spec_lex_one (const char **inp, | 
 | 				const struct language_defn *language, | 
 | 				explicit_completion_info *completion_info) | 
 | { | 
 |   const char *start = *inp; | 
 |  | 
 |   if (*start == '\0') | 
 |     return NULL; | 
 |  | 
 |   /* If quoted, skip to the ending quote.  */ | 
 |   if (strchr (get_gdb_linespec_parser_quote_characters (), *start)) | 
 |     { | 
 |       if (completion_info != NULL) | 
 | 	completion_info->quoted_arg_start = start; | 
 |  | 
 |       const char *end = find_end_quote (start + 1, *start); | 
 |  | 
 |       if (end == NULL) | 
 | 	{ | 
 | 	  if (completion_info == NULL) | 
 | 	    error (_("Unmatched quote, %s."), start); | 
 |  | 
 | 	  end = start + strlen (start); | 
 | 	  *inp = end; | 
 | 	  return gdb::unique_xmalloc_ptr<char> (savestring (start + 1, | 
 | 							    *inp - start - 1)); | 
 | 	} | 
 |  | 
 |       if (completion_info != NULL) | 
 | 	completion_info->quoted_arg_end = end; | 
 |       *inp = end + 1; | 
 |       return gdb::unique_xmalloc_ptr<char> (savestring (start + 1, | 
 | 							*inp - start - 2)); | 
 |     } | 
 |  | 
 |   /* If the input starts with '-' or '+', the string ends with the next | 
 |      whitespace or comma.  */ | 
 |   if (*start == '-' || *start == '+') | 
 |     { | 
 |       while (*inp[0] != '\0' && *inp[0] != ',' && !isspace (*inp[0])) | 
 | 	++(*inp); | 
 |     } | 
 |   else | 
 |     { | 
 |       /* Handle numbers first, stopping at the next whitespace or ','.  */ | 
 |       while (isdigit (*inp[0])) | 
 | 	++(*inp); | 
 |       if (*inp[0] == '\0' || isspace (*inp[0]) || *inp[0] == ',') | 
 | 	return gdb::unique_xmalloc_ptr<char> (savestring (start, | 
 | 							  *inp - start)); | 
 |  | 
 |       /* Otherwise stop at the next occurrence of whitespace, '\0', | 
 | 	 keyword, or ','.  */ | 
 |       *inp = start; | 
 |       while ((*inp)[0] | 
 | 	     && (*inp)[0] != ',' | 
 | 	     && !(isspace ((*inp)[0]) | 
 | 		  || linespec_lexer_lex_keyword (&(*inp)[1]))) | 
 | 	{ | 
 | 	  /* Special case: C++ operator,.  */ | 
 | 	  if (language->la_language == language_cplus | 
 | 	      && startswith (*inp, CP_OPERATOR_STR)) | 
 | 	    (*inp) += CP_OPERATOR_LEN; | 
 | 	  ++(*inp); | 
 | 	} | 
 |     } | 
 |  | 
 |   if (*inp - start > 0) | 
 |     return gdb::unique_xmalloc_ptr<char> (savestring (start, *inp - start)); | 
 |  | 
 |   return NULL; | 
 | } | 
 |  | 
 | /* Return true if COMMA points past "operator".  START is the start of | 
 |    the line that COMMAND points to, hence when reading backwards, we | 
 |    must not read any character before START.  */ | 
 |  | 
 | static bool | 
 | is_cp_operator (const char *start, const char *comma) | 
 | { | 
 |   if (comma != NULL | 
 |       && (comma - start) >= CP_OPERATOR_LEN) | 
 |     { | 
 |       const char *p = comma; | 
 |  | 
 |       while (p > start && isspace (p[-1])) | 
 | 	p--; | 
 |       if (p - start >= CP_OPERATOR_LEN) | 
 | 	{ | 
 | 	  p -= CP_OPERATOR_LEN; | 
 | 	  if (strncmp (p, CP_OPERATOR_STR, CP_OPERATOR_LEN) == 0 | 
 | 	      && (p == start | 
 | 		  || !(isalnum (p[-1]) || p[-1] == '_'))) | 
 | 	    { | 
 | 	      return true; | 
 | 	    } | 
 | 	} | 
 |     } | 
 |   return false; | 
 | } | 
 |  | 
 | /* When scanning the input string looking for the next explicit | 
 |    location spec option/delimiter, we jump to the next option by looking | 
 |    for ",", and "-".  Such a character can also appear in C++ symbols | 
 |    like "operator," and "operator-".  So when we find such a | 
 |    character, we call this function to check if we found such a | 
 |    symbol, meaning we had a false positive for an option string.  In | 
 |    that case, we keep looking for the next delimiter, until we find | 
 |    one that is not a false positive, or we reach end of string.  FOUND | 
 |    is the character that scanning found (either '-' or ','), and START | 
 |    is the start of the line that FOUND points to, hence when reading | 
 |    backwards, we must not read any character before START.  Returns a | 
 |    pointer to the next non-false-positive delimiter character, or NULL | 
 |    if none was found.  */ | 
 |  | 
 | static const char * | 
 | skip_op_false_positives (const char *start, const char *found) | 
 | { | 
 |   while (found != NULL && is_cp_operator (start, found)) | 
 |     { | 
 |       if (found[0] == '-' && found[1] == '-') | 
 | 	start = found + 2; | 
 |       else | 
 | 	start = found + 1; | 
 |       found = find_toplevel_char (start, *found); | 
 |     } | 
 |  | 
 |   return found; | 
 | } | 
 |  | 
 | /* Assuming both FIRST and NEW_TOK point into the same string, return | 
 |    the pointer that is closer to the start of the string.  If FIRST is | 
 |    NULL, returns NEW_TOK.  If NEW_TOK is NULL, returns FIRST.  */ | 
 |  | 
 | static const char * | 
 | first_of (const char *first, const char *new_tok) | 
 | { | 
 |   if (first == NULL) | 
 |     return new_tok; | 
 |   else if (new_tok != NULL && new_tok < first) | 
 |     return new_tok; | 
 |   else | 
 |     return first; | 
 | } | 
 |  | 
 | /* A lexer for functions in explicit location specs.  This function will | 
 |    advance INP past a function until the next option, or until end of | 
 |    string.  Returns a malloc'd copy of the lexed string or NULL if no | 
 |    lexing was done.  */ | 
 |  | 
 | static gdb::unique_xmalloc_ptr<char> | 
 | explicit_location_spec_lex_one_function | 
 |   (const char **inp, | 
 |    const struct language_defn *language, | 
 |    explicit_completion_info *completion_info) | 
 | { | 
 |   const char *start = *inp; | 
 |  | 
 |   if (*start == '\0') | 
 |     return NULL; | 
 |  | 
 |   /* If quoted, skip to the ending quote.  */ | 
 |   if (strchr (get_gdb_linespec_parser_quote_characters (), *start)) | 
 |     { | 
 |       char quote_char = *start; | 
 |  | 
 |       /* If the input is not an Ada operator, skip to the matching | 
 | 	 closing quote and return the string.  */ | 
 |       if (!(language->la_language == language_ada | 
 | 	    && quote_char == '\"' && is_ada_operator (start))) | 
 | 	{ | 
 | 	  if (completion_info != NULL) | 
 | 	    completion_info->quoted_arg_start = start; | 
 |  | 
 | 	  const char *end = find_toplevel_char (start + 1, quote_char); | 
 |  | 
 | 	  if (end == NULL) | 
 | 	    { | 
 | 	      if (completion_info == NULL) | 
 | 		error (_("Unmatched quote, %s."), start); | 
 |  | 
 | 	      end = start + strlen (start); | 
 | 	      *inp = end; | 
 | 	      char *saved = savestring (start + 1, *inp - start - 1); | 
 | 	      return gdb::unique_xmalloc_ptr<char> (saved); | 
 | 	    } | 
 |  | 
 | 	  if (completion_info != NULL) | 
 | 	    completion_info->quoted_arg_end = end; | 
 | 	  *inp = end + 1; | 
 | 	  char *saved = savestring (start + 1, *inp - start - 2); | 
 | 	  return gdb::unique_xmalloc_ptr<char> (saved); | 
 | 	} | 
 |     } | 
 |  | 
 |   const char *comma = find_toplevel_char (start, ','); | 
 |  | 
 |   /* If we have "-function -myfunction", or perhaps better example, | 
 |      "-function -[BasicClass doIt]" (objc selector), treat | 
 |      "-myfunction" as the function name.  I.e., skip the first char if | 
 |      it is an hyphen.  Don't skip the first char always, because we | 
 |      may have C++ "operator<", and find_toplevel_char needs to see the | 
 |      'o' in that case.  */ | 
 |   const char *hyphen | 
 |     = (*start == '-' | 
 |        ? find_toplevel_char (start + 1, '-') | 
 |        : find_toplevel_char (start, '-')); | 
 |  | 
 |   /* Check for C++ "operator," and "operator-".  */ | 
 |   comma = skip_op_false_positives (start, comma); | 
 |   hyphen = skip_op_false_positives (start, hyphen); | 
 |  | 
 |   /* Pick the one that appears first.  */ | 
 |   const char *end = first_of (hyphen, comma); | 
 |  | 
 |   /* See if a linespec keyword appears first.  */ | 
 |   const char *s = start; | 
 |   const char *ws = find_toplevel_char (start, ' '); | 
 |   while (ws != NULL && linespec_lexer_lex_keyword (ws + 1) == NULL) | 
 |     { | 
 |       s = ws + 1; | 
 |       ws = find_toplevel_char (s, ' '); | 
 |     } | 
 |   if (ws != NULL) | 
 |     end = first_of (end, ws + 1); | 
 |  | 
 |   /* If we don't have any terminator, then take the whole string.  */ | 
 |   if (end == NULL) | 
 |     end = start + strlen (start); | 
 |  | 
 |   /* Trim whitespace at the end.  */ | 
 |   while (end > start && end[-1] == ' ') | 
 |     end--; | 
 |  | 
 |   *inp = end; | 
 |  | 
 |   if (*inp - start > 0) | 
 |     return gdb::unique_xmalloc_ptr<char> (savestring (start, *inp - start)); | 
 |  | 
 |   return NULL; | 
 | } | 
 |  | 
 | /* See description in location.h.  */ | 
 |  | 
 | location_spec_up | 
 | string_to_explicit_location_spec (const char **argp, | 
 | 				  const struct language_defn *language, | 
 | 				  explicit_completion_info *completion_info) | 
 | { | 
 |   /* It is assumed that input beginning with '-' and a non-digit | 
 |      character is an explicit location.  "-p" is reserved, though, | 
 |      for probe locations.  */ | 
 |   if (argp == NULL | 
 |       || *argp == NULL | 
 |       || *argp[0] != '-' | 
 |       || !isalpha ((*argp)[1]) | 
 |       || ((*argp)[0] == '-' && (*argp)[1] == 'p')) | 
 |     return NULL; | 
 |  | 
 |   std::unique_ptr<explicit_location_spec> locspec | 
 |     (new explicit_location_spec ()); | 
 |  | 
 |   /* Process option/argument pairs.  dprintf_command | 
 |      requires that processing stop on ','.  */ | 
 |   while ((*argp)[0] != '\0' && (*argp)[0] != ',') | 
 |     { | 
 |       int len; | 
 |       const char *start; | 
 |  | 
 |       /* Clear these on each iteration, since they should be filled | 
 | 	 with info about the last option.  */ | 
 |       if (completion_info != NULL) | 
 | 	{ | 
 | 	  completion_info->quoted_arg_start = NULL; | 
 | 	  completion_info->quoted_arg_end = NULL; | 
 | 	} | 
 |  | 
 |       /* If *ARGP starts with a keyword, stop processing | 
 | 	 options.  */ | 
 |       if (linespec_lexer_lex_keyword (*argp) != NULL) | 
 | 	break; | 
 |  | 
 |       /* Mark the start of the string in case we need to rewind.  */ | 
 |       start = *argp; | 
 |  | 
 |       if (completion_info != NULL) | 
 | 	completion_info->last_option = start; | 
 |  | 
 |       /* Get the option string.  */ | 
 |       gdb::unique_xmalloc_ptr<char> opt | 
 | 	= explicit_location_spec_lex_one (argp, language, NULL); | 
 |  | 
 |       /* Use the length of the option to allow abbreviations.  */ | 
 |       len = strlen (opt.get ()); | 
 |  | 
 |       /* Get the argument string.  */ | 
 |       *argp = skip_spaces (*argp); | 
 |  | 
 |       /* All options have a required argument.  Checking for this | 
 | 	 required argument is deferred until later.  */ | 
 |       gdb::unique_xmalloc_ptr<char> oarg; | 
 |       /* True if we have an argument.  This is required because we'll | 
 | 	 move from OARG before checking whether we have an | 
 | 	 argument.  */ | 
 |       bool have_oarg = false; | 
 |  | 
 |       /* True if the option needs an argument.  */ | 
 |       bool need_oarg = false; | 
 |  | 
 |       /* Convenience to consistently set both OARG/HAVE_OARG from | 
 | 	 ARG.  */ | 
 |       auto set_oarg = [&] (gdb::unique_xmalloc_ptr<char> arg) | 
 | 	{ | 
 | 	  if (completion_info != NULL) | 
 | 	    { | 
 | 	      /* We do this here because the set of options that take | 
 | 		 arguments matches the set of explicit location | 
 | 		 options.  */ | 
 | 	      completion_info->saw_explicit_location_spec_option = true; | 
 | 	    } | 
 | 	  oarg = std::move (arg); | 
 | 	  have_oarg = oarg != NULL; | 
 | 	  need_oarg = true; | 
 | 	}; | 
 |  | 
 |       if (strncmp (opt.get (), "-source", len) == 0) | 
 | 	{ | 
 | 	  set_oarg (explicit_location_spec_lex_one (argp, language, | 
 | 						    completion_info)); | 
 | 	  locspec->source_filename = oarg.release (); | 
 | 	} | 
 |       else if (strncmp (opt.get (), "-function", len) == 0) | 
 | 	{ | 
 | 	  set_oarg (explicit_location_spec_lex_one_function (argp, language, | 
 | 							     completion_info)); | 
 | 	  locspec->function_name = oarg.release (); | 
 | 	} | 
 |       else if (strncmp (opt.get (), "-qualified", len) == 0) | 
 | 	{ | 
 | 	  locspec->func_name_match_type = symbol_name_match_type::FULL; | 
 | 	} | 
 |       else if (strncmp (opt.get (), "-line", len) == 0) | 
 | 	{ | 
 | 	  set_oarg (explicit_location_spec_lex_one (argp, language, NULL)); | 
 | 	  *argp = skip_spaces (*argp); | 
 | 	  if (have_oarg) | 
 | 	    { | 
 | 	      locspec->line_offset = linespec_parse_line_offset (oarg.get ()); | 
 | 	      continue; | 
 | 	    } | 
 | 	} | 
 |       else if (strncmp (opt.get (), "-label", len) == 0) | 
 | 	{ | 
 | 	  set_oarg (explicit_location_spec_lex_one (argp, language, | 
 | 						    completion_info)); | 
 | 	  locspec->label_name = oarg.release (); | 
 | 	} | 
 |       /* Only emit an "invalid argument" error for options | 
 | 	 that look like option strings.  */ | 
 |       else if (opt.get ()[0] == '-' && !isdigit (opt.get ()[1])) | 
 | 	{ | 
 | 	  if (completion_info == NULL) | 
 | 	    error (_("invalid explicit location argument, \"%s\""), opt.get ()); | 
 | 	} | 
 |       else | 
 | 	{ | 
 | 	  /* End of the explicit location specification. | 
 | 	     Stop parsing and return whatever explicit location was | 
 | 	     parsed.  */ | 
 | 	  *argp = start; | 
 | 	  break; | 
 | 	} | 
 |  | 
 |       *argp = skip_spaces (*argp); | 
 |  | 
 |       /* It's a little lame to error after the fact, but in this | 
 | 	 case, it provides a much better user experience to issue | 
 | 	 the "invalid argument" error before any missing | 
 | 	 argument error.  */ | 
 |       if (need_oarg && !have_oarg && completion_info == NULL) | 
 | 	error (_("missing argument for \"%s\""), opt.get ()); | 
 |     } | 
 |  | 
 |   /* One special error check:  If a source filename was given | 
 |      without offset, function, or label, issue an error.  */ | 
 |   if (locspec->source_filename != NULL | 
 |       && locspec->function_name == NULL | 
 |       && locspec->label_name == NULL | 
 |       && (locspec->line_offset.sign == LINE_OFFSET_UNKNOWN) | 
 |       && completion_info == NULL) | 
 |     { | 
 |       error (_("Source filename requires function, label, or " | 
 | 	       "line offset.")); | 
 |     } | 
 |  | 
 |   return location_spec_up (locspec.release ()); | 
 | } | 
 |  | 
 | /* See description in location.h.  */ | 
 |  | 
 | location_spec_up | 
 | string_to_location_spec_basic (const char **stringp, | 
 | 			       const struct language_defn *language, | 
 | 			       symbol_name_match_type match_type) | 
 | { | 
 |   location_spec_up locspec; | 
 |   const char *cs; | 
 |  | 
 |   /* Try the input as a probe spec.  */ | 
 |   cs = *stringp; | 
 |   if (cs != NULL && probe_linespec_to_static_ops (&cs) != NULL) | 
 |     { | 
 |       locspec = new_probe_location_spec (*stringp); | 
 |       *stringp += strlen (*stringp); | 
 |     } | 
 |   else | 
 |     { | 
 |       /* Try an address location spec.  */ | 
 |       if (*stringp != NULL && **stringp == '*') | 
 | 	{ | 
 | 	  const char *arg, *orig; | 
 | 	  CORE_ADDR addr; | 
 |  | 
 | 	  orig = arg = *stringp; | 
 | 	  addr = linespec_expression_to_pc (&arg); | 
 | 	  locspec = new_address_location_spec (addr, orig, arg - orig); | 
 | 	  *stringp += arg - orig; | 
 | 	} | 
 |       else | 
 | 	{ | 
 | 	  /* Everything else is a linespec.  */ | 
 | 	  locspec = new_linespec_location_spec (stringp, match_type); | 
 | 	} | 
 |     } | 
 |  | 
 |   return locspec; | 
 | } | 
 |  | 
 | /* See description in location.h.  */ | 
 |  | 
 | location_spec_up | 
 | string_to_location_spec (const char **stringp, | 
 | 			 const struct language_defn *language, | 
 | 			 symbol_name_match_type match_type) | 
 | { | 
 |   const char *arg, *orig; | 
 |  | 
 |   /* Try an explicit location spec.  */ | 
 |   orig = arg = *stringp; | 
 |   location_spec_up locspec | 
 |     = string_to_explicit_location_spec (&arg, language, NULL); | 
 |   if (locspec != nullptr) | 
 |     { | 
 |       /* It was a valid explicit location.  Advance STRINGP to | 
 | 	 the end of input.  */ | 
 |       *stringp += arg - orig; | 
 |  | 
 |       /* If the user really specified a location spec, then we're | 
 | 	 done.  */ | 
 |       if (!locspec->empty_p ()) | 
 | 	return locspec; | 
 |  | 
 |       /* Otherwise, the user _only_ specified optional flags like | 
 | 	 "-qualified", otherwise string_to_explicit_location_spec | 
 | 	 would have thrown an error.  Save the flags for "basic" | 
 | 	 linespec parsing below and discard the explicit location | 
 | 	 spec.  */ | 
 |       explicit_location_spec *xloc | 
 | 	= gdb::checked_static_cast<explicit_location_spec *> (locspec.get ()); | 
 |       match_type = xloc->func_name_match_type; | 
 |     } | 
 |  | 
 |   /* Everything else is a "basic" linespec, address, or probe location | 
 |      spec.  */ | 
 |   return string_to_location_spec_basic (stringp, language, match_type); | 
 | } |