This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[RFA 1/2] Linespec rewrite (update 2)


Hi,

Tom requested a repost of this patch, so here it is. Here is a list of differences between this version and the previously posted patch:

* skip_quote_char rewritten (Doug found a host of bugs in it)
* added backward compatibility for quote-enclosed linespecs (so no NEWS entry needed)
* copy_token_string now uses replace_trailing_whitespace function from cli/cli-utils.[ch]
* miscellaneous test fiddling (from other posts by Tom and Pedro)
* some new error checking/conditions added (bugs reported by Doug)


This patch is now essentially standalone, as Pedro requested. [Previously I posted patches to be committed all together when finally approved.]

Keith

ChangeLog
2012-03-26  Keith Seitz  <keiths@redhat.com>

	* linespec.c (decode_compound): Remove.
	(enum offset_relative_sign): New enum.
	(struct line_offset): New struct.
	(struct linespec): New struct.
	(struct linespec_state): Move file_symtabs,
	user_filename, and user_function into struct linespec.
	Make result an anonymous struct holding vectors of
	symbolp and minsym_and_objfile_d.
	(enum ls_token_type): New enum.
	(linespec_keywords): New array.
	(struct ls_token): New struct.
	(struct ls_parser): New struct.
	(linespec_lexer_lex_number): New function.
	(linespec_lexer_lex_keyword): New function.
	(is_ada_operator): New function.
	(skip_quote_char): New function.
	(copy_token_string): New function.
	(linespec_lexer_lex_string): New function.
	(linespec_lexer_lex_one): New function.
	(linespec_lexer_consume_token): New function.
	(linespec_lexer_peek_token): New function.
	(cplusplus_error): Remove unused function.
	(find_methods): Update comment.
	(find_toplevel_char): Return const.
	(is_objc_method_format): Remove unused function.
	(find_toplevel_string): New function.
	(is_linespec_boundary): Remove.
	(symbol_not_found_error): New function.
	(find_method_overload_end): Remove function.
	(unexpected_linespec_error): New function.
	(keep_name_info): Remove.
	(linespec_parse_line_offset): New function.
	(linespec_parse_basic): New function.
	(canonicalize_linespec): New function.
	(decode_line_internal): Remove.
	(create_sals_line_offset): New function adapted from
	decode_all_digits.
	(convert_linespec_to_sals): New function.
	(parse_linespec): New function.
	(linespec_parser_new): New function.
	(linespec_state_destructor): Change parameter type to
	struct linespec_state *.
	Remove freeing of moved members.
	(linespec_parser_delete): New function.
	(decode_line_full): Use parse_linespec and linespec_parser_new.
	(decode_line_1): Likewise.
	(decode_indirect): Rename to ...
	(linespec_expression_to_pc): ... this and rewrite
	to simply find CORE_ADDR, storing this result for later
	conversion to SALs.
	(locate_first_half): Remove.
	(deocde_objc): Add parameter LS.
	Initialize new struct collect_info members.
	Handle minimal symbols, too.
	(decode_compound): Delete.
	(lookup_prefix_sym): Rewrite.
	(compare_msymbols): New function.
	(find_method): Rewrite.
	Do not call cplusplus_error.
	(symtabs_from_filename): Rewrite.
	(collect_function_symbols): Delete.
	(find_function_symbols): Rewrite without ARGPTR-style
	processing.
	(decode_all_digits): Delete. (Rewritten as create_sals_line_offset.)
	(decode_dollar): Adapted and renamed to ...
	(linespec_parse_variable): ... this.
	(find_linespec_symbols): New function.
	(decode_label): Adapted and renamed to ...
	(find_label_symbols): ... this.
	(decode_digits_list_mode): Add and use LS argument.
	(decode_digits_ordinary): Likewise.
	(collect_symbols): Do not collect SALs, just symbols and msymbols.
	If in list mode, allow any symbol class.  Otherwise, only
	permit LOC_BLOCK symbols.
	(minsym_found): Update comments.
	(search_minsyms_for_name): Do not convert the matching symbol
	into a SAL.  Simply push the symbol and objfile into the
	result vector.
	(decode_variable): Delete. Contents adapted into
	find_linespec_symbols.

testsuite/ChangeLog
2012-03-26  Keith Seitz  <keiths@redhat.com>

	* gdb.base/advance.exp: Update error message for
	"advance malformed" test.
	* gdb.base/break.exp: Likewise for "breakpoint with
	trailing garbage" test.
	* gdb.base/hbreak2.exp: Likewise for "hardware breakpoint
	with trailing garbage" test.
	* gdb.base/sepdebug.exp: Likewise for "breakpoint with
	trailng garbage" test.
	* gdb.base/until.exp: Likewise for "malformed until" test.
	* gdb.cp/ovldbreak.exp: Create the breakpoint table
	for "breakpoint info (after setting on all)".
	* gdb.cp/userdef.exp: Remove quoting for "break A2::operator+"
	tests.
	* gdb.cp/cplabel.cc: New file.
	* gdb.cp/cplabel.exp: New test.
	* gdb.linespec/ls-errs.c: New file.
	* gdb.linespec/ls-errs.exp: New test.

diff --git a/gdb/linespec.c b/gdb/linespec.c
index 1e9770e..50cf2e0 100644
--- a/gdb/linespec.c
+++ b/gdb/linespec.c
@@ -63,6 +63,74 @@ struct address_entry
   CORE_ADDR addr;
 };
 
+/* A helper struct which just holds a minimal symbol and the object
+   file from which it came.  */
+
+typedef struct minsym_and_objfile
+{
+  struct minimal_symbol *minsym;
+  struct objfile *objfile;
+} minsym_and_objfile_d;
+
+DEF_VEC_O (minsym_and_objfile_d);
+
+/* An enumeration of possible signs for a line offset.  */
+enum offset_relative_sign
+{
+  /* No sign  */
+  none,
+
+  /* A plus sign ("+")  */
+  plus,
+
+  /* A minus sign ("-")  */
+  minus,
+
+  /* A special "sign" for unspecified offset.  */
+  unknown
+};
+
+/* A line offset in a linespec.  */
+
+struct line_offset
+{
+  /* Line offset and any specified sign.  */
+  int offset;
+  enum offset_relative_sign sign;
+};
+
+/* A linespec.  Elements of this structure are filled in by a parser
+   (either parse_linespec or some other function).  The structure is
+   then converted into SALs by convert_linespec_to_sals.  */
+
+struct linespec
+{
+  /* An expression and the resulting PC.  */
+  char *expression;
+  CORE_ADDR expr_pc;
+
+  /* Any specified file symtabs.  */
+  char *source_filename;
+  VEC (symtab_p) *file_symtabs;
+
+  /* The name of a function or method and any matching symbols.  */
+  char *function_name;
+  VEC (symbolp) *function_symbols;
+  VEC (minsym_and_objfile_d) *minimal_symbols;
+
+  /* The name of a label and matching symbols.  */
+  char *label_name;
+  struct
+  {
+    VEC (symbolp) *label_symbols;
+    VEC (symbolp) *function_symbols;
+  } labels;
+
+  /* Line offset  */
+  struct line_offset line_offset;
+};
+typedef struct linespec *linespec_t;
+
 /* An instance of this is used to keep all state while linespec
    operates.  This instance is passed around as a 'this' pointer to
    the various implementation methods.  */
@@ -78,19 +146,6 @@ struct linespec_state
   /* The default line to use.  */
   int default_line;
 
-  /* If the linespec started with "FILE:", this holds all the matching
-     symtabs.  Otherwise, it will hold a single NULL entry, meaning
-     that the default symtab should be used.  */
-  VEC (symtab_p) *file_symtabs;
-
-  /* If the linespec started with "FILE:", this holds an xmalloc'd
-     copy of "FILE".  */
-  char *user_filename;
-
-  /* If the linespec is "FUNCTION:LABEL", this holds an xmalloc'd copy
-     of "FUNCTION".  */
-  char *user_function;
-
   /* The 'funfirstline' value that was passed in to decode_line_1 or
      decode_line_full.  */
   int funfirstline;
@@ -117,67 +172,125 @@ struct collect_info
   /* The linespec object in use.  */
   struct linespec_state *state;
 
+  /* A list of symtabs to which to restrict matches.  */
+  VEC (symtab_p) *file_symtabs;
+
   /* The result being accumulated.  */
-  struct symtabs_and_lines result;
+  struct
+  {
+    VEC (symbolp) *symbols;
+    VEC (minsym_and_objfile_d) *minimal_symbols;
+  } result;
 };
 
-/* Prototypes for local functions.  */
+/* Token types  */
 
-static void initialize_defaults (struct symtab **default_symtab,
-				 int *default_line);
+enum ls_token_type
+{
+  /* A keyword  */
+  LSTOKEN_KEYWORD = 0,
 
-static struct symtabs_and_lines decode_indirect (struct linespec_state *self,
-						 char **argptr);
+  /* A colon "separator"  */
+  LSTOKEN_COLON,
 
-static char *locate_first_half (char **argptr, int *is_quote_enclosed);
+  /* A string  */
+  LSTOKEN_STRING,
 
-static struct symtabs_and_lines decode_objc (struct linespec_state *self,
-					     char **argptr);
+  /* A number  */
+  LSTOKEN_NUMBER,
+
+  /* EOI (end of input)  */
+  LSTOKEN_EOI,
+
+  /* Consumed token  */
+  LSTOKEN_CONSUMED
+};
+typedef enum ls_token_type linespec_token_type;
 
-static struct symtabs_and_lines decode_compound (struct linespec_state *self,
-						 char **argptr,
-						 char *saved_arg,
-						 char *p);
+/* List of keywords  */
 
-static VEC (symbolp) *lookup_prefix_sym (char **argptr, char *p,
-					 VEC (symtab_p) *,
-					 char **);
+const char * const linespec_keywords[] = { "if", "thread", "task" };
+
+/* A token of the linespec lexer  */
+
+struct ls_token
+{
+  /* The type of the token  */
+  linespec_token_type type;
+
+  /* Data for the token  */
+  union
+  {
+    /* A string, given as a stoken  */
+    struct stoken string;
+
+    /* A keyword  */
+    const char *keyword;
+  } data;
+};
+typedef struct ls_token linespec_token;
+
+#define LS_TOKEN_STOKEN(TOK) (TOK).data.string
+#define LS_TOKEN_KEYWORD(TOK) (TOK).data.keyword
+
+/* An instance of the linespec parser.  */
+
+struct ls_parser
+{
+  /* Lexer internal data  */
+  struct
+  {
+    /* Save head of input stream.  */
+    char *saved_arg;
+
+    /* Head of the input stream.  */
+    char **stream;
+#define PARSER_STREAM(P) (*(P)->lexer.stream)
+
+    /* The current token.  */
+    linespec_token current;
+  } lexer;
+
+  /* Is the entire linespec quote-enclosed?  */
+  int is_quote_enclosed;
 
-static struct symtabs_and_lines find_method (struct linespec_state *self,
-					     char *saved_arg,
-					     char *copy,
-					     const char *class_name,
-					     VEC (symbolp) *sym_classes);
+  /* The state of the parse.  */
+  struct linespec_state state;
+#define PARSER_STATE(PPTR) (&(PPTR)->state)
 
-static void cplusplus_error (const char *name, const char *fmt, ...)
-     ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (2, 3);
+  /* The result of the parse.  */
+  struct linespec result;
+#define PARSER_RESULT(PPTR) (&(PPTR)->result)
+};
+typedef struct ls_parser linespec_parser;
 
-static char *find_toplevel_char (char *s, char c);
+/* Prototypes for local functions.  */
 
-static int is_objc_method_format (const char *s);
+static void initialize_defaults (struct symtab **default_symtab,
+				 int *default_line);
 
-static VEC (symtab_p) *symtabs_from_filename (char **argptr,
-					      char *p, int is_quote_enclosed,
-					      char **user_filename);
+static CORE_ADDR linespec_expression_to_pc (char **exp_ptr);
 
-static VEC (symbolp) *find_function_symbols (char **argptr, char *p,
-					     int is_quote_enclosed,
-					     char **user_function);
+static struct symtabs_and_lines decode_objc (struct linespec_state *self,
+					     linespec_t ls,
+					     char **argptr);
 
-static struct symtabs_and_lines decode_all_digits (struct linespec_state *self,
-						   char **argptr,
-						   char *q);
+static VEC (symtab_p) *symtabs_from_filename (const char *);
 
-static struct symtabs_and_lines decode_dollar (struct linespec_state *self,
-					       char *copy);
+static VEC (symbolp) *find_label_symbols (struct linespec_state *self,
+					  VEC (symbolp) *function_symbols,
+					  VEC (symbolp) **label_funcs_ret,
+					  const char *name);
 
-static int decode_label (struct linespec_state *self,
-			 VEC (symbolp) *function_symbols,
-			 char *copy,
-			 struct symtabs_and_lines *result);
+void find_linespec_symbols (struct linespec_state *self,
+			    VEC (symtab_p) *file_symtabs,
+			    const char *name,
+			    VEC (symbolp) **symbols,
+			    VEC (minsym_and_objfile_d) **minsyms);
 
-static struct symtabs_and_lines decode_variable (struct linespec_state *self,
-						 char *copy);
+static struct line_offset
+     linespec_parse_variable (struct linespec_state *self,
+			      const char *variable);
 
 static int symbol_to_sal (struct symtab_and_line *result,
 			  int funfirstline, struct symbol *sym);
@@ -190,6 +303,367 @@ static void add_all_symbol_names_from_pspace (struct collect_info *info,
 					      struct program_space *pspace,
 					      VEC (const_char_ptr) *names);
 
+static VEC (symtab_p) *collect_symtabs_from_filename (const char *file);
+
+static void decode_digits_ordinary (struct linespec_state *self,
+				    linespec_t ls,
+				    int line,
+				    struct symtabs_and_lines *sals,
+				    struct linetable_entry **best_entry);
+
+static void decode_digits_list_mode (struct linespec_state *self,
+				     linespec_t ls,
+				     struct symtabs_and_lines *values,
+				     struct symtab_and_line val);
+
+static void minsym_found (struct linespec_state *self, struct objfile *objfile,
+			  struct minimal_symbol *msymbol,
+			  struct symtabs_and_lines *result);
+
+static int compare_symbols (const void *a, const void *b);
+
+static int compare_msymbols (const void *a, const void *b);
+
+static const char *find_toplevel_char (const char *s, char c);
+
+/* Permitted quote characters for the parser.  This is different from the
+   completer's quote characters to allow backward compatibility with the
+   previous parser.  */
+static const char *const linespec_quote_characters = "\"\'";
+
+/* Lexer functions.  */
+
+/* Lex a number from the input in PARSER.  This only supports
+   decimal numbers.  */
+
+static linespec_token
+linespec_lexer_lex_number (linespec_parser *parser)
+{
+  linespec_token token;
+
+  token.type = LSTOKEN_NUMBER;
+  LS_TOKEN_STOKEN (token).length = 0;
+  LS_TOKEN_STOKEN (token).ptr = PARSER_STREAM (parser);
+
+  /* Keep any sign at the start of the stream.  */
+  if (*PARSER_STREAM (parser) == '+' || *PARSER_STREAM (parser) == '-')
+    {
+      ++LS_TOKEN_STOKEN (token).length;
+      ++(PARSER_STREAM (parser));
+    }
+
+  while (isdigit (*PARSER_STREAM (parser)))
+    {
+      ++LS_TOKEN_STOKEN (token).length;
+      ++(PARSER_STREAM (parser));
+    }
+
+  return token;
+}
+
+/* Does P represent one of the keywords?  If so, return
+   the keyword.  If not, return NULL.  */
+
+static const char *
+linespec_lexer_lex_keyword (const char *p)
+{
+  int i;
+
+  if (p != NULL)
+    {
+      for (i = 0; i < ARRAY_SIZE (linespec_keywords); ++i)
+	{
+	  int len = strlen (linespec_keywords[i]);
+
+	  /* If P begins with one of the keywords and the next
+	     character is not a valid identifier character,
+	     we have found a keyword.  */
+	  if (strncmp (p, linespec_keywords[i], len) == 0
+	      && !(isalnum (p[len]) || p[len] == '_'))
+	    return linespec_keywords[i];
+	}
+    }
+
+  return NULL;
+}
+
+/* Does STRING represent an Ada operator?  If so, return the length
+   of the decoded operator name.  If not, return 0.  */
+
+static int
+is_ada_operator (const char *string)
+{
+  const struct ada_opname_map *mapping;
+
+  for (mapping = ada_opname_table;
+       mapping->encoded != NULL
+	 && strncmp (mapping->decoded, string,
+		     strlen (mapping->decoded)) != 0; ++mapping)
+    ;
+
+  return mapping->decoded == NULL ? 0 : strlen (mapping->decoded);
+}
+
+/* Find QUOTE_CHAR in STRING, accounting for the ':' terminal.  Return
+   the location of QUOTE_CHAR, or NULL if not found.  */
+
+static char *
+skip_quote_char (const char *string, char quote_char)
+{
+  const char *p, *last;
+
+  p = last = find_toplevel_char (string, quote_char);
+  while (p && *p != '\0' && *p != ':')
+    {
+      p = find_toplevel_char (p, quote_char);
+      if (p != NULL)
+	last = p++;
+    }
+
+  return (char *) last;
+}
+
+/* Make a writable copy of the string given in TOKEN, trimming
+   any trailing whitespace.  */
+
+static char *
+copy_token_string (linespec_token token)
+{
+  char *str, *s;
+
+  if (token.type == LSTOKEN_KEYWORD)
+    return xstrdup (LS_TOKEN_KEYWORD (token));
+
+  str = savestring (LS_TOKEN_STOKEN (token).ptr,
+		    LS_TOKEN_STOKEN (token).length);
+  s = remove_trailing_whitespace (str, str + LS_TOKEN_STOKEN (token).length);
+  *s = '\0';
+
+  return str;
+}
+
+/* Lex a string from the input in PARSER.  */
+
+static linespec_token
+linespec_lexer_lex_string (linespec_parser *parser)
+{
+  linespec_token token;
+  char *start = PARSER_STREAM (parser);
+
+  token.type = LSTOKEN_STRING;
+
+  /* If the input stream starts with a quote character, skip to the next
+     quote character, regardless of the content.  */
+  if (strchr (linespec_quote_characters, *PARSER_STREAM (parser)))
+    {
+      char *end;
+      char quote_char = *PARSER_STREAM (parser);
+
+      /* Special case: Ada operators.  */
+      if (current_language->la_language == language_ada
+	  && quote_char == '\"')
+	{
+	  int len = is_ada_operator (PARSER_STREAM (parser));
+
+	  if (len != 0)
+	    {
+	      /* The input is an Ada operator.  Return the quoted string
+		 as-is.  */
+	      LS_TOKEN_STOKEN (token).ptr = PARSER_STREAM (parser);
+	      LS_TOKEN_STOKEN (token).length = len;
+	      PARSER_STREAM (parser) += len;
+	      return token;
+	    }
+
+	  /* The input does not represent an Ada operator -- fall through
+	     to normal quoted string handling.  */
+	}
+
+      /* Skip past the beginning quote.  */
+      ++(PARSER_STREAM (parser));
+
+      /* Mark the start of the string.  */
+      LS_TOKEN_STOKEN (token).ptr = PARSER_STREAM (parser);
+
+      /* Skip to the ending quote.  */
+      end = skip_quote_char (PARSER_STREAM (parser), quote_char);
+
+      /* Error if the input did not terminate properly.  */
+      if (end == NULL)
+	error (_("unmatched quote"));
+
+      /* Skip over the ending quote and mark the length of the string.  */
+      PARSER_STREAM (parser) = ++end;
+      LS_TOKEN_STOKEN (token).length = PARSER_STREAM (parser) - 2 - start;
+    }
+  else
+    {
+      char *p;
+
+      /* Otherwise, only identifier characters are permitted.
+	 Spaces are the exception.  In general, we keep spaces,
+	 but only if the next characters in the input do not resolve
+	 to one of the keywords.
+
+	 This allows users to forgo quoting CV-qualifiers, template arguments,
+	 and similar common language constructs.  */
+
+      while (1)
+	{
+	  if (isspace (*PARSER_STREAM (parser)))
+	    {
+	      p = skip_spaces (PARSER_STREAM (parser));
+	      if (linespec_lexer_lex_keyword (p) != NULL)
+		{
+		  LS_TOKEN_STOKEN (token).ptr = start;
+		  LS_TOKEN_STOKEN (token).length
+		    = PARSER_STREAM (parser) - start;
+		  return token;
+		}
+
+	      /* Advance past the whitespace.  */
+	      PARSER_STREAM (parser) = p;
+	    }
+
+	  /* If the next character is EOI or (single) ':', the
+	     string is complete;  return the token.  */
+	  if (*PARSER_STREAM (parser) == 0)
+	    {
+	      LS_TOKEN_STOKEN (token).ptr = start;
+	      LS_TOKEN_STOKEN (token).length = PARSER_STREAM (parser) - start;
+	      return token;
+	    }
+	  else if (PARSER_STREAM (parser)[0] == ':')
+	    {
+	      /* Do not tokenize the C++ scope operator. */
+	      if (PARSER_STREAM (parser)[1] == ':')
+		++(PARSER_STREAM (parser));
+
+	      /* Do not tokenify if the input length so far is one
+		 (i.e, a single-letter drive name) and the next character
+		 is a directory separator.  This allows Windows-style
+		 paths to be recognized as filenames without quoting it.  */
+	      else if ((PARSER_STREAM (parser) - start) != 1
+		       || !IS_DIR_SEPARATOR (PARSER_STREAM (parser)[1]))
+		{
+		  LS_TOKEN_STOKEN (token).ptr = start;
+		  LS_TOKEN_STOKEN (token).length
+		    = PARSER_STREAM (parser) - start;
+		  return token;
+		}
+	    }
+	  /* Special case: permit quote-enclosed linespecs.  */
+	  else if (parser->is_quote_enclosed
+		   && strchr (linespec_quote_characters,
+			      PARSER_STREAM (parser)[0])
+		   && PARSER_STREAM (parser)[1] == '\0')
+	    {
+	      LS_TOKEN_STOKEN (token).ptr = start;
+	      LS_TOKEN_STOKEN (token).length = PARSER_STREAM (parser) - start;
+	      return token;
+	    }
+
+	  /* Advance the stream.  */
+	  ++(PARSER_STREAM (parser));
+	}
+    }
+
+  return token;
+}
+
+/* Lex a single linespec token from PARSER.  */
+
+static linespec_token
+linespec_lexer_lex_one (linespec_parser *parser)
+{
+  const char *keyword;
+
+  if (parser->lexer.current.type == LSTOKEN_CONSUMED)
+    {
+      /* Skip any whitespace.  */
+      PARSER_STREAM (parser) = skip_spaces (PARSER_STREAM (parser));
+
+      /* Check for a keyword.  */
+      keyword = linespec_lexer_lex_keyword (PARSER_STREAM (parser));
+      if (keyword != NULL)
+	{
+	  parser->lexer.current.type = LSTOKEN_KEYWORD;
+	  LS_TOKEN_KEYWORD (parser->lexer.current) = keyword;
+	  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':
+          parser->lexer.current = linespec_lexer_lex_number (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 && PARSER_STREAM (parser)[1] == '\0')
+	    {
+	      ++(PARSER_STREAM (parser));
+	      parser->lexer.current.type = LSTOKEN_EOI;
+	    }
+	  else
+	    parser->lexer.current = linespec_lexer_lex_string (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.  */
+
+static linespec_token
+linespec_lexer_consume_token (linespec_parser *parser)
+{
+  parser->lexer.current.type = LSTOKEN_CONSUMED;
+  return linespec_lexer_lex_one (parser);
+}
+
+/* Return the next token without consuming the current token.  */
+
+static linespec_token
+linespec_lexer_peek_token (linespec_parser *parser)
+{
+  linespec_token next;
+  char *saved_stream = PARSER_STREAM (parser);
+  linespec_token saved_token = parser->lexer.current;
+
+  next = linespec_lexer_consume_token (parser);
+  PARSER_STREAM (parser) = saved_stream;
+  parser->lexer.current = saved_token;
+  return next;
+}
+
 /* Helper functions.  */
 
 /* Add SAL to SALS.  */
@@ -288,39 +762,6 @@ maybe_add_address (htab_t set, struct program_space *pspace, CORE_ADDR addr)
   return 1;
 }
 
-/* Issue a helpful hint on using the command completion feature on
-   single quoted demangled C++ symbols as part of the completion
-   error.  */
-
-static void
-cplusplus_error (const char *name, const char *fmt, ...)
-{
-  struct ui_file *tmp_stream;
-  char *message;
-
-  tmp_stream = mem_fileopen ();
-  make_cleanup_ui_file_delete (tmp_stream);
-
-  {
-    va_list args;
-
-    va_start (args, fmt);
-    vfprintf_unfiltered (tmp_stream, fmt, args);
-    va_end (args);
-  }
-
-  while (*name == '\'')
-    name++;
-  fprintf_unfiltered (tmp_stream,
-		      ("Hint: try '%s<TAB> or '%s<ESC-?>\n"
-		       "(Note leading single quote.)"),
-		      name, name);
-
-  message = ui_file_xstrdup (tmp_stream, NULL);
-  make_cleanup (xfree, message);
-  throw_error (NOT_FOUND_ERROR, "%s", message);
-}
-
 /* A callback function and the additional data to call it with.  */
 
 struct symbol_and_data_callback
@@ -468,7 +909,7 @@ get_search_block (struct symtab *symtab)
 }
 
 /* A helper for find_method.  This finds all methods in type T which
-   match NAME.  It adds resulting symbol names to RESULT_NAMES, and
+   match NAME.  It adds matching symbol names to RESULT_NAMES, and
    adds T's direct superclasses to SUPERCLASSES.  */
 
 static void
@@ -542,14 +983,14 @@ find_methods (struct type *t, const char *name,
    strings.  Also, ignore the char within a template name, like a ','
    within foo<int, int>.  */
 
-static char *
-find_toplevel_char (char *s, char c)
+static 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.  */
-  char *scan;
+  const char *scan;
 
   for (scan = s; *scan; scan++)
     {
@@ -573,23 +1014,34 @@ find_toplevel_char (char *s, char c)
   return 0;
 }
 
-/* Determines if the gives string corresponds to an Objective-C method
-   representation, such as -[Foo bar:] or +[Foo bar].  Objective-C symbols
-   are allowed to have spaces and parentheses in them.  */
+/* 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 int 
-is_objc_method_format (const char *s)
+static const char *
+find_toplevel_string (const char *haystack, const char *needle)
 {
-  if (s == NULL || *s == '\0')
-    return 0;
-  /* Handle arguments with the format FILENAME:SYMBOL.  */
-  if ((s[0] == ':') && (strchr ("+-", s[1]) != NULL) 
-      && (s[2] == '[') && strchr(s, ']'))
-    return 1;
-  /* Handle arguments that are just SYMBOL.  */
-  else if ((strchr ("+-", s[0]) != NULL) && (s[1] == '[') && strchr(s, ']'))
-    return 1;
-  return 0;
+  const char *s = haystack;
+
+  do
+    {
+      s = find_toplevel_char (s, *needle);
+
+      if (s != NULL)
+	{
+	  /* Found first char in HAYSTACK;  check rest of string.  */
+	  if (strncmp (s, needle, strlen (needle)) == 0)
+	    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;
 }
 
 /* Given FILTERS, a list of canonical names, filter the sals in RESULT
@@ -758,118 +1210,640 @@ decode_line_2 (struct linespec_state *self,
   do_cleanups (old_chain);
 }
 
-/* Valid delimiters for linespec keywords "if", "thread" or "task".  */
+
 
-static int
-is_linespec_boundary (char c)
+/* 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 (char *symbol, char *filename)
 {
-  return c == ' ' || c == '\t' || c == '\0' || c == ',';
+  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);
+    }
 }
 
-/* A helper function for decode_line_1 and friends which skips P
-   past any method overload information at the beginning of P, e.g.,
-   "(const struct foo *)".
+/* Throw an appropriate error when an unexpected token is encountered 
+   in the input.  */
 
-   This function assumes that P has already been validated to contain
-   overload information, and it will assert if *P != '('.  */
-static char *
-find_method_overload_end (char *p)
+static void ATTRIBUTE_NORETURN
+unexpected_linespec_error (linespec_parser *parser)
 {
-  int depth = 0;
+  linespec_token token;
+  static const char * token_type_strings[]
+    = {"keyword", "colon", "string", "number", "end of input"};
 
-  gdb_assert (*p == '(');
+  /* Get the token that generated the error.  */
+  token = linespec_lexer_lex_one (parser);
 
-  while (*p)
+  /* Finally, throw the error.  */
+  if (token.type == LSTOKEN_STRING || token.type == LSTOKEN_NUMBER
+      || token.type == LSTOKEN_KEYWORD)
     {
-      if (*p == '(')
-	++depth;
-      else if (*p == ')')
+      char *string;
+      struct cleanup *cleanup;
+
+      string = copy_token_string (token);
+      cleanup = make_cleanup (xfree, string);
+      throw_error (GENERIC_ERROR,
+		   _("malformed linespec error: unexpected %s, \"%s\""),
+		   token_type_strings[token.type], string);
+    }
+  else
+    throw_error (GENERIC_ERROR,
+		 _("malformed linespec error: unexpected %s"),
+		 token_type_strings[token.type]);
+}
+
+/* Parse and return a line offset in STRING.  */
+
+static struct line_offset
+linespec_parse_line_offset (char *string)
+{
+  struct line_offset line_offset = {0, none};
+
+  if (*string == '+')
+    {
+      line_offset.sign = plus;
+      ++string;
+    }
+  else if (*string == '-')
+    {
+      line_offset.sign = minus;
+      ++string;
+    }
+
+  /* Right now, we only allow base 10 for offsets.  */
+  line_offset.offset = atoi (string);
+  return line_offset;
+}
+
+/* Parse the basic_spec in PARSER's input.  */
+
+static void
+linespec_parse_basic (linespec_parser *parser)
+{
+  char *name;
+  linespec_token token;
+  VEC (symbolp) *symbols, *labels;
+  VEC (minsym_and_objfile_d) *minimal_symbols;
+  struct cleanup *cleanup;
+
+  /* Get the next token.  */
+  token = linespec_lexer_lex_one (parser);
+
+  /* If it is EOI or KEYWORD, issue an error.  */
+  if (token.type == LSTOKEN_KEYWORD || token.type == LSTOKEN_EOI)
+    unexpected_linespec_error (parser);
+  /* If it is a LSTOKEN_NUMBER, we have an offset.  */
+  else if (token.type == LSTOKEN_NUMBER)
+    {
+      /* Record the line offset and get the next token.  */
+      name = copy_token_string (token);
+      cleanup = make_cleanup (xfree, name);
+      PARSER_RESULT (parser)->line_offset = linespec_parse_line_offset (name);
+      do_cleanups (cleanup);
+
+      /* Get the next token.  */
+      token = linespec_lexer_consume_token (parser);
+
+      /* If we're in list mode, and the next token is a string beginning
+	 with ",", we're dealing with a ranged listing.  Stop parsing
+	 and return.  */
+      if (PARSER_STATE (parser)->list_mode
+	  && token.type == LSTOKEN_STRING
+	  && *LS_TOKEN_STOKEN (token).ptr == ',')
+	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)
+    unexpected_linespec_error (parser);
+
+  /* The current token will contain the name of a function, method,
+     or label.  */
+  name  = copy_token_string (token);
+  cleanup = make_cleanup (xfree, name);
+
+  /* Try looking it up as a function/method.  */
+  find_linespec_symbols (PARSER_STATE (parser),
+			 PARSER_RESULT (parser)->file_symtabs, name,
+			 &symbols, &minimal_symbols);
+
+  if (symbols != NULL || minimal_symbols != NULL)
+    {
+      PARSER_RESULT (parser)->function_symbols = symbols;
+      PARSER_RESULT (parser)->minimal_symbols = minimal_symbols;
+      PARSER_RESULT (parser)->function_name = name;
+      symbols = NULL;
+      discard_cleanups (cleanup);
+    }
+  else
+    {
+      /* NAME was not a function or a method.  So it must be a label
+	 name.  */
+      labels = find_label_symbols (PARSER_STATE (parser), NULL,
+				   &symbols, name);
+      if (labels != NULL)
 	{
-	  if (--depth == 0)
+	  PARSER_RESULT (parser)->labels.label_symbols = labels;
+	  PARSER_RESULT (parser)->labels.function_symbols = symbols;
+	  PARSER_RESULT (parser)->label_name = name;
+	  symbols = NULL;
+	  discard_cleanups (cleanup);
+	}
+      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_RESULT (parser)->function_name = name;
+	  discard_cleanups (cleanup);
+	  return;
+	}
+    }
+
+  /* Get the next token.  */
+  token = linespec_lexer_consume_token (parser);
+
+  if (token.type == LSTOKEN_COLON)
+    {
+      /* User specified a label or a lineno.  */
+      token = linespec_lexer_consume_token (parser);
+
+      if (token.type == LSTOKEN_NUMBER)
+	{
+	  if (PARSER_RESULT (parser)->function_name != NULL)
 	    {
-	      ++p;
-	      break;
+	      /* We could do this, but it is a new feature.  */
+	      throw_error (UNSUPPORTED_ERROR,
+			   _("FUNCTION:OFFSET is unimplemented"));
+	    }
+	  else
+	    {
+	      /* User specified FILE:LINE.  Record the line offset and
+		 get the next token.  */
+	      name = copy_token_string (token);
+	      cleanup = make_cleanup (xfree, name);
+	      PARSER_RESULT (parser)->line_offset
+		= linespec_parse_line_offset (name);
+	      do_cleanups (cleanup);
+
+	      /* Ge the next token.  */
+	      token = linespec_lexer_consume_token (parser);
+	    }
+	}
+      else if (token.type == LSTOKEN_STRING)
+	{
+	  /* Grab a copy of the label's name and look it up.  */
+	  name = copy_token_string (token);
+	  cleanup = make_cleanup (xfree, name);
+	  labels = find_label_symbols (PARSER_STATE (parser),
+				       PARSER_RESULT (parser)->function_symbols,
+				       &symbols, name);
+
+	  if (labels != NULL)
+	    {
+	      PARSER_RESULT (parser)->labels.label_symbols = labels;
+	      PARSER_RESULT (parser)->labels.function_symbols = symbols;
+	      PARSER_RESULT (parser)->label_name = name;
+	      symbols = NULL;
+	      discard_cleanups (cleanup);
+	    }
+	  else
+	    {
+	      /* We don't know what it was, but it isn't a label.  */
+	      throw_error (NOT_FOUND_ERROR,
+			   _("No label \"%s\" defined in function \"%s\"."),
+			   name, PARSER_RESULT (parser)->function_name);
+	    }
+
+	  /* Check for a line offset.  */
+	  token = linespec_lexer_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 lione offset and get the next token.  */
+	      name = copy_token_string (token);
+	      cleanup = make_cleanup (xfree, name);
+
+	      PARSER_RESULT (parser)->line_offset
+		= linespec_parse_line_offset (name);
+	      do_cleanups (cleanup);
+
+	      /* Get the next token.  */
+	      token = linespec_lexer_consume_token (parser);
 	    }
 	}
-      ++p;
+      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.  */
+
+static void
+canonicalize_linespec (struct linespec_state *state, linespec_t ls)
+{
+  /* If canonicalization was not requested, no need to do anything.  */
+  if (!state->canonical)
+    return;
+
+  /* Shortcut expressions, which can only appear by themselves.  */
+  if (ls->expression != NULL)
+    state->canonical->addr_string = xstrdup (ls->expression);
+  else
+    {
+      struct ui_file *buf;
+      int need_colon = 0;
+
+      buf = mem_fileopen ();
+      if (ls->source_filename)
+	{
+	  fputs_unfiltered (ls->source_filename, buf);
+	  need_colon = 1;
+	}
+
+      if (ls->function_name)
+	{
+	  if (need_colon)
+	    fputc_unfiltered (':', buf);
+	  fputs_unfiltered (ls->function_name, buf);
+	  need_colon = 1;
+	}
 
-  return p;
+      if (ls->label_name)
+	{
+	  if (need_colon)
+	    fputc_unfiltered (':', buf);
+
+	  if (ls->function_name == NULL)
+	    {
+	      struct symbol *s;
+
+	      /* No function was specified, so add the symbol name.  */
+	      gdb_assert (ls->labels.function_symbols != NULL
+			  && (VEC_length (symbolp, ls->labels.function_symbols)
+			      == 1));
+	      s = VEC_index (symbolp, ls->labels.function_symbols, 0);
+	      fputs_unfiltered (SYMBOL_NATURAL_NAME (s), buf);
+	      fputc_unfiltered (':', buf);
+	    }
+
+	  fputs_unfiltered (ls->label_name, buf);
+	  need_colon = 1;
+	  state->canonical->special_display = 1;
+	}
+
+      if (ls->line_offset.sign != unknown)
+	{
+	  if (need_colon)
+	    fputc_unfiltered (':', buf);
+	  fprintf_filtered (buf, "%s%d",
+			    (ls->line_offset.sign == none ? ""
+			     : ls->line_offset.sign == plus ? "+" : "-"),
+			    ls->line_offset.offset);
+	}
+
+      state->canonical->addr_string = ui_file_xstrdup (buf, NULL);
+      ui_file_delete (buf);
+    }
 }
 
-/* Keep important information used when looking up a name.  This includes
-   template parameters, overload information, and important keywords, including
-   the possible Java trailing type.  */
+/* Given a line offset in LS, construct the relevant SALs.  */
 
-static char *
-keep_name_info (char *p, int on_boundary)
+static struct symtabs_and_lines
+create_sals_line_offset (struct linespec_state *self,
+			 linespec_t ls)
 {
-  const char *quotes = get_gdb_completer_quote_characters ();
-  char *saved_p = p;
-  int nest = 0;
+  struct symtabs_and_lines values;
+  struct symtab_and_line val;
+  int use_default = 0;
 
-  while (*p)
+  init_sal (&val);
+  values.sals = NULL;
+  values.nelts = 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 anme, since
+     set_default_source_symtab_and_line uses
+     select_source_symtab that calls us with such an argument.  */
+
+  if (VEC_length (symtab_p, ls->file_symtabs) == 1
+      && VEC_index (symtab_p, ls->file_symtabs, 0) == NULL)
     {
-      if (strchr (quotes, *p))
-	break;
+      set_current_program_space (self->program_space);
 
-      if (*p == ',' && !nest)
-	break;
+      /* Make sure we have at least a default source line.  */
+      set_default_source_symtab_and_line ();
+      initialize_defaults (&self->default_symtab, &self->default_line);
+      VEC_pop (symtab_p, ls->file_symtabs);
+      VEC_free (symtab_p, ls->file_symtabs);
+      ls->file_symtabs
+	= collect_symtabs_from_filename (self->default_symtab->filename);
+      use_default = 1;
+    }
+
+  val.line = ls->line_offset.offset;
+  switch (ls->line_offset.sign)
+    {
+    case plus:
+      if (ls->line_offset.offset == 0)
+	val.line = 5;
+      if (use_default)
+	val.line = self->default_line + val.line;
+      break;
+
+    case minus:
+      if (ls->line_offset.offset == 0)
+	val.line = 15;
+      if (use_default)
+	val.line = self->default_line - val.line;
+      else
+	val.line = -val.line;
+      break;
+
+    case none:
+      break;			/* No need to adjust val.line.  */
+    }
+
+  if (self->list_mode)
+    decode_digits_list_mode (self, ls, &values, val);
+  else
+    {
+      struct linetable_entry *best_entry = NULL;
+      int *filter;
+      struct block **blocks;
+      struct cleanup *cleanup;
+      struct symtabs_and_lines intermediate_results;
+      int i, j;
+
+      intermediate_results.sals = NULL;
+      intermediate_results.nelts = 0;
+
+      decode_digits_ordinary (self, ls, val.line, &intermediate_results,
+			      &best_entry);
+      if (intermediate_results.nelts == 0 && best_entry != NULL)
+	decode_digits_ordinary (self, ls, best_entry->line,
+				&intermediate_results, &best_entry);
+
+      cleanup = make_cleanup (xfree, intermediate_results.sals);
 
-      if (on_boundary && !nest)
+      /* 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.  */
+
+      filter = XNEWVEC (int, intermediate_results.nelts);
+      make_cleanup (xfree, filter);
+      blocks = XNEWVEC (struct block *, intermediate_results.nelts);
+      make_cleanup (xfree, blocks);
+
+      for (i = 0; i < intermediate_results.nelts; ++i)
 	{
-	  const char *const words[] = { "if", "thread", "task" };
-	  int wordi;
+	  set_current_program_space (intermediate_results.sals[i].pspace);
 
-	  for (wordi = 0; wordi < ARRAY_SIZE (words); wordi++)
-	    if (strncmp (p, words[wordi], strlen (words[wordi])) == 0
-		&& is_linespec_boundary (p[strlen (words[wordi])]))
-	      break;
-	  if (wordi < ARRAY_SIZE (words))
-	    break;
+	  filter[i] = 1;
+	  blocks[i] = block_for_pc_sect (intermediate_results.sals[i].pc,
+					 intermediate_results.sals[i].section);
 	}
 
-      if (*p == '(' || *p == '<' || *p == '[')
-	nest++;
-      else if ((*p == ')' || *p == '>' || *p == ']') && nest > 0)
-	nest--;
+      for (i = 0; i < intermediate_results.nelts; ++i)
+	{
+	  if (blocks[i] != NULL)
+	    for (j = i + 1; j < intermediate_results.nelts; ++j)
+	      {
+		if (blocks[j] == blocks[i])
+		  {
+		    filter[j] = 0;
+		    break;
+		  }
+	      }
+	}
 
-      p++;
+      for (i = 0; i < intermediate_results.nelts; ++i)
+	if (filter[i])
+	  {
+	    struct symbol *sym = (blocks[i]
+				  ? block_containing_function (blocks[i])
+				  : NULL);
 
-      /* The ',' check could fail on "operator ,".  */
-      p += cp_validate_operator (p);
+	    if (self->funfirstline)
+	      skip_prologue_sal (&intermediate_results.sals[i]);
+	    /* Make sure the line matches the request, not what was
+	       found.  */
+	    intermediate_results.sals[i].line = val.line;
+	    add_sal_to_sals (self, &values, &intermediate_results.sals[i],
+			     sym ? SYMBOL_NATURAL_NAME (sym) : NULL);
+	  }
 
-      on_boundary = is_linespec_boundary (p[-1]);
+      do_cleanups (cleanup);
     }
 
-  while (p > saved_p && is_linespec_boundary (p[-1]))
-    p--;
+  if (values.nelts == 0)
+    {
+      if (ls->source_filename)
+	throw_error (NOT_FOUND_ERROR, _("No line %d in file \"%s\"."),
+		     val.line, ls->source_filename);
+      else
+	throw_error (NOT_FOUND_ERROR, _("No line %d in the current file."),
+		     val.line);
+    }
 
-  return p;
+  return values;
 }
 
-
-/* The parser of linespec itself.  */
+/* Create and return SALs from the linespec LS.  */
+
+static struct symtabs_and_lines
+convert_linespec_to_sals (struct linespec_state *state, linespec_t ls)
+{
+  struct symtabs_and_lines sals = {NULL, 0};
+
+  if (ls->expression != NULL)
+    {
+      /* We have an expression.  No other attribute is allowed.  */
+      sals.sals = XMALLOC (struct symtab_and_line);
+      sals.nelts = 1;
+      sals.sals[0] = find_pc_line (ls->expr_pc, 0);
+      sals.sals[0].pc = ls->expr_pc;
+      sals.sals[0].section = find_pc_overlay (ls->expr_pc);
+      sals.sals[0].explicit_pc = 1;
+    }
+  else if (ls->labels.label_symbols != NULL)
+    {
+      /* We have just a bunch of functions/methods or labels.  */
+      int i;
+      struct symtab_and_line sal;
+      struct symbol *sym;
+
+      for (i = 0; VEC_iterate (symbolp, ls->labels.label_symbols, i, sym); ++i)
+	{
+	  symbol_to_sal (&sal, state->funfirstline, sym);
+	  add_sal_to_sals (state, &sals, &sal,
+			   SYMBOL_NATURAL_NAME (sym));
+	}
+    }
+  else if (ls->function_symbols != NULL)
+    {
+      /* We have just a bunch of functions and/or methods.  */
+      int i;
+      struct symtab_and_line sal;
+      struct symbol *sym;
+      minsym_and_objfile_d *elem;
+      struct program_space *pspace;
+
+      /* Sort symbols so that symbols with the same program space are next
+	 to each other.  */
+      qsort (VEC_address (symbolp, ls->function_symbols),
+	     VEC_length (symbolp, ls->function_symbols),
+	     sizeof (symbolp), compare_symbols);
+
+      for (i = 0; VEC_iterate (symbolp, ls->function_symbols, i, sym); ++i)
+	{
+	  pspace = SYMTAB_PSPACE (SYMBOL_SYMTAB (sym));
+	  set_current_program_space (pspace);
+	  symbol_to_sal (&sal, state->funfirstline, sym);
+	  if (maybe_add_address (state->addr_set, pspace, sal.pc))
+	    add_sal_to_sals (state, &sals, &sal, SYMBOL_NATURAL_NAME (sym));
+	}
+
+      /* Sort minimal symbols by program space, too.  */
+      qsort (VEC_address (minsym_and_objfile_d, ls->minimal_symbols),
+	     VEC_length (minsym_and_objfile_d, ls->minimal_symbols),
+	     sizeof (minsym_and_objfile_d), compare_msymbols);
+
+      for (i = 0;
+	   VEC_iterate (minsym_and_objfile_d, ls->minimal_symbols, i, elem);
+	   ++i)
+	{
+	  pspace = elem->minsym->ginfo.obj_section->objfile->pspace;
+	  set_current_program_space (pspace);
+	  minsym_found (state, elem->objfile, elem->minsym, &sals);
+	}
+    }
+  else if (ls->minimal_symbols != NULL)
+    {
+      /* We found minimal symbols, but no normal symbols.  */
+      int i;
+      minsym_and_objfile_d *elem;
+
+      for (i = 0;
+	   VEC_iterate (minsym_and_objfile_d, ls->minimal_symbols, i, elem);
+	   ++i)
+	minsym_found (state, elem->objfile, elem->minsym, &sals);
+    }
+  else if (ls->line_offset.sign != unknown)
+    {
+      /* Only an offset was specified.  */
+	sals = create_sals_line_offset (state, ls);
 
-/* Parse a string that specifies a line number.
+	/* Make sure we have a filename for canonicalization.  */
+	if (ls->source_filename == NULL)
+	  ls->source_filename = xstrdup (state->default_symtab->filename);
+    }
+  else
+    {
+      /* We haven't found any results...  */
+      return sals;
+    }
+
+  canonicalize_linespec (state, ls);
+
+  if (sals.nelts > 0 && state->canonical != NULL)
+    state->canonical->pre_expanded = 1;
+
+  return sals;
+}
+
+/* Parse a string that specifies a linespec.
    Pass the address of a char * variable; that variable will be
    advanced over the characters actually parsed.
 
-   The string can be:
+   The basic grammar of linespecs:
 
-   LINENUM -- that line number in current file.  PC returned is 0.
-   FILE:LINENUM -- that line in that file.  PC returned is 0.
-   FUNCTION -- line number of openbrace of that function.
-   PC returned is the start of the function.
-   LABEL -- a label in the current scope
-   VARIABLE -- line number of definition of that variable.
-   PC returned is 0.
-   FILE:FUNCTION -- likewise, but prefer functions in that file.
-   *EXPR -- line in which address EXPR appears.
+   linespec -> expr_spec | var_spec | basic_spec
+   expr_spec -> '*' STRING
+   var_spec -> '$' (STRING | NUMBER)
 
-   This may all be followed by an "if EXPR", which we ignore.
+   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
 
-   FUNCTION may be an undebuggable function found in minimal symbol table.
+   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.
+
+   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
@@ -889,334 +1863,205 @@ keep_name_info (char *p, int on_boundary)
    if no file is validly specified.  Callers must check that.
    Also, the line number returned may be invalid.  */
 
-/* We allow single quotes in various places.  This is a hideous
-   kludge, which exists because the completer can't yet deal with the
-   lack of single quotes.  FIXME: write a linespec_completer which we
-   can use as appropriate instead of make_symbol_completion_list.  */
+/* Parse the linespec in ARGPTR.  */
 
 static struct symtabs_and_lines
-decode_line_internal (struct linespec_state *self, char **argptr)
+parse_linespec (linespec_parser *parser, char **argptr)
 {
-  char *p;
-  char *q;
-
-  char *copy;
-  /* This says whether or not something in *ARGPTR is quoted with
-     completer_quotes (i.e. with single quotes).  */
-  int is_quoted;
-  /* Is *ARGPTR enclosed in double quotes?  */
-  int is_quote_enclosed;
-  int is_objc_method = 0;
-  char *saved_arg = *argptr;
-  /* If IS_QUOTED, the end of the quoted bit.  */
-  char *end_quote = NULL;
-  /* Is *ARGPTR enclosed in single quotes?  */
-  int is_squote_enclosed = 0;
-  /* The "first half" of the linespec.  */
-  char *first_half;
-
-  /* If we are parsing `function:label', this holds the symbols
-     matching the function name.  */
-  VEC (symbolp) *function_symbols = NULL;
-  /* If FUNCTION_SYMBOLS is not NULL, then this is the exception that
-     was thrown when trying to parse a filename.  */
+  linespec_token token;
+  struct symtabs_and_lines values;
   volatile struct gdb_exception file_exception;
+  struct cleanup *cleanup;
 
-  struct cleanup *cleanup = make_cleanup (null_cleanup, NULL);
-
-  /* Defaults have defaults.  */
-
-  initialize_defaults (&self->default_symtab, &self->default_line);
-  
-  /* See if arg is *PC.  */
-
-  if (**argptr == '*')
-    {
-      do_cleanups (cleanup);
-      return decode_indirect (self, argptr);
-    }
-
-  is_quoted = (strchr (get_gdb_completer_quote_characters (),
-		       **argptr) != NULL);
-
-  if (is_quoted)
-    {
-      end_quote = skip_quoted (*argptr);
-      if (*end_quote == '\0')
-	is_squote_enclosed = 1;
-    }
-
-  /* Check to see if it's a multipart linespec (with colons or
-     periods).  */
-
-  /* Locate the end of the first half of the linespec.
-     After the call, for instance, if the argptr string is "foo.c:123"
-     p will point at ":123".  If there is only one part, like "foo", p
-     will point to "".  If this is a C++ name, like "A::B::foo", p will
-     point to "::B::foo".  Argptr is not changed by this call.  */
-
-  first_half = p = locate_first_half (argptr, &is_quote_enclosed);
-
-  /* First things first: if ARGPTR starts with a filename, get its
-     symtab and strip the filename from ARGPTR.
-     Avoid calling symtab_from_filename if we know can,
-     it can be expensive.  We know we can avoid the call if we see a
-     single word (e.g., "break NAME") or if we see a qualified C++
-     name ("break QUAL::NAME").  */
-
-  if (*p != '\0' && !(p[0] == ':' && p[1] == ':'))
+  /* 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 (!is_ada_operator (*argptr)
+      && strchr (linespec_quote_characters, **argptr) != NULL)
     {
-      TRY_CATCH (file_exception, RETURN_MASK_ERROR)
-	{
-	  self->file_symtabs = symtabs_from_filename (argptr, p,
-						      is_quote_enclosed,
-						      &self->user_filename);
-	}
-
-      if (file_exception.reason >= 0)
-	{
-	  /* Check for single quotes on the non-filename part.  */
-	  is_quoted = (**argptr
-		       && strchr (get_gdb_completer_quote_characters (),
-				  **argptr) != NULL);
-	  if (is_quoted)
-	    end_quote = skip_quoted (*argptr);
-
-	  /* Locate the next "half" of the linespec.  */
-	  first_half = p = locate_first_half (argptr, &is_quote_enclosed);
-	}
+      char *end;
 
-      if (VEC_empty (symtab_p, self->file_symtabs))
+      end = skip_quote_char (*argptr + 1, **argptr);
+      if (end != NULL && end[1] == '\0')
 	{
-	  /* A NULL entry means to use GLOBAL_DEFAULT_SYMTAB.  */
-	  VEC_safe_push (symtab_p, self->file_symtabs, NULL);
+	  /* Here's the special case.  Skip ARGPTR past the initial
+	     quote.  */
+	  ++(*argptr);
+	  parser->is_quote_enclosed = 1;
 	}
     }
-  else
-    {
-      /* A NULL entry means to use GLOBAL_DEFAULT_SYMTAB.  */
-      VEC_safe_push (symtab_p, self->file_symtabs, NULL);
-    }
 
-  /* Check if this is an Objective-C method (anything that starts with
-     a '+' or '-' and a '[').  */
-  if (is_objc_method_format (p))
-    is_objc_method = 1;
+  parser->lexer.saved_arg = *argptr;
+  parser->lexer.stream = argptr;
+  file_exception.reason = 0;
 
-  /* Check if the symbol could be an Objective-C selector.  */
+  /* Initialize the default symtab and line offset.  */
+  initialize_defaults (&PARSER_STATE (parser)->default_symtab,
+		       &PARSER_STATE (parser)->default_line);
 
-  {
-    struct symtabs_and_lines values;
+  /* Objective-C shortcut.  */
+  values = decode_objc (PARSER_STATE (parser), PARSER_RESULT (parser), argptr);
+  if (values.sals != NULL)
+    return values;
 
-    values = decode_objc (self, argptr);
-    if (values.sals != NULL)
-      {
-	do_cleanups (cleanup);
-	return values;
-      }
-  }
+  /* Start parsing.  */
 
-  /* Does it look like there actually were two parts?  */
+  /* Get the first token.  */
+  token = linespec_lexer_lex_one (parser);
 
-  if (p[0] == ':' || p[0] == '.')
+  /* It must be either LSTOKEN_STRING or LSTOKEN_NUMBER.  */
+  if (token.type == LSTOKEN_STRING && *LS_TOKEN_STOKEN (token).ptr == '*')
     {
-      /* Is it a C++ or Java compound data structure?
-	 The check on p[1] == ':' is capturing the case of "::",
-	 since p[0]==':' was checked above.
-	 Note that the call to decode_compound does everything
-	 for us, including the lookup on the symbol table, so we
-	 can return now.  */
-	
-      if (p[0] == '.' || p[1] == ':')
-	{
-	 /* We only perform this check for the languages where it might
-	    make sense.  For instance, Ada does not use this type of
-	    syntax, and trying to apply this logic on an Ada linespec
-	    may trigger a spurious error (for instance, decode_compound
-	    does not like expressions such as `ops."<"', which is a
-	    valid function name in Ada).  */
-	  if (current_language->la_language == language_c
-	      || current_language->la_language == language_cplus
-	      || current_language->la_language == language_java)
-	    {
-	      struct symtabs_and_lines values;
-	      volatile struct gdb_exception ex;
-	      char *saved_argptr = *argptr;
+      char *expr, *copy;
 
-	      if (is_quote_enclosed)
-		++saved_arg;
+      /* User specified an expression, *EXPR.  */
+      copy = expr = copy_token_string (token);
+      cleanup = make_cleanup (xfree, expr);
+      PARSER_RESULT (parser)->expr_pc = linespec_expression_to_pc (&copy);
+      discard_cleanups (cleanup);
+      PARSER_RESULT (parser)->expression = expr;
 
-	      /* Initialize it just to avoid a GCC false warning.  */
-	      memset (&values, 0, sizeof (values));
+      /* This is a little hacky/tricky.  If linespec_expression_to_pc
+	 did not evaluate the entire token, then we must find the
+	 string COPY inside the original token buffer.  */
+      if (*copy != '\0')
+	{
+	  PARSER_STREAM (parser) = strstr (parser->lexer.saved_arg, copy);
+	  gdb_assert (PARSER_STREAM (parser) != NULL);
+	}
 
-	      TRY_CATCH (ex, RETURN_MASK_ERROR)
-		{
-		  values = decode_compound (self, argptr, saved_arg, p);
-		}
-	      if ((is_quoted || is_squote_enclosed) && **argptr == '\'')
-		*argptr = *argptr + 1;
+      /* Consume the token.  */
+      linespec_lexer_consume_token (parser);
 
-	      if (ex.reason >= 0)
-		{
-		  do_cleanups (cleanup);
-		  return values;
-		}
+      goto convert_to_sals;
+    }
+  else if (token.type == LSTOKEN_STRING && *LS_TOKEN_STOKEN (token).ptr == '$')
+    {
+      char *var;
 
-	      if (ex.error != NOT_FOUND_ERROR)
-		throw_exception (ex);
+      /* A NULL entry means to use GLOBAL_DEFAULT_SYMTAB.  */
+      VEC_safe_push (symtab_p, PARSER_RESULT (parser)->file_symtabs, NULL);
 
-	      *argptr = saved_argptr;
-	    }
-	}
-      else
-	{
-	  /* If there was an exception looking up a specified filename earlier,
-	     then check whether we were really given `function:label'.   */
-	  if (file_exception.reason < 0)
-	    {
-	      function_symbols = find_function_symbols (argptr, p,
-							is_quote_enclosed,
-							&self->user_function);
+      /* User specified a convenience variable or history value.  */
+      var = copy_token_string (token);
+      cleanup = make_cleanup (xfree, var);
+      PARSER_RESULT (parser)->line_offset
+	= linespec_parse_variable (PARSER_STATE (parser), var);
 
-	      /* If we did not find a function, re-throw the original
-		 exception.  */
-	      if (!function_symbols)
-		throw_exception (file_exception);
+      /* If a line_offset wasn't found (VAR is the name of a user
+	 variable/function), then skip to normal symbol processing.  */
+      if (PARSER_RESULT (parser)->line_offset.sign != unknown)
+	{
+	  discard_cleanups (cleanup);
 
-	      make_cleanup (VEC_cleanup (symbolp), &function_symbols);
-	    }
+	  /* Consume this token.  */
+	  linespec_lexer_consume_token (parser);
 
-	  /* Check for single quotes on the non-filename part.  */
-	  if (!is_quoted)
-	    {
-	      is_quoted = (**argptr
-			   && strchr (get_gdb_completer_quote_characters (),
-				      **argptr) != NULL);
-	      if (is_quoted)
-		end_quote = skip_quoted (*argptr);
-	    }
+	  goto convert_to_sals;
 	}
-    }
 
-  /* self->file_symtabs holds the  specified file symtabs, or 0 if no file
-     specified.
-     If we are parsing `function:symbol', then FUNCTION_SYMBOLS holds the
-     functions before the `:'.
-     arg no longer contains the file name.  */
+      do_cleanups (cleanup);
+    }
+  else if (token.type != LSTOKEN_STRING && token.type != LSTOKEN_NUMBER)
+    unexpected_linespec_error (parser);
 
-  /* If the filename was quoted, we must re-check the quotation.  */
+  /* 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 (end_quote == first_half && *end_quote!= '\0')
+  if (token.type == LSTOKEN_COLON)
     {
-      is_quoted = (**argptr
-		   && strchr (get_gdb_completer_quote_characters (),
-			      **argptr) != NULL);
-      if (is_quoted)
-	end_quote = skip_quoted (*argptr);
-    }
+      char *user_filename;
 
-  /* Check whether arg is all digits (and sign).  */
+      /* Get the current token again and extract the filename.  */
+      token = linespec_lexer_lex_one (parser);
+      user_filename = copy_token_string (token);
 
-  q = *argptr;
-  if (*q == '-' || *q == '+')
-    q++;
-  while (*q >= '0' && *q <= '9')
-    q++;
+      /* Check if the input is a filename.  */
+      TRY_CATCH (file_exception, RETURN_MASK_ERROR)
+	{
+	  PARSER_RESULT (parser)->file_symtabs
+	    = symtabs_from_filename (user_filename);
+	}
 
-  if (q != *argptr && (*q == 0 || *q == ' ' || *q == '\t' || *q == ',')
-      && function_symbols == NULL)
-    {
-      struct symtabs_and_lines values;
+      if (file_exception.reason >= 0)
+	{
+	  /* Symtabs were found for the file.  Record the filename.  */
+	  PARSER_RESULT (parser)->source_filename = user_filename;
 
-      /* We found a token consisting of all digits -- at least one digit.  */
-      values = decode_all_digits (self, argptr, q);
-      do_cleanups (cleanup);
-      return values;
-    }
+	  /* Get the next token.  */
+	  token = linespec_lexer_consume_token (parser);
 
-  /* Arg token is not digits => try it as a variable name
-     Find the next token (everything up to end or next whitespace).  */
+	  /* This token must be LSTOKEN_COLON.  */
+	  if (token.type != LSTOKEN_COLON)
+	    return values;
 
-  if (**argptr == '$')		/* May be a convenience variable.  */
-    /* One or two $ chars possible.  */
-    p = skip_quoted (*argptr + (((*argptr)[1] == '$') ? 2 : 1));
-  else if (is_quoted || is_squote_enclosed)
-    {
-      p = end_quote;
-      if (p[-1] != '\'')
-	error (_("Unmatched single quote."));
+	  /* Consume the LSTOKEN_COLON.  */
+	  linespec_lexer_consume_token (parser);
+	}
+      else
+	{
+	  /* No symtabs found -- discard user_filename.  */
+	  xfree (user_filename);
+
+	  /* A NULL entry means to use GLOBAL_DEFAULT_SYMTAB.  */
+	  VEC_safe_push (symtab_p, PARSER_RESULT (parser)->file_symtabs, NULL);
+	}
     }
-  else if (is_objc_method)
+  /* If the next token is not EOI or KEYWORD, issue an error.
+     Exception: In list mode, we allow the next token to be STRING,
+     as long as it starts with ',' (to accomodate ranges).  */
+  else if (token.type != LSTOKEN_EOI && token.type != LSTOKEN_KEYWORD
+	   && (!PARSER_STATE (parser)->list_mode
+	       || (token.type == LSTOKEN_STRING
+		   && *LS_TOKEN_STOKEN (token).ptr != ',')))
     {
-      /* allow word separators in method names for Obj-C.  */
-      p = skip_quoted_chars (*argptr, NULL, "");
+      /* 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
     {
-      p = skip_quoted (*argptr);
-    }
-
-  /* Keep any important naming information.  */
-  p = keep_name_info (p, p == saved_arg || is_linespec_boundary (p[-1]));
-
-  copy = (char *) alloca (p - *argptr + 1);
-  memcpy (copy, *argptr, p - *argptr);
-  copy[p - *argptr] = '\0';
-  if (p != *argptr
-      && copy[0]
-      && copy[0] == copy[p - *argptr - 1]
-      && strchr (get_gdb_completer_quote_characters (), copy[0]) != NULL)
-    {
-      copy[p - *argptr - 1] = '\0';
-      copy++;
+      /* A NULL entry means to use GLOBAL_DEFAULT_SYMTAB.  */
+      VEC_safe_push (symtab_p, PARSER_RESULT (parser)->file_symtabs, NULL);
     }
-  else if (is_quoted || is_squote_enclosed)
-    copy[p - *argptr - 1] = '\0';
-  
-  *argptr = skip_spaces (p);
 
-  /* If it starts with $: may be a legitimate variable or routine name
-     (e.g. HP-UX millicode routines such as $$dyncall), or it may
-     be history value, or it may be a convenience variable.  */
+  /* Parse the rest of the linespec.  */
+  linespec_parse_basic (parser);
 
-  if (*copy == '$' && function_symbols == NULL)
+  if (PARSER_RESULT (parser)->function_symbols == NULL
+      && PARSER_RESULT (parser)->labels.label_symbols == NULL
+      && PARSER_RESULT (parser)->line_offset.sign == unknown
+      && PARSER_RESULT (parser)->minimal_symbols == NULL)
     {
-      struct symtabs_and_lines values;
+      /* The linespec didn't parse.  Re-throw the file exception if
+	 there was one.  */
+      if (file_exception.reason < 0)
+	throw_exception (file_exception);
 
-      values = decode_dollar (self, copy);
-      do_cleanups (cleanup);
-      return values;
-    }
-
-  /* Try the token as a label, but only if no file was specified,
-     because we can only really find labels in the current scope.  */
-
-  if (VEC_length (symtab_p, self->file_symtabs) == 1
-      && VEC_index (symtab_p, self->file_symtabs, 0) == NULL)
-    {
-      struct symtabs_and_lines label_result;
-      if (decode_label (self, function_symbols, copy, &label_result))
-	{
-	  do_cleanups (cleanup);
-	  return label_result;
-	}
+      /* Otherwise, the symbol is not found.  */
+      symbol_not_found_error (PARSER_RESULT (parser)->function_name,
+			      PARSER_RESULT (parser)->source_filename);
     }
 
-  if (function_symbols)
-    throw_exception (file_exception);
+ convert_to_sals:
 
-  /* Look up that token as a variable.
-     If file specified, use that file's per-file block to start with.  */
+  /* 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)
+    PARSER_STREAM (parser) = LS_TOKEN_STOKEN (token).ptr;
 
-  {
-    struct symtabs_and_lines values;
+  /* Convert the data in PARSER_RESULT to SALs.  */
+  values = convert_linespec_to_sals (PARSER_STATE (parser),
+				     PARSER_RESULT (parser));
 
-    values = decode_variable (self, copy);
-    do_cleanups (cleanup);
-    return values;
-  }
+  return values;
 }
 
+
 /* A constructor for linespec_state.  */
 
 static void
@@ -1237,19 +2082,63 @@ linespec_state_constructor (struct linespec_state *self,
 				      xfree, xcalloc, xfree);
 }
 
+/* Initialize a new linespec parser.  */
+static void
+linespec_parser_new (linespec_parser *parser,
+		     int flags,
+		     struct symtab *default_symtab,
+		     int default_line,
+		     struct linespec_result *canonical)
+{
+  parser->lexer.current.type = LSTOKEN_CONSUMED;
+  memset (PARSER_RESULT (parser), 0, sizeof (struct linespec));
+  PARSER_RESULT (parser)->line_offset.sign = unknown;
+  linespec_state_constructor (PARSER_STATE (parser), flags,
+			      default_symtab, default_line, canonical);
+}
+
 /* A destructor for linespec_state.  */
 
 static void
-linespec_state_destructor (void *arg)
+linespec_state_destructor (struct linespec_state *self)
 {
-  struct linespec_state *self = arg;
-
-  xfree (self->user_filename);
-  xfree (self->user_function);
-  VEC_free (symtab_p, self->file_symtabs);
   htab_delete (self->addr_set);
 }
 
+/* Delete a linespec parser.  */
+
+static void
+linespec_parser_delete (void *arg)
+{
+  linespec_parser *parser = (linespec_parser *) arg;
+
+  if (PARSER_RESULT (parser)->expression)
+    xfree (PARSER_RESULT (parser)->expression);
+  if (PARSER_RESULT (parser)->source_filename)
+    xfree (PARSER_RESULT (parser)->source_filename);
+  if (PARSER_RESULT (parser)->label_name)
+    xfree (PARSER_RESULT (parser)->label_name);
+  if (PARSER_RESULT (parser)->function_name)
+    xfree (PARSER_RESULT (parser)->function_name);
+
+  if (PARSER_RESULT (parser)->file_symtabs != NULL)
+    VEC_free (symtab_p, PARSER_RESULT (parser)->file_symtabs);
+
+  if (PARSER_RESULT (parser)->function_symbols != NULL)
+    VEC_free (symbolp, PARSER_RESULT (parser)->function_symbols);
+
+  if (PARSER_RESULT (parser)->minimal_symbols != NULL)
+    VEC_free (minsym_and_objfile_d, PARSER_RESULT (parser)->minimal_symbols);
+
+  if (PARSER_RESULT (parser)->labels.label_symbols != NULL)
+    VEC_free (symbolp, PARSER_RESULT (parser)->labels.label_symbols);
+
+  if (PARSER_RESULT (parser)->labels.function_symbols != NULL)
+    VEC_free (symbolp, PARSER_RESULT (parser)->labels.function_symbols);
+
+  linespec_state_destructor (PARSER_STATE (parser));
+}
+
 /* See linespec.h.  */
 
 void
@@ -1260,10 +2149,11 @@ decode_line_full (char **argptr, int flags,
 		  const char *filter)
 {
   struct symtabs_and_lines result;
-  struct linespec_state state;
   struct cleanup *cleanups;
   char *arg_start = *argptr;
   VEC (const_char_ptr) *filters = NULL;
+  linespec_parser parser;
+  struct linespec_state *state;
 
   gdb_assert (canonical != NULL);
   /* The filter only makes sense for 'all'.  */
@@ -1274,12 +2164,12 @@ decode_line_full (char **argptr, int flags,
 	      || select_mode == multiple_symbols_cancel);
   gdb_assert ((flags & DECODE_LINE_LIST_MODE) == 0);
 
-  linespec_state_constructor (&state, flags,
-			      default_symtab, default_line, canonical);
-  cleanups = make_cleanup (linespec_state_destructor, &state);
+  linespec_parser_new (&parser, flags, default_symtab, default_line, canonical);
+  cleanups = make_cleanup (linespec_parser_delete, &parser);
   save_current_program_space ();
 
-  result = decode_line_internal (&state, argptr);
+  result = parse_linespec (&parser, argptr);
+  state = PARSER_STATE (&parser);
 
   gdb_assert (result.nelts == 1 || canonical->pre_expanded);
   gdb_assert (canonical->addr_string != NULL);
@@ -1290,15 +2180,15 @@ decode_line_full (char **argptr, int flags,
     {
       int i;
 
-      if (state.canonical_names == NULL)
-	state.canonical_names = xcalloc (result.nelts, sizeof (char *));
-      make_cleanup (xfree, state.canonical_names);
+      if (state->canonical_names == NULL)
+	state->canonical_names = xcalloc (result.nelts, sizeof (char *));
+      make_cleanup (xfree, state->canonical_names);
       for (i = 0; i < result.nelts; ++i)
 	{
-	  if (state.canonical_names[i] == NULL)
-	    state.canonical_names[i] = savestring (arg_start,
-						   *argptr - arg_start);
-	  make_cleanup (xfree, state.canonical_names[i]);
+	  if (state->canonical_names[i] == NULL)
+	    state->canonical_names[i] = savestring (arg_start,
+						    *argptr - arg_start);
+	  make_cleanup (xfree, state->canonical_names[i]);
 	}
     }
 
@@ -1316,13 +2206,13 @@ decode_line_full (char **argptr, int flags,
 	{
 	  make_cleanup (VEC_cleanup (const_char_ptr), &filters);
 	  VEC_safe_push (const_char_ptr, filters, filter);
-	  filter_results (&state, &result, filters);
+	  filter_results (state, &result, filters);
 	}
       else
-	convert_results_to_lsals (&state, &result);
+	convert_results_to_lsals (state, &result);
     }
   else
-    decode_line_2 (&state, &result, select_mode);
+    decode_line_2 (state, &result, select_mode);
 
   do_cleanups (cleanups);
 }
@@ -1333,15 +2223,15 @@ decode_line_1 (char **argptr, int flags,
 	       int default_line)
 {
   struct symtabs_and_lines result;
-  struct linespec_state state;
+  linespec_parser parser;
   struct cleanup *cleanups;
 
-  linespec_state_constructor (&state, flags,
-			      default_symtab, default_line, NULL);
-  cleanups = make_cleanup (linespec_state_destructor, &state);
+  linespec_parser_new (&parser, flags, default_symtab, default_line, NULL);
+  cleanups = make_cleanup (linespec_parser_delete, &parser);
   save_current_program_space ();
 
-  result = decode_line_internal (&state, argptr);
+  result = parse_linespec (&parser, argptr);
+
   do_cleanups (cleanups);
   return result;
 }
@@ -1369,178 +2259,20 @@ initialize_defaults (struct symtab **default_symtab, int *default_line)
 
 
 
-/* Decode arg of the form *PC.  */
+/* Evaluate the expression pointed to by EXP_PTR into a CORE_ADDR,
+   advancing EXP_PTR past any parsed text.  */
 
-static struct symtabs_and_lines
-decode_indirect (struct linespec_state *self, char **argptr)
+static CORE_ADDR
+linespec_expression_to_pc (char **exp_ptr)
 {
-  struct symtabs_and_lines values;
-  CORE_ADDR pc;
-  char *initial = *argptr;
-  
   if (current_program_space->executing_startup)
     /* The error message doesn't really matter, because this case
        should only hit during breakpoint reset.  */
     throw_error (NOT_FOUND_ERROR, _("cannot evaluate expressions while "
 				    "program space is in startup"));
 
-  (*argptr)++;
-  pc = value_as_address (parse_to_comma_and_eval (argptr));
-
-  values.sals = (struct symtab_and_line *)
-    xmalloc (sizeof (struct symtab_and_line));
-
-  values.nelts = 1;
-  values.sals[0] = find_pc_line (pc, 0);
-  values.sals[0].pc = pc;
-  values.sals[0].section = find_pc_overlay (pc);
-  values.sals[0].explicit_pc = 1;
-
-  if (self->canonical)
-    self->canonical->addr_string = savestring (initial, *argptr - initial);
-
-  return values;
-}
-
-
-
-/* Locate the first half of the linespec, ending in a colon, period,
-   or whitespace.  (More or less.)  Also, check to see if *ARGPTR is
-   enclosed in double quotes; if so, set is_quote_enclosed, advance
-   ARGPTR past that and zero out the trailing double quote.
-   If ARGPTR is just a simple name like "main", p will point to ""
-   at the end.  */
-
-static char *
-locate_first_half (char **argptr, int *is_quote_enclosed)
-{
-  char *ii;
-  char *p, *p1;
-  int has_comma;
-
-  /* Check if the linespec starts with an Ada operator (such as "+",
-     or ">", for instance).  */
-  p = *argptr;
-  if (p[0] == '"'
-      && current_language->la_language == language_ada)
-    {
-      const struct ada_opname_map *op;
-
-      for (op = ada_opname_table; op->encoded != NULL; op++)
-        if (strncmp (op->decoded, p, strlen (op->decoded)) == 0)
-	  break;
-      if (op->encoded != NULL)
-	{
-	  *is_quote_enclosed = 0;
-	  return p + strlen (op->decoded);
-	}
-    }
-
-  /* Maybe we were called with a line range FILENAME:LINENUM,FILENAME:LINENUM
-     and we must isolate the first half.  Outer layers will call again later
-     for the second half.
-
-     Don't count commas that appear in argument lists of overloaded
-     functions, or in quoted strings.  It's stupid to go to this much
-     trouble when the rest of the function is such an obvious roach hotel.  */
-  ii = find_toplevel_char (*argptr, ',');
-  has_comma = (ii != 0);
-
-  /* Temporarily zap out second half to not confuse the code below.
-     This is undone below.  Do not change ii!!  */
-  if (has_comma)
-    {
-      *ii = '\0';
-    }
-
-  /* Maybe arg is FILE : LINENUM or FILE : FUNCTION.  May also be
-     CLASS::MEMBER, or NAMESPACE::NAME.  Look for ':', but ignore
-     inside of <>.  */
-
-  p = *argptr;
-  if (p[0] == '"')
-    {
-      *is_quote_enclosed = 1;
-      (*argptr)++;
-      p++;
-    }
-  else
-    {
-      *is_quote_enclosed = 0;
-      if (strchr (get_gdb_completer_quote_characters (), *p))
-	{
-	  ++(*argptr);
-	  ++p;
-	}
-    }
-
-
-  /* Check for a drive letter in the filename.  This is done on all hosts
-     to capture cross-compilation environments.  On Unixen, directory
-     separators are illegal in filenames, so if the user enters "e:/foo.c",
-     he is referring to a directory named "e:" and a source file named
-     "foo.c", and we still want to keep these two pieces together.  */
-  if (isalpha (p[0]) && p[1] == ':' && IS_DIR_SEPARATOR (p[2]))
-    p += 3;
-
-  for (; *p; p++)
-    {
-      if (p[0] == '<')
-	{
-	  char *temp_end = find_template_name_end (p);
-
-	  if (!temp_end)
-	    error (_("malformed template specification in command"));
-	  p = temp_end;
-	}
-
-      if (p[0] == '(')
-	p = find_method_overload_end (p);
-
-      /* Check for a colon and a plus or minus and a [ (which
-         indicates an Objective-C method).  */
-      if (is_objc_method_format (p))
-	{
-	  break;
-	}
-      /* Check for the end of the first half of the linespec.  End of
-         line, a tab, a colon or a space.  But if enclosed in double
-	 quotes we do not break on enclosed spaces.  */
-      if (!*p
-	  || p[0] == '\t'
-	  || (p[0] == ':')
-	  || ((p[0] == ' ') && !*is_quote_enclosed))
-	break;
-      if (p[0] == '.' && strchr (p, ':') == NULL)
-	{
-	  /* Java qualified method.  Find the *last* '.', since the
-	     others are package qualifiers.  Stop at any open parenthesis
-	     which might provide overload information.  */
-	  for (p1 = p; *p1 && *p1 != '('; p1++)
-	    {
-	      if (*p1 == '.')
-		p = p1;
-	    }
-	  break;
-	}
-    }
-  p = skip_spaces (p);
-
-  /* If the closing double quote was left at the end, remove it.  */
-  if (*is_quote_enclosed)
-    {
-      char *closing_quote = strchr (p - 1, '"');
-
-      if (closing_quote && closing_quote[1] == '\0')
-	*closing_quote = '\0';
-    }
-
-  /* Now that we've safely parsed the first half, put back ',' so
-     outer layers can see it.  */
-  if (has_comma)
-    *ii = ',';
-
-  return p;
+  (*exp_ptr)++;
+  return value_as_address (parse_to_comma_and_eval (exp_ptr));
 }
 
 
@@ -1553,28 +2285,34 @@ locate_first_half (char **argptr, int *is_quote_enclosed)
    the existing C++ code to let the user choose one.  */
 
 static struct symtabs_and_lines
-decode_objc (struct linespec_state *self, char **argptr)
+decode_objc (struct linespec_state *self, linespec_t ls, char **argptr)
 {
   struct collect_info info;
   VEC (const_char_ptr) *symbol_names = NULL;
+  struct symtabs_and_lines values;
   char *new_argptr;
   struct cleanup *cleanup = make_cleanup (VEC_cleanup (const_char_ptr),
 					  &symbol_names);
 
   info.state = self;
-  info.result.sals = NULL;
-  info.result.nelts = 0;
+  info.file_symtabs = NULL;
+  VEC_safe_push (symtab_p, info.file_symtabs, NULL);
+  info.result.symbols = NULL;
+  info.result.minimal_symbols = NULL;
+  values.nelts = 0;
+  values.sals = NULL;
 
   new_argptr = find_imps (*argptr, &symbol_names); 
   if (VEC_empty (const_char_ptr, symbol_names))
     {
       do_cleanups (cleanup);
-      return info.result;
+      return values;
     }
 
   add_all_symbol_names_from_pspace (&info, NULL, symbol_names);
 
-  if (info.result.nelts > 0)
+  if (!VEC_empty (symbolp, info.result.symbols)
+      || !VEC_empty (minsym_and_objfile_d, info.result.minimal_symbols))
     {
       char *saved_arg;
 
@@ -1582,12 +2320,16 @@ decode_objc (struct linespec_state *self, char **argptr)
       memcpy (saved_arg, *argptr, new_argptr - *argptr);
       saved_arg[new_argptr - *argptr] = '\0';
 
+      ls->function_symbols = info.result.symbols;
+      ls->minimal_symbols = info.result.minimal_symbols;
+      values = convert_linespec_to_sals (self, ls);
+
       if (self->canonical)
 	{
 	  self->canonical->pre_expanded = 1;
-	  if (self->user_filename)
+	  if (ls->source_filename)
 	    self->canonical->addr_string
-	      = xstrprintf ("%s:%s", self->user_filename, saved_arg);
+	      = xstrprintf ("%s:%s", ls->source_filename, saved_arg);
 	  else
 	    self->canonical->addr_string = xstrdup (saved_arg);
 	}
@@ -1596,241 +2338,8 @@ decode_objc (struct linespec_state *self, char **argptr)
   *argptr = new_argptr;
 
   do_cleanups (cleanup);
-  return info.result;
-}
-
-/* This handles C++ and Java compound data structures.  P should point
-   at the first component separator, i.e. double-colon or period.  As
-   an example, on entrance to this function we could have ARGPTR
-   pointing to "AAA::inA::fun" and P pointing to "::inA::fun".  */
-
-static struct symtabs_and_lines
-decode_compound (struct linespec_state *self,
-		 char **argptr, char *the_real_saved_arg, char *p)
-{
-  struct symtabs_and_lines values;
-  char *p2;
-  char *saved_arg2 = *argptr;
-  char *temp_end;
-  struct symbol *sym;
-  char *copy;
-  VEC (symbolp) *sym_classes;
-  char *saved_arg, *class_name;
-  struct cleanup *cleanup = make_cleanup (null_cleanup, NULL);
-
-  /* If the user specified any completer quote characters in the input,
-     strip them.  They are superfluous.  */
-  saved_arg = alloca (strlen (the_real_saved_arg) + 1);
-  {
-    char *dst = saved_arg;
-    char *src = the_real_saved_arg;
-    char *quotes = get_gdb_completer_quote_characters ();
-    while (*src != '\0')
-      {
-	if (strchr (quotes, *src) == NULL)
-	  *dst++ = *src;
-	++src;
-      }
-    *dst = '\0';
-  }
-
-  /* First check for "global" namespace specification, of the form
-     "::foo".  If found, skip over the colons and jump to normal
-     symbol processing.  I.e. the whole line specification starts with
-     "::" (note the condition that *argptr == p).  */
-  if (p[0] == ':' 
-      && ((*argptr == p) || (p[-1] == ' ') || (p[-1] == '\t')))
-    saved_arg2 += 2;
-
-  /* Given our example "AAA::inA::fun", we have two cases to consider:
-
-     1) AAA::inA is the name of a class.  In that case, presumably it
-        has a method called "fun"; we then look up that method using
-        find_method.
-
-     2) AAA::inA isn't the name of a class.  In that case, either the
-        user made a typo, AAA::inA is the name of a namespace, or it is
-        the name of a minimal symbol.
-	In this case we just delegate to decode_variable.
-
-     Thus, our first task is to find everything before the last set of
-     double-colons and figure out if it's the name of a class.  So we
-     first loop through all of the double-colons.  */
-
-  p2 = p;		/* Save for restart.  */
-
-  /* This is very messy.  Following the example above we have now the
-     following pointers:
-     p -> "::inA::fun"
-     argptr -> "AAA::inA::fun
-     saved_arg -> "AAA::inA::fun
-     saved_arg2 -> "AAA::inA::fun
-     p2 -> "::inA::fun".  */
-
-  /* In the loop below, with these strings, we'll make 2 passes, each
-     is marked in comments.  */
-
-  while (1)
-    {
-      static char *break_characters = " \t(";
-
-      /* Move pointer up to next possible class/namespace token.  */
-
-      p = p2 + 1;	/* Restart with old value +1.  */
-
-      /* PASS1: at this point p2->"::inA::fun", so p->":inA::fun",
-	 i.e. if there is a double-colon, p will now point to the
-	 second colon.  */
-      /* PASS2: p2->"::fun", p->":fun" */
-
-      /* Move pointer ahead to next double-colon.  */
-      while (*p
-	     && strchr (break_characters, *p) == NULL
-	     && strchr (get_gdb_completer_quote_characters (), *p) == NULL)
-	{
-	  if (current_language->la_language == language_cplus)
-	    p += cp_validate_operator (p);
-
-	  if (p[0] == '<')
-	    {
-	      temp_end = find_template_name_end (p);
-	      if (!temp_end)
-		error (_("malformed template specification in command"));
-	      p = temp_end;
-	    }
-	  /* Note that, since, at the start of this loop, p would be
-	     pointing to the second colon in a double-colon, we only
-	     satisfy the condition below if there is another
-	     double-colon to the right (after).  I.e. there is another
-	     component that can be a class or a namespace.  I.e, if at
-	     the beginning of this loop (PASS1), we had
-	     p->":inA::fun", we'll trigger this when p has been
-	     advanced to point to "::fun".  */
-	  /* PASS2: we will not trigger this.  */
-	  else if ((p[0] == ':') && (p[1] == ':'))
-	    break;	/* Found double-colon.  */
-	  else
-	    {
-	      /* PASS2: We'll keep getting here, until P points to one of the
-		 break characters, at which point we exit this loop.  */
-	      if (*p)
-		{
-		  if (p[1] == '('
-		      && strncmp (&p[1], CP_ANONYMOUS_NAMESPACE_STR,
-				  CP_ANONYMOUS_NAMESPACE_LEN) == 0)
-		    p += CP_ANONYMOUS_NAMESPACE_LEN;
-		  else if (strchr (break_characters, *p) == NULL)
-		    ++p;
-		}
-	    }
-	}
-
-      if (*p != ':')
-	break;		/* Out of the while (1).  This would happen
-			   for instance if we have looked up
-			   unsuccessfully all the components of the
-			   string, and p->""(PASS2).  */
-
-      /* We get here if p points to one of the break characters or "" (i.e.,
-	 string ended).  */
-      /* Save restart for next time around.  */
-      p2 = p;
-      /* Restore argptr as it was on entry to this function.  */
-      *argptr = saved_arg2;
-      /* PASS1: at this point p->"::fun" argptr->"AAA::inA::fun",
-	 p2->"::fun".  */
-
-      /* All ready for next pass through the loop.  */
-    }			/* while (1) */
-
-
-  /* Start of lookup in the symbol tables.  */
-
-  /* Lookup in the symbol table the substring between argptr and
-     p.  Note, this call changes the value of argptr.  */
-  /* Before the call, argptr->"AAA::inA::fun",
-     p->"", p2->"::fun".  After the call: argptr->"fun", p, p2
-     unchanged.  */
-  sym_classes = lookup_prefix_sym (argptr, p2, self->file_symtabs,
-				   &class_name);
-  make_cleanup (VEC_cleanup (symbolp), &sym_classes);
-  make_cleanup (xfree, class_name);
-
-  /* If a class has been found, then we're in case 1 above.  So we
-     look up "fun" as a method of those classes.  */
-  if (!VEC_empty (symbolp, sym_classes))
-    {
-      /* Arg token is not digits => try it as a function name.
-	 Find the next token (everything up to end or next
-	 blank).  */
-      if (**argptr
-	  && strchr (get_gdb_completer_quote_characters (),
-		     **argptr) != NULL)
-	{
-	  p = skip_quoted (*argptr);
-	  *argptr = *argptr + 1;
-	}
-      else
-	{
-	  /* At this point argptr->"fun".  */
-	  char *a;
-
-	  p = *argptr;
-	  while (*p && *p != ' ' && *p != '\t' && *p != ',' && *p != ':'
-		 && *p != '(')
-	    p++;
-	  /* At this point p->"".  String ended.  */
-	  /* Nope, C++ operators could have spaces in them
-	     ("foo::operator <" or "foo::operator delete []").
-	     I apologize, this is a bit hacky...  */
-	  if (current_language->la_language == language_cplus
-	      && *p == ' ' && p - 8 - *argptr + 1 > 0)
-	    {
-	      /* The above loop has already swallowed "operator".  */
-	      p += cp_validate_operator (p - 8) - 8;
-	    }
-
-	  /* Keep any important naming information.  */
-	  p = keep_name_info (p, 1);
-	}
-
-      /* Allocate our own copy of the substring between argptr and
-	 p.  */
-      copy = (char *) alloca (p - *argptr + 1);
-      memcpy (copy, *argptr, p - *argptr);
-      copy[p - *argptr] = '\0';
-      if (p != *argptr
-	  && copy[p - *argptr - 1]
-	  && strchr (get_gdb_completer_quote_characters (),
-		     copy[p - *argptr - 1]) != NULL)
-	copy[p - *argptr - 1] = '\0';
-
-      /* At this point copy->"fun", p->"".  */
-
-      /* No line number may be specified.  */
-      *argptr = skip_spaces (p);
-      /* At this point arptr->"".  */
-
-      /* Look for copy as a method of sym_class.  */
-      /* At this point copy->"fun", sym_class is "AAA:inA",
-	 saved_arg->"AAA::inA::fun".  This concludes the scanning of
-	 the string for possible components matches.  If we find it
-	 here, we return.  If not, and we are at the and of the string,
-	 we'll lookup the whole string in the symbol tables.  */
-
-      values = find_method (self, saved_arg, copy, class_name, sym_classes);
-
-      do_cleanups (cleanup);
-      return values;
-    } /* End if symbol found.  */
-
 
-  /* We couldn't find a class, so we're in case 2 above.  We check the
-     entire name as a symbol instead.  The simplest way to do this is
-     to just throw an exception and let our caller fall through to
-     decode_variable.  */
-
-  throw_error (NOT_FOUND_ERROR, _("see caller, this text doesn't matter"));
+  return values;
 }
 
 /* An instance of this type is used when collecting prefix symbols for
@@ -1876,46 +2385,20 @@ collect_one_symbol (struct symbol *sym, void *d)
   return 1; /* Continue iterating.  */
 }
 
-/* Return the symbol corresponding to the substring of *ARGPTR ending
-   at P, allowing whitespace.  Also, advance *ARGPTR past the symbol
-   name in question, the compound object separator ("::" or "."), and
-   whitespace.  Note that *ARGPTR is changed whether or not the
-   this call finds anything (i.e we return NULL).  As an
-   example, say ARGPTR is "AAA::inA::fun" and P is "::inA::fun".  */
+/* Return any symbols corresponding to CLASS_NAME in FILE_SYMTABS.  */
 
 static VEC (symbolp) *
-lookup_prefix_sym (char **argptr, char *p, VEC (symtab_p) *file_symtabs,
-		   char **class_name)
+lookup_prefix_sym (struct linespec_state *state, VEC (symtab_p) *file_symtabs,
+		   const char *class_name)
 {
-  char *p1;
-  char *copy;
   int ix;
   struct symtab *elt;
   struct decode_compound_collector collector;
   struct cleanup *outer;
   struct cleanup *cleanup;
-  struct block *search_block;
-
-  /* Extract the class name.  */
-  p1 = p;
-  while (p != *argptr && p[-1] == ' ')
-    --p;
-  copy = (char *) xmalloc (p - *argptr + 1);
-  memcpy (copy, *argptr, p - *argptr);
-  copy[p - *argptr] = 0;
-  *class_name = copy;
-  outer = make_cleanup (xfree, copy);
-
-  /* Discard the class name from the argptr.  */
-  p = p1 + (p1[0] == ':' ? 2 : 1);
-  p = skip_spaces (p);
-  *argptr = p;
-
-  /* At this point p1->"::inA::fun", p->"inA::fun" copy->"AAA",
-     argptr->"inA::fun".  */
 
   collector.symbols = NULL;
-  make_cleanup (VEC_cleanup (symbolp), &collector.symbols);
+  outer = make_cleanup (VEC_cleanup (symbolp), &collector.symbols);
 
   collector.unique_syms = htab_create_alloc (1, htab_hash_pointer,
 					     htab_eq_pointer, NULL,
@@ -1926,10 +2409,10 @@ lookup_prefix_sym (char **argptr, char *p, VEC (symtab_p) *file_symtabs,
     {
       if (elt == NULL)
 	{
-	  iterate_over_all_matching_symtabs (copy, STRUCT_DOMAIN,
+	  iterate_over_all_matching_symtabs (class_name, STRUCT_DOMAIN,
 					     collect_one_symbol, &collector,
 					     NULL, 0);
-	  iterate_over_all_matching_symtabs (copy, VAR_DOMAIN,
+	  iterate_over_all_matching_symtabs (class_name, VAR_DOMAIN,
 					     collect_one_symbol, &collector,
 					     NULL, 0);
 	}
@@ -1942,9 +2425,9 @@ lookup_prefix_sym (char **argptr, char *p, VEC (symtab_p) *file_symtabs,
 	  gdb_assert (!SYMTAB_PSPACE (elt)->executing_startup);
 	  set_current_program_space (SYMTAB_PSPACE (elt));
 	  search_block = get_search_block (elt);
-	  LA_ITERATE_OVER_SYMBOLS (search_block, copy, STRUCT_DOMAIN,
+	  LA_ITERATE_OVER_SYMBOLS (search_block, class_name, STRUCT_DOMAIN,
 				   collect_one_symbol, &collector);
-	  LA_ITERATE_OVER_SYMBOLS (search_block, copy, VAR_DOMAIN,
+	  LA_ITERATE_OVER_SYMBOLS (search_block, class_name, VAR_DOMAIN,
 				   collect_one_symbol, &collector);
 	}
     }
@@ -1959,14 +2442,42 @@ lookup_prefix_sym (char **argptr, char *p, VEC (symtab_p) *file_symtabs,
    symbols with the same program space end up next to each other.  */
 
 static int
-compare_symbols (const void *a, const void *b)
+compare_symbols (const void *a, const void *b)
+{
+  struct symbol * const *sa = a;
+  struct symbol * const *sb = b;
+  uintptr_t uia, uib;
+
+  uia = (uintptr_t) SYMTAB_PSPACE (SYMBOL_SYMTAB (*sa));
+  uib = (uintptr_t) SYMTAB_PSPACE (SYMBOL_SYMTAB (*sb));
+
+  if (uia < uib)
+    return -1;
+  if (uia > uib)
+    return 1;
+
+  uia = (uintptr_t) *sa;
+  uib = (uintptr_t) *sb;
+
+  if (uia < uib)
+    return -1;
+  if (uia > uib)
+    return 1;
+
+  return 0;
+}
+
+/* Like compare_symbols but for minimal symbols.  */
+
+static int
+compare_msymbols (const void *a, const void *b)
 {
-  struct symbol * const *sa = a;
-  struct symbol * const *sb = b;
+  struct minimal_symbol * const *sa = a;
+  struct minimal_symbol * const *sb = b;
   uintptr_t uia, uib;
 
-  uia = (uintptr_t) SYMTAB_PSPACE (SYMBOL_SYMTAB (*sa));
-  uib = (uintptr_t) SYMTAB_PSPACE (SYMBOL_SYMTAB (*sb));
+  uia = (uintptr_t) SYMBOL_OBJ_SECTION (*sa)->objfile->pspace;
+  uib = (uintptr_t) SYMBOL_OBJ_SECTION (*sb)->objfile->pspace;
 
   if (uia < uib)
     return -1;
@@ -2031,14 +2542,16 @@ find_superclass_methods (VEC (typep) *superclasses,
   do_cleanups (cleanup);
 }
 
-/* This finds the method COPY in the class whose type is given by one
-   of the symbols in SYM_CLASSES.  */
+/* This finds the method METHOD_NAME in the class CLASS_NAME whose type is
+   given by one of the symbols in SYM_CLASSES.  Matches are returned
+   in SYMBOLS (for debug symbols) and MINSYMS (for minimal symbols).  */
 
-static struct symtabs_and_lines
-find_method (struct linespec_state *self, char *saved_arg,
-	     char *copy, const char *class_name, VEC (symbolp) *sym_classes)
+static void
+find_method (struct linespec_state *self, VEC (symtab_p) *file_symtabs,
+	     const char *class_name, const char *method_name,
+	     VEC (symbolp) *sym_classes, VEC (symbolp) **symbols,
+	     VEC (minsym_and_objfile_d) **minsyms)
 {
-  char *canon;
   struct symbol *sym;
   struct cleanup *cleanup = make_cleanup (null_cleanup, NULL);
   int ix;
@@ -2048,15 +2561,6 @@ find_method (struct linespec_state *self, char *saved_arg,
   struct collect_info info;
   char *name_iter;
 
-  /* NAME is typed by the user: it needs to be canonicalized before
-     searching the symbol tables.  */
-  canon = cp_canonicalize_string_no_typedefs (copy);
-  if (canon != NULL)
-    {
-      copy = canon;
-      make_cleanup (xfree, copy);
-    }
-
   /* Sort symbols so that symbols with the same program space are next
      to each other.  */
   qsort (VEC_address (symbolp, sym_classes),
@@ -2065,11 +2569,12 @@ find_method (struct linespec_state *self, char *saved_arg,
 	 compare_symbols);
 
   info.state = self;
-  info.result.sals = NULL;
-  info.result.nelts = 0;
+  info.file_symtabs = file_symtabs;
+  info.result.symbols = NULL;
+  info.result.minimal_symbols = NULL;
 
   /* Iterate over all the types, looking for the names of existing
-     methods matching COPY.  If we cannot find a direct method in a
+     methods matching METHOD_NAME.  If we cannot find a direct method in a
      given program space, then we consider inherited methods; this is
      not ideal (ideal would be to respect C++ hiding rules), but it
      seems good enough and is what GDB has historically done.  We only
@@ -2093,7 +2598,7 @@ find_method (struct linespec_state *self, char *saved_arg,
       pspace = SYMTAB_PSPACE (SYMBOL_SYMTAB (sym));
       set_current_program_space (pspace);
       t = check_typedef (SYMBOL_TYPE (sym));
-      find_methods (t, copy, &result_names, &superclass_vec);
+      find_methods (t, method_name, &result_names, &superclass_vec);
 
       /* Handle all items from a single program space at once; and be
 	 sure not to miss the last batch.  */
@@ -2105,7 +2610,8 @@ find_method (struct linespec_state *self, char *saved_arg,
 	  /* If we did not find a direct implementation anywhere in
 	     this program space, consider superclasses.  */
 	  if (VEC_length (const_char_ptr, result_names) == last_result_len)
-	    find_superclass_methods (superclass_vec, copy, &result_names);
+	    find_superclass_methods (superclass_vec, method_name,
+				     &result_names);
 
 	  /* We have a list of candidate symbol names, so now we
 	     iterate over the symbol tables looking for all
@@ -2117,31 +2623,18 @@ find_method (struct linespec_state *self, char *saved_arg,
 	}
     }
 
-  if (info.result.nelts > 0)
+  if (!VEC_empty (symbolp, info.result.symbols)
+      || !VEC_empty (minsym_and_objfile_d, info.result.minimal_symbols))
     {
-      if (self->canonical)
-	{
-	  self->canonical->pre_expanded = 1;
-	  if (self->user_filename)
-	    self->canonical->addr_string
-	      = xstrprintf ("%s:%s", self->user_filename, saved_arg);
-	  else
-	    self->canonical->addr_string = xstrdup (saved_arg);
-	}
-
+      *symbols = info.result.symbols;
+      *minsyms = info.result.minimal_symbols;
       do_cleanups (cleanup);
-
-      return info.result;
+      return;
     }
 
-  if (copy[0] == '~')
-    cplusplus_error (saved_arg,
-		     "the class `%s' does not have destructor defined\n",
-		     class_name);
-  else
-    cplusplus_error (saved_arg,
-		     "the class %s does not have any method named %s\n",
-		     class_name, copy);
+  /* Throw an NOT_FOUND_ERROR.  This will be caught by the caller
+     and other attempts to locate the symbol will be made.  */
+  throw_error (NOT_FOUND_ERROR, _("see caller, this text doesn't matter"));
 }
 
 
@@ -2203,488 +2696,192 @@ collect_symtabs_from_filename (const char *file)
   return collector.symtabs;
 }
 
-/* Return all the symtabs associated to the filename given by the
-   substring of *ARGPTR ending at P, and advance ARGPTR past that
-   filename.  */
-
-static VEC (symtab_p) *
-symtabs_from_filename (char **argptr, char *p, int is_quote_enclosed,
-		       char **user_filename)
-{
-  char *p1;
-  char *copy;
-  struct cleanup *outer;
-  VEC (symtab_p) *result;
-  
-  p1 = p;
-  while (p != *argptr && p[-1] == ' ')
-    --p;
-  if ((*p == '"') && is_quote_enclosed)
-    --p;
-  copy = xmalloc (p - *argptr + 1);
-  outer = make_cleanup (xfree, copy);
-  memcpy (copy, *argptr, p - *argptr);
-  /* It may have the ending quote right after the file name.  */
-  if ((is_quote_enclosed && copy[p - *argptr - 1] == '"')
-      || copy[p - *argptr - 1] == '\'')
-    copy[p - *argptr - 1] = 0;
-  else
-    copy[p - *argptr] = 0;
-
-  result = collect_symtabs_from_filename (copy);
-
-  if (VEC_empty (symtab_p, result))
-    {
-      if (!have_full_symbols () && !have_partial_symbols ())
-	throw_error (NOT_FOUND_ERROR,
-		     _("No symbol table is loaded.  "
-		       "Use the \"file\" command."));
-      throw_error (NOT_FOUND_ERROR, _("No source file named %s."), copy);
-    }
-
-  /* Discard the file name from the arg.  */
-  if (*p1 == '\0')
-    *argptr = p1;
-  else
-    *argptr = skip_spaces (p1 + 1);
-
-  discard_cleanups (outer);
-  *user_filename = copy;
-  return result;
-}
-
-/* A callback used by iterate_over_all_matching_symtabs that collects
-   symbols for find_function_symbols.  */
-
-static int
-collect_function_symbols (struct symbol *sym, void *arg)
-{
-  VEC (symbolp) **syms = arg;
-
-  if (SYMBOL_CLASS (sym) == LOC_BLOCK)
-    VEC_safe_push (symbolp, *syms, sym);
-
-  return 1; /* Continue iterating.  */
-}
-
-/* Look up a function symbol in *ARGPTR.  If found, advance *ARGPTR
-   and return the symbol.  If not found, return NULL.  */
-
-static VEC (symbolp) *
-find_function_symbols (char **argptr, char *p, int is_quote_enclosed,
-		       char **user_function)
-{
-  char *p1;
-  char *copy;
-  VEC (symbolp) *result = NULL;
-
-  p1 = p;
-  while (p != *argptr && p[-1] == ' ')
-    --p;
-  if ((*p == '"') && is_quote_enclosed)
-    --p;
-  copy = (char *) xmalloc (p - *argptr + 1);
-  *user_function = copy;
-  memcpy (copy, *argptr, p - *argptr);
-  /* It may have the ending quote right after the file name.  */
-  if ((is_quote_enclosed && copy[p - *argptr - 1] == '"')
-      || copy[p - *argptr - 1] == '\'')
-    copy[p - *argptr - 1] = 0;
-  else
-    copy[p - *argptr] = 0;
-
-  iterate_over_all_matching_symtabs (copy, VAR_DOMAIN,
-				     collect_function_symbols, &result, NULL,
-				     0);
-
-  if (VEC_empty (symbolp, result))
-    VEC_free (symbolp, result);
-  else
-    {
-      /* Discard the file name from the arg.  */
-      *argptr = skip_spaces (p1 + 1);
-    }
-
-  return result;
-}
-
-
-
-/* A helper for decode_all_digits that handles the 'list_mode' case.  */
-
-static void
-decode_digits_list_mode (struct linespec_state *self,
-			 struct symtabs_and_lines *values,
-			 struct symtab_and_line val)
-{
-  int ix;
-  struct symtab *elt;
-
-  gdb_assert (self->list_mode);
-
-  for (ix = 0; VEC_iterate (symtab_p, self->file_symtabs, ix, elt); ++ix)
-    {
-      /* The logic above should ensure this.  */
-      gdb_assert (elt != NULL);
-
-      set_current_program_space (SYMTAB_PSPACE (elt));
-
-      /* Simplistic search just for the list command.  */
-      val.symtab = find_line_symtab (elt, val.line, NULL, NULL);
-      if (val.symtab == NULL)
-	val.symtab = elt;
-      val.pspace = SYMTAB_PSPACE (elt);
-      val.pc = 0;
-      val.explicit_line = 1;
-
-      add_sal_to_sals (self, values, &val, NULL);
-    }
-}
-
-/* A helper for decode_all_digits that iterates over the symtabs,
-   adding lines to the VEC.  */
-
-static void
-decode_digits_ordinary (struct linespec_state *self,
-			int line,
-			struct symtabs_and_lines *sals,
-			struct linetable_entry **best_entry)
-{
-  int ix;
-  struct symtab *elt;
-
-  for (ix = 0; VEC_iterate (symtab_p, self->file_symtabs, ix, elt); ++ix)
-    {
-      int i;
-      VEC (CORE_ADDR) *pcs;
-      CORE_ADDR pc;
-
-      /* The logic above should ensure this.  */
-      gdb_assert (elt != NULL);
-
-      set_current_program_space (SYMTAB_PSPACE (elt));
-
-      pcs = find_pcs_for_symtab_line (elt, line, best_entry);
-      for (i = 0; VEC_iterate (CORE_ADDR, pcs, i, pc); ++i)
-	{
-	  struct symtab_and_line sal;
-
-	  init_sal (&sal);
-	  sal.pspace = SYMTAB_PSPACE (elt);
-	  sal.symtab = elt;
-	  sal.line = line;
-	  sal.pc = pc;
-	  add_sal_to_sals_basic (sals, &sal);
-	}
-
-      VEC_free (CORE_ADDR, pcs);
-    }
-}
-
-/* This decodes a line where the argument is all digits (possibly
-   preceded by a sign).  Q should point to the end of those digits;
-   the other arguments are as usual.  */
-
-static struct symtabs_and_lines
-decode_all_digits (struct linespec_state *self,
-		   char **argptr,
-		   char *q)
-{
-  struct symtabs_and_lines values;
-  struct symtab_and_line val;
-  int use_default = 0;
-  char *saved_arg = *argptr;
-
-  enum sign
-    {
-      none, plus, minus
-    }
-  sign = none;
-
-  init_sal (&val);
-  values.sals = NULL;
-  values.nelts = 0;
-
-  /* This is where we need to make sure that 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 (VEC_length (symtab_p, self->file_symtabs) == 1
-      && VEC_index (symtab_p, self->file_symtabs, 0) == NULL)
-    {
-      set_current_program_space (self->program_space);
-
-      /* Make sure we have at least a default source file.  */
-      set_default_source_symtab_and_line ();
-      initialize_defaults (&self->default_symtab, &self->default_line);
-      VEC_pop (symtab_p, self->file_symtabs);
-      VEC_free (symtab_p, self->file_symtabs);
-      self->file_symtabs
-	= collect_symtabs_from_filename (self->default_symtab->filename);
-      use_default = 1;
-    }
-
-  if (**argptr == '+')
-    sign = plus, (*argptr)++;
-  else if (**argptr == '-')
-    sign = minus, (*argptr)++;
-  val.line = atoi (*argptr);
-  switch (sign)
-    {
-    case plus:
-      if (q == *argptr)
-	val.line = 5;
-      if (use_default)
-	val.line = self->default_line + val.line;
-      break;
-    case minus:
-      if (q == *argptr)
-	val.line = 15;
-      if (use_default)
-	val.line = self->default_line - val.line;
-      else
-	val.line = 1;
-      break;
-    case none:
-      break;		/* No need to adjust val.line.  */
-    }
-
-  *argptr = skip_spaces (q);
-
-  if (self->list_mode)
-    decode_digits_list_mode (self, &values, val);
-  else
-    {
-      struct linetable_entry *best_entry = NULL;
-      int *filter;
-      struct block **blocks;
-      struct cleanup *cleanup;
-      struct symtabs_and_lines intermediate_results;
-      int i, j;
-
-      intermediate_results.sals = NULL;
-      intermediate_results.nelts = 0;
-
-      decode_digits_ordinary (self, val.line, &intermediate_results,
-			      &best_entry);
-      if (intermediate_results.nelts == 0 && best_entry != NULL)
-	decode_digits_ordinary (self, best_entry->line, &intermediate_results,
-				&best_entry);
-
-      cleanup = make_cleanup (xfree, intermediate_results.sals);
-
-      /* For optimized code, compiler can scatter one source line
-	 accross disjoint ranges of PC values, even when no duplicate
-	 functions or inline functions are involved.  For example,
-	 'for (;;)' inside non-template non-inline non-ctor-or-dtor
-	 function can result in two PC ranges.  In this case, we don't
-	 want to set breakpoint on 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.  */
-
-      filter = xmalloc (intermediate_results.nelts * sizeof (int));
-      make_cleanup (xfree, filter);
-      blocks = xmalloc (intermediate_results.nelts * sizeof (struct block *));
-      make_cleanup (xfree, blocks);
-
-      for (i = 0; i < intermediate_results.nelts; ++i)
-	{
-	  set_current_program_space (intermediate_results.sals[i].pspace);
-
-	  filter[i] = 1;
-	  blocks[i] = block_for_pc_sect (intermediate_results.sals[i].pc,
-					 intermediate_results.sals[i].section);
-	}
-
-      for (i = 0; i < intermediate_results.nelts; ++i)
-	{
-	  if (blocks[i] != NULL)
-	    for (j = i + 1; j < intermediate_results.nelts; ++j)
-	      {
-		if (blocks[j] == blocks[i])
-		  {
-		    filter[j] = 0;
-		    break;
-		  }
-	      }
-	}
-
-      for (i = 0; i < intermediate_results.nelts; ++i)
-	if (filter[i])
-	  {
-	    struct symbol *sym = (blocks[i]
-				  ? block_containing_function (blocks[i])
-				  : NULL);
-
-	    if (self->funfirstline)
-	      skip_prologue_sal (&intermediate_results.sals[i]);
-	    /* Make sure the line matches the request, not what was
-	       found.  */
-	    intermediate_results.sals[i].line = val.line;
-	    add_sal_to_sals (self, &values, &intermediate_results.sals[i],
-			     sym ? SYMBOL_NATURAL_NAME (sym) : NULL);
-	  }
-
-      do_cleanups (cleanup);
-    }
-
-  if (values.nelts == 0)
-    {
-      if (self->user_filename)
-	throw_error (NOT_FOUND_ERROR, _("No line %d in file \"%s\"."),
-		     val.line, self->user_filename);
-      else
-	throw_error (NOT_FOUND_ERROR, _("No line %d in the current file."),
-		     val.line);
-    }
+/* Return all the symtabs associated to the FILENAME.  */
 
-  if (self->canonical)
-    {
-      char *copy = savestring (saved_arg, q - saved_arg);
+static VEC (symtab_p) *
+symtabs_from_filename (const char *filename)
+{
+  VEC (symtab_p) *result;
+  
+  result = collect_symtabs_from_filename (filename);
 
-      self->canonical->pre_expanded = 1;
-      gdb_assert (self->user_filename || use_default);
-      self->canonical->addr_string
-	= xstrprintf ("%s:%s", (self->user_filename
-				? self->user_filename
-				: self->default_symtab->filename),
-		      copy);
-      xfree (copy);
+  if (VEC_empty (symtab_p, result))
+    {
+      if (!have_full_symbols () && !have_partial_symbols ())
+	throw_error (NOT_FOUND_ERROR,
+		     _("No symbol table is loaded.  "
+		       "Use the \"file\" command."));
+      throw_error (NOT_FOUND_ERROR, _("No source file named %s."), filename);
     }
 
-  return values;
+  return result;
 }
 
-
-
-/* Decode a linespec starting with a dollar sign.  */
+/* Look up a function symbol named NAME in symtabs FILE_SYMTABS.  Matching
+   debug symbols are returned in SYMBOLS.  Matching minimal symbols are
+   returned in MINSYMS.  */
 
-static struct symtabs_and_lines
-decode_dollar (struct linespec_state *self, char *copy)
+static void
+find_function_symbols (struct linespec_state *state,
+		       VEC (symtab_p) *file_symtabs, const char *name,
+		       VEC (symbolp) **symbols,
+		       VEC (minsym_and_objfile_d) **minsyms)
 {
-  LONGEST valx;
-  int index = 0;
-  struct symtabs_and_lines values;
-  struct symtab_and_line val;
-  char *p;
-  struct symbol *sym;
-  struct minimal_symbol *msymbol;
-  int ix;
-  struct symtab *elt;
+  struct collect_info info;
+  VEC (const_char_ptr) *symbol_names = NULL;
+  struct cleanup *cleanup = make_cleanup (VEC_cleanup (const_char_ptr),
+					  &symbol_names);
 
-  p = (copy[1] == '$') ? copy + 2 : copy + 1;
-  while (*p >= '0' && *p <= '9')
-    p++;
-  if (!*p)		/* Reached end of token without hitting non-digit.  */
-    {
-      /* We have a value history reference.  */
-      struct value *val_history;
+  info.state = state;
+  info.result.symbols = NULL;
+  info.result.minimal_symbols = NULL;
+  info.file_symtabs = file_symtabs;
 
-      sscanf ((copy[1] == '$') ? copy + 2 : copy + 1, "%d", &index);
-      val_history = access_value_history ((copy[1] == '$') ? -index : index);
-      if (TYPE_CODE (value_type (val_history)) != TYPE_CODE_INT)
-	error (_("History values used in line "
-		 "specs must have integer values."));
-      valx = value_as_long (val_history);
+  /* Try NAME as an Objective-C selector.  */
+  find_imps ((char *) name, &symbol_names);
+  if (!VEC_empty (const_char_ptr, symbol_names))
+    add_all_symbol_names_from_pspace (&info, NULL, symbol_names);
+  else
+    add_matching_symbols_to_info (name, &info, NULL);
+
+  do_cleanups (cleanup);
+
+  if (VEC_empty (symbolp, info.result.symbols))
+    {
+      VEC_free (symbolp, info.result.symbols);
+      *symbols = NULL;
     }
   else
+    *symbols = info.result.symbols;
+
+  if (VEC_empty (minsym_and_objfile_d, info.result.minimal_symbols))
     {
-      /* Not all digits -- may be user variable/function or a
-	 convenience variable.  */
+      VEC_free (minsym_and_objfile_d, info.result.minimal_symbols);
+      *minsyms = NULL;
+    }
+  else
+    *minsyms = info.result.minimal_symbols;
+}
+
+/* Find all symbols named NAME in FILE_SYMTABS, returning debug symbols
+   in SYMBOLS and minimal symbols in MINSYMS.  */
 
-      volatile struct gdb_exception exc;
+void
+find_linespec_symbols (struct linespec_state *state,
+		       VEC (symtab_p) *file_symtabs,
+		       const char *name,
+		       VEC (symbolp) **symbols,
+		       VEC (minsym_and_objfile_d) **minsyms)
+{
+  char *klass, *method, *canon;
+  const char *lookup_name, *last, *p, *scope_op;
+  struct cleanup *cleanup;
+  VEC (symbolp) *classes;
+  volatile struct gdb_exception except;
 
-      /* Avoid "may be used uninitialized" warning.  */
-      values.sals = NULL;
-      values.nelts = 0;
+  cleanup = demangle_for_lookup (name, current_language->la_language,
+				 &lookup_name);
+  if (current_language->la_language == language_ada)
+    {
+      /* In Ada, the symbol lookups are performed using the encoded
+         name rather than the demangled name.  */
+      lookup_name = ada_name_for_lookup (name);
+      make_cleanup (xfree, (void *) lookup_name);
+    }
 
-      TRY_CATCH (exc, RETURN_MASK_ERROR)
-	{
-	  values = decode_variable (self, copy);
-	}
+  canon = cp_canonicalize_string_no_typedefs (lookup_name);
+  if (canon != NULL)
+    {
+      lookup_name = canon;
+      cleanup = make_cleanup (xfree, canon);
+    }
 
-      if (exc.reason == 0)
-	return values;
+  /* See if we can find a scope operator and break this symbol
+     name into namespaces${SCOPE_OPERATOR}class_name and method_name.  */
+  scope_op = "::";
+  p = find_toplevel_string (lookup_name, scope_op);
+  if (p == NULL)
+    {
+      /* No C++ scope operator.  Try Java.  */
+      scope_op = ".";
+      p = find_toplevel_string (lookup_name, scope_op);
+    }
 
-      if (exc.error != NOT_FOUND_ERROR)
-	throw_exception (exc);
+  last = NULL;
+  while (p != NULL)
+    {
+      last = p;
+      p = find_toplevel_string (p + strlen (scope_op), scope_op);
+    }
 
-      /* Not a user variable or function -- must be convenience variable.  */
-      if (!get_internalvar_integer (lookup_internalvar (copy + 1), &valx))
-	error (_("Convenience variables used in line "
-		 "specs must have integer values."));
+  /* If no scope operator was found, lookup the name as a symbol.  */
+  if (last == NULL)
+    {
+      find_function_symbols (state, file_symtabs, lookup_name,
+			     symbols, minsyms);
+      do_cleanups (cleanup);
+      return;
     }
 
-  init_sal (&val);
+  /* NAME points to the class name.
+     LAST points to the method name.  */
+  klass = xmalloc ((last - lookup_name + 1) * sizeof (char));
+  make_cleanup (xfree, klass);
+  strncpy (klass, lookup_name, last - lookup_name);
+  klass[last - lookup_name] = '\0';
 
-  values.sals = NULL;
-  values.nelts = 0;
+  /* Skip past the scope operator.  */
+  last += strlen (scope_op);
+  method = xmalloc ((strlen (last) + 1) * sizeof (char));
+  make_cleanup (xfree, method);
+  strcpy (method, last);
 
-  for (ix = 0; VEC_iterate (symtab_p, self->file_symtabs, ix, elt); ++ix)
+  /* Find a list of classes named KLASS.  */
+  classes = lookup_prefix_sym (state, file_symtabs, klass);
+  if (!VEC_empty (symbolp, classes))
     {
-      if (elt == NULL)
+      /* Now locate a list of suitable methods named METHOD.  */
+      TRY_CATCH (except, RETURN_MASK_ERROR)
 	{
-	  elt = self->default_symtab;
-	  set_current_program_space (self->program_space);
+	  find_method (state, file_symtabs, klass, method, classes,
+		       symbols, minsyms);
 	}
-      else
-	set_current_program_space (SYMTAB_PSPACE (elt));
-
-      /* Either history value or convenience value from above, in valx.  */
-      val.symtab = elt;
-      val.line = valx;
-      val.pc = 0;
-      val.pspace = elt ? SYMTAB_PSPACE (elt) : current_program_space;
-
-      add_sal_to_sals (self, &values, &val, NULL);
-    }
 
-  if (self->canonical)
-    {
-      self->canonical->pre_expanded = 1;
-      if (self->user_filename)
-	self->canonical->addr_string = xstrprintf ("%s:%s",
-						   self->user_filename, copy);
-      else
-	self->canonical->addr_string = xstrdup (copy);
+      /* If successful, we're done.  If NOT_FOUND_ERROR
+	 was not thrown, rethrow the exception that we did get.
+	 Otherwise, fall back to looking up the entire name as a symbol.
+	 This can happen with namespace::function.  */
+      if (except.reason >= 0)
+	{
+	  do_cleanups (cleanup);
+	  return;
+	}
+      else if (except.error != NOT_FOUND_ERROR)
+	throw_exception (except);
     }
 
-  return values;
+  /* We couldn't find a class, so we check the entire name as a symbol
+     instead.  */
+   find_function_symbols (state, file_symtabs, lookup_name, symbols, minsyms);
+   do_cleanups (cleanup);
 }
 
-
-
-/* A helper for decode_line_1 that tries to find a label.  The label
-   is searched for in the current block.
-   FUNCTION_SYMBOLS is a list of the enclosing functions; or NULL if none
-   specified.
-   COPY is the name of the label to find.
-   CANONICAL is the same as the "canonical" argument to decode_line_1.
-   RESULT is a pointer to a symtabs_and_lines structure which will be
-   filled in on success.
-   This function returns 1 if a label was found, 0 otherwise.  */
+/* Return all labels named NAME in FUNCTION_SYMBOLS.  Return the
+   actual function symbol in which the label was found in LABEL_FUNC_RET.  */
 
-static int
-decode_label (struct linespec_state *self,
-	      VEC (symbolp) *function_symbols, char *copy,
-	      struct symtabs_and_lines *result)
+static VEC (symbolp) *
+find_label_symbols (struct linespec_state *self,
+		    VEC (symbolp) *function_symbols,
+		    VEC (symbolp) **label_funcs_ret, const char *name)
 {
-  struct symbol *fn_sym;
   int ix;
+  struct block *block;
+  struct symbol *sym;
+  struct symbol *fn_sym;
+  VEC (symbolp) *result = NULL;
 
   if (function_symbols == NULL)
     {
-      struct block *block;
-      struct symbol *sym;
-      struct symtab_and_line sal;
-      struct symtabs_and_lines values;
-
-      values.nelts = 0;
-      values.sals = NULL;
-
       set_current_program_space (self->program_space);
       block = get_search_block (NULL);
 
@@ -2693,69 +2890,174 @@ decode_label (struct linespec_state *self,
 	   block = BLOCK_SUPERBLOCK (block))
 	;
       if (!block)
-	return 0;
+	return NULL;
       fn_sym = BLOCK_FUNCTION (block);
 
-      sym = lookup_symbol (copy, block, LABEL_DOMAIN, 0);
-
-      if (sym == NULL)
-	return 0;
+      sym = lookup_symbol (name, block, LABEL_DOMAIN, 0);
 
-      symbol_to_sal (&sal, self->funfirstline, sym);
-      add_sal_to_sals (self, &values, &sal,
-		       SYMBOL_NATURAL_NAME (fn_sym));
+      if (sym != NULL)
+	{
+	  VEC_safe_push (symbolp, result, sym);
 
-      if (self->canonical)
+	  if (label_funcs_ret != NULL)
+	    VEC_safe_push (symbolp, *label_funcs_ret, fn_sym);
+	}
+    }
+  else
+    {
+      for (ix = 0;
+	   VEC_iterate (symbolp, function_symbols, ix, fn_sym); ++ix)
 	{
-	  self->canonical->special_display = 1;
-	  self->canonical->addr_string
-	    = xstrprintf ("%s:%s", SYMBOL_NATURAL_NAME (fn_sym),
-			  copy);
+	  set_current_program_space (SYMTAB_PSPACE (SYMBOL_SYMTAB (fn_sym)));
+	  block = SYMBOL_BLOCK_VALUE (fn_sym);
+	  sym = lookup_symbol (name, block, LABEL_DOMAIN, 0);
+
+	  if (sym != NULL)
+	    {
+	      VEC_safe_push (symbolp, result, sym);
+	      if (label_funcs_ret != NULL)
+		VEC_safe_push (symbolp, *label_funcs_ret, fn_sym);
+	    }
 	}
+    }
+
+  return result;
+}
 
-      *result = values;
+
 
-      return 1;
+/* A helper for create_sals_line_offset that handles the 'list_mode' case.  */
+
+static void
+decode_digits_list_mode (struct linespec_state *self,
+			 linespec_t ls,
+			 struct symtabs_and_lines *values,
+			 struct symtab_and_line val)
+{
+  int ix;
+  struct symtab *elt;
+
+  gdb_assert (self->list_mode);
+
+  for (ix = 0; VEC_iterate (symtab_p, ls->file_symtabs, ix, elt);
+       ++ix)
+    {
+      /* The logic above should ensure this.  */
+      gdb_assert (elt != NULL);
+
+      set_current_program_space (SYMTAB_PSPACE (elt));
+
+      /* Simplistic search just for the list command.  */
+      val.symtab = find_line_symtab (elt, val.line, NULL, NULL);
+      if (val.symtab == NULL)
+	val.symtab = elt;
+      val.pspace = SYMTAB_PSPACE (elt);
+      val.pc = 0;
+      val.explicit_line = 1;
+
+      add_sal_to_sals (self, values, &val, NULL);
     }
+}
+
+/* A helper for create_sals_line_offset that iterates over the symtabs,
+   adding lines to the VEC.  */
 
-  result->sals = NULL;
-  result->nelts = 0;
+static void
+decode_digits_ordinary (struct linespec_state *self,
+			linespec_t ls,
+			int line,
+			struct symtabs_and_lines *sals,
+			struct linetable_entry **best_entry)
+{
+  int ix;
+  struct symtab *elt;
 
-  for (ix = 0; VEC_iterate (symbolp, function_symbols, ix, fn_sym); ++ix)
+  for (ix = 0; VEC_iterate (symtab_p, ls->file_symtabs, ix, elt); ++ix)
     {
-      struct block *block;
-      struct symbol *sym;
+      int i;
+      VEC (CORE_ADDR) *pcs;
+      CORE_ADDR pc;
 
-      set_current_program_space (SYMTAB_PSPACE (SYMBOL_SYMTAB (fn_sym)));
-      block = SYMBOL_BLOCK_VALUE (fn_sym);
-      sym = lookup_symbol (copy, block, LABEL_DOMAIN, 0);
+      /* The logic above should ensure this.  */
+      gdb_assert (elt != NULL);
 
-      if (sym != NULL)
+      set_current_program_space (SYMTAB_PSPACE (elt));
+
+      pcs = find_pcs_for_symtab_line (elt, line, best_entry);
+      for (i = 0; VEC_iterate (CORE_ADDR, pcs, i, pc); ++i)
 	{
 	  struct symtab_and_line sal;
-	  char *symname;
-
-	  symbol_to_sal (&sal, self->funfirstline, sym);
-	  symname = xstrprintf ("%s:%s",
-				SYMBOL_NATURAL_NAME (fn_sym),
-				SYMBOL_NATURAL_NAME (sym));
-	  add_sal_to_sals (self, result, &sal, symname);
-	  xfree (symname);
+
+	  init_sal (&sal);
+	  sal.pspace = SYMTAB_PSPACE (elt);
+	  sal.symtab = elt;
+	  sal.line = line;
+	  sal.pc = pc;
+	  add_sal_to_sals_basic (sals, &sal);
 	}
+
+      VEC_free (CORE_ADDR, pcs);
     }
+}
+
+
+
+/* Return the line offset represented by VARIABLE.  */
+
+static struct line_offset
+linespec_parse_variable (struct linespec_state *self, const char *variable)
+{
+  int index = 0;
+  const char *p;
+  struct line_offset offset = {0, none};
 
-  if (self->canonical && result->nelts > 0)
+  p = (variable[1] == '$') ? variable + 2 : variable + 1;
+  if (*p == '$')
+    ++p;
+  while (*p >= '0' && *p <= '9')
+    ++p;
+  if (!*p)		/* Reached end of token without hitting non-digit.  */
     {
-      self->canonical->pre_expanded = 1;
-      self->canonical->special_display = 1;
+      /* We have a value history reference.  */
+      struct value *val_history;
 
-      gdb_assert (self->user_function);
-      self->canonical->addr_string
-	= xstrprintf ("%s:%s", self->user_function, copy);
+      sscanf ((variable[1] == '$') ? variable + 2 : variable + 1, "%d", &index);
+      val_history
+	= access_value_history ((variable[1] == '$') ? -index : index);
+      if (TYPE_CODE (value_type (val_history)) != TYPE_CODE_INT)
+	error (_("History values used in line "
+		 "specs must have integer values."));
+      offset.offset = value_as_long (val_history);
+    }
+  else
+    {
+      /* Not all digits -- may be user variable/function or a
+	 convenience variable.  */
+      LONGEST valx;
+      struct internalvar *ivar;
+
+      /* Try it as a convenience variable.  If it is not a convenience
+	 variable, return and allow normal symbol lookup to occur.  */
+      ivar = lookup_only_internalvar (variable + 1);
+      if (ivar == NULL)
+	/* No internal variable with that name.  Mark the offset
+	   as unknown to allow the name to be looked up as a symbol.  */
+	offset.sign = unknown;
+      else
+	{
+	  /* We found a valid variable name.  If it is not an integer,
+	     throw an error.  */
+	  if (!get_internalvar_integer (ivar, &valx))
+	    error (_("Convenience variables used in line "
+		     "specs must have integer values."));
+	  else
+	    offset.offset = valx;
+	}
     }
 
-  return result->nelts > 0;
+  return offset;
 }
+
 
 /* A callback used to possibly add a symbol to the results.  */
 
@@ -2763,20 +3065,16 @@ static int
 collect_symbols (struct symbol *sym, void *data)
 {
   struct collect_info *info = data;
-  struct symtab_and_line sal;
-
-  if (symbol_to_sal (&sal, info->state->funfirstline, sym)
-      && maybe_add_address (info->state->addr_set,
-			    SYMTAB_PSPACE (SYMBOL_SYMTAB (sym)),
-			    sal.pc))
-    add_sal_to_sals (info->state, &info->result, &sal,
-		     SYMBOL_NATURAL_NAME (sym));
 
+  /* In list mode, add all matching symbols, regardless of class.
+     This allows the user to type "list a_global_variable".  */
+  if (SYMBOL_CLASS (sym) == LOC_BLOCK || info->state->list_mode)
+    VEC_safe_push (symbolp, info->result.symbols, sym);
   return 1; /* Continue iterating.  */
 }
 
-/* We've found a minimal symbol MSYMBOL to associate with our
-   linespec; add it to the result symtabs_and_lines.  */
+/* We've found a minimal symbol MSYMBOL in OBJFILE to associate with our
+   linespec; return the SAL in RESULT.  */
 
 static void
 minsym_found (struct linespec_state *self, struct objfile *objfile,
@@ -2804,17 +3102,6 @@ minsym_found (struct linespec_state *self, struct objfile *objfile,
     add_sal_to_sals (self, result, &sal, SYMBOL_NATURAL_NAME (msymbol));
 }
 
-/* A helper struct which just holds a minimal symbol and the object
-   file from which it came.  */
-
-typedef struct minsym_and_objfile
-{
-  struct minimal_symbol *minsym;
-  struct objfile *objfile;
-} minsym_and_objfile_d;
-
-DEF_VEC_O (minsym_and_objfile_d);
-
 /* A helper struct to pass some data through
    iterate_over_minimal_symbols.  */
 
@@ -2967,8 +3254,8 @@ search_minsyms_for_name (struct collect_info *info, const char *name,
 	    if (classify_mtype (MSYMBOL_TYPE (item->minsym)) != classification)
 	      break;
 
-	    minsym_found (info->state, item->objfile, item->minsym,
-			  &info->result);
+	    VEC_safe_push (minsym_and_objfile_d,
+			   info->result.minimal_symbols, item);
 	  }
       }
 
@@ -2988,7 +3275,7 @@ add_matching_symbols_to_info (const char *name,
   int ix;
   struct symtab *elt;
 
-  for (ix = 0; VEC_iterate (symtab_p, info->state->file_symtabs, ix, elt); ++ix)
+  for (ix = 0; VEC_iterate (symtab_p, info->file_symtabs, ix, elt); ++ix)
     {
       struct symbol *sym;
 
@@ -3012,67 +3299,6 @@ add_matching_symbols_to_info (const char *name,
     }
 }
 
-/* Decode a linespec that's a variable.  If FILE_SYMTAB is non-NULL,
-   look in that symtab's static variables first.  */ 
-
-static struct symtabs_and_lines
-decode_variable (struct linespec_state *self, char *copy)
-{
-  struct collect_info info;
-  const char *lookup_name;
-  char *canon;
-  struct cleanup *cleanup;
-
-  info.state = self;
-  info.result.sals = NULL;
-  info.result.nelts = 0;
-
-  cleanup = demangle_for_lookup (copy, current_language->la_language,
-				 &lookup_name);
-  if (current_language->la_language == language_ada)
-    {
-      /* In Ada, the symbol lookups are performed using the encoded
-         name rather than the demangled name.  */
-      lookup_name = ada_name_for_lookup (copy);
-      make_cleanup (xfree, (void *) lookup_name);
-    }
-
-  canon = cp_canonicalize_string_no_typedefs (lookup_name);
-  if (canon != NULL)
-    {
-      make_cleanup (xfree, canon);
-      lookup_name = canon;
-    }
-
-  add_matching_symbols_to_info (lookup_name, &info, NULL);
-
-  if (info.result.nelts > 0)
-    {
-      if (self->canonical)
-	{
-	  self->canonical->pre_expanded = 1;
-	  if (self->user_filename)
-	    self->canonical->addr_string
-	      = xstrprintf ("%s:%s", self->user_filename, copy);
-	  else
-	    self->canonical->addr_string = xstrdup (copy);
-	}
-      return info.result;
-    }
-
-  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 (self->user_filename)
-    throw_error (NOT_FOUND_ERROR, _("Function \"%s\" not defined in \"%s\"."),
-		 copy, self->user_filename);
-  else
-    throw_error (NOT_FOUND_ERROR, _("Function \"%s\" not defined."), copy);
-}
-
-
 
 
 /* Now come some functions that are called from multiple places within
diff --git a/gdb/testsuite/gdb.base/advance.exp b/gdb/testsuite/gdb.base/advance.exp
index 456268d..617a7fb 100644
--- a/gdb/testsuite/gdb.base/advance.exp
+++ b/gdb/testsuite/gdb.base/advance.exp
@@ -45,7 +45,8 @@ gdb_test "advance [gdb_get_line_number "advance this location"]" \
 # Verify that a malformed "advance" is gracefully caught.
 #
 gdb_test "advance [gdb_get_line_number "advance malformed"] then stop" \
-	"Junk at end of arguments." "malformed advance"
+    "malformed linespec error: unexpected string, \"then stop\"" \
+    "malformed advance"
 
 # Verify that "advance <funcname>" works.
 #
diff --git a/gdb/testsuite/gdb.base/break.exp b/gdb/testsuite/gdb.base/break.exp
index 71eb3a6..a203364 100644
--- a/gdb/testsuite/gdb.base/break.exp
+++ b/gdb/testsuite/gdb.base/break.exp
@@ -600,7 +600,7 @@ gdb_test "break $bp_location12 thread foo" \
 # trailing garbage.
 #
 gdb_test "break $bp_location12 foo" \
-    "Junk at end of arguments.*" \
+    "malformed linespec error: unexpected string, \"foo\".*" \
     "breakpoint with trailing garbage disallowed"
 
 # Verify that GDB responds gracefully to a "clear" command that has
diff --git a/gdb/testsuite/gdb.base/hbreak2.exp b/gdb/testsuite/gdb.base/hbreak2.exp
index 7f52cd3..a11d8b6 100644
--- a/gdb/testsuite/gdb.base/hbreak2.exp
+++ b/gdb/testsuite/gdb.base/hbreak2.exp
@@ -356,7 +356,7 @@ gdb_test "hbreak $bp_location12 thread foo" \
 # trailing garbage.
 #
 gdb_test "hbreak $bp_location12 foo" \
-    "Junk at end of arguments.*" \
+    "malformed linespec error: unexpected string, \"foo\".*" \
     "hardware breakpoint with trailing garbage disallowed"
 
 # Verify that GDB responds gracefully to a "clear" command that has
diff --git a/gdb/testsuite/gdb.base/jump.exp b/gdb/testsuite/gdb.base/jump.exp
index a5577e2..a7b2177 100644
--- a/gdb/testsuite/gdb.base/jump.exp
+++ b/gdb/testsuite/gdb.base/jump.exp
@@ -92,7 +92,7 @@ gdb_test "jump" "Argument required .starting address.*" \
 # trailing junk.
 #
 gdb_test "jump 21 100" \
-    "Junk at end of line specification: 100.*" \
+    "malformed linespec error: unexpected number, \"100\"" \
     "jump with trailing argument junk"
 
 
diff --git a/gdb/testsuite/gdb.base/sepdebug.exp b/gdb/testsuite/gdb.base/sepdebug.exp
index bd60c70..e6973fb 100644
--- a/gdb/testsuite/gdb.base/sepdebug.exp
+++ b/gdb/testsuite/gdb.base/sepdebug.exp
@@ -408,7 +408,7 @@ gdb_test "break $bp_location12 thread foo" \
 #
 
 gdb_test "break $bp_location12 foo" \
-    "Junk at end of arguments.*" \
+    "malformed linespec error: unexpected string, \"foo\".*" \
     "breakpoint with trailing garbage disallowed"
 
 # Verify that GDB responds gracefully to a "clear" command that has
diff --git a/gdb/testsuite/gdb.base/until.exp b/gdb/testsuite/gdb.base/until.exp
index 6b647cd..bdee21d 100644
--- a/gdb/testsuite/gdb.base/until.exp
+++ b/gdb/testsuite/gdb.base/until.exp
@@ -40,7 +40,8 @@ gdb_test "until $bp_location1" \
 # Verify that a malformed "advance" is gracefully caught.
 #
 gdb_test "until 80 then stop" \
-	"Junk at end of arguments." "malformed until"
+    "malformed linespec error: unexpected string, \"then stop\"." \
+    "malformed until"
 
 # Rerun up to factorial, outer invocation
 if { ![runto factorial] } then { gdb_suppress_tests; }
diff --git a/gdb/testsuite/gdb.cp/ovldbreak.exp b/gdb/testsuite/gdb.cp/ovldbreak.exp
index 3e35b79..2c27f2d 100644
--- a/gdb/testsuite/gdb.cp/ovldbreak.exp
+++ b/gdb/testsuite/gdb.cp/ovldbreak.exp
@@ -320,23 +320,17 @@ gdb_expect {
     }
 }
 
-gdb_test "info break" \
-    "Num     Type\[\t \]+Disp Enb Address\[\t \]+What.*
-\[0-9\]+\[\t \]+breakpoint     keep y\[\t \]+<MULTIPLE>\[\t \]*\r
-\[0-9\]+.1\[\t \]+y\[\t \]+$hex\[\t \]+in foo::overload1arg\\(double\\) at.*$srcfile:140\r
-\[0-9\]+.2\[\t \]+y\[\t \]+$hex\[\t \]+in foo::overload1arg\\(float\\) at.*$srcfile:137\r
-\[0-9\]+.3\[\t \]+y\[\t \]+$hex\[\t \]+in foo::overload1arg\\((unsigned long|long unsigned)( int)?\\) at.*$srcfile:134\r
-\[0-9\]+.4\[\t \]+y\[\t \]+$hex\[\t \]+in foo::overload1arg\\(long( int)?\\) at.*$srcfile:131\r
-\[0-9\]+.5\[\t \]+y\[\t \]+$hex\[\t \]+in foo::overload1arg\\((unsigned|unsigned int)\\) at.*$srcfile:128\r
-\[0-9\]+.6\[\t \]+y\[\t \]+$hex\[\t \]+in foo::overload1arg\\(int\\) at.*$srcfile:125\r
-\[0-9\]+.7\[\t \]+y\[\t \]+$hex\[\t \]+in foo::overload1arg\\((unsigned short|short unsigned)( int)?\\) at.*$srcfile:122\r
-\[0-9\]+.8\[\t \]+y\[\t \]+$hex\[\t \]+in foo::overload1arg\\(short( int)?\\) at.*$srcfile:119\r
-\[0-9\]+.9\[\t \]+y\[\t \]+$hex\[\t \]+in foo::overload1arg\\(unsigned char\\) at.*$srcfile:116\r
-\[0-9\]+.10\[\t \]+y\[\t \]+$hex\[\t \]+in foo::overload1arg\\(signed char\\) at.*$srcfile:113\r
-\[0-9\]+.11\[\t \]+y\[\t \]+$hex\[\t \]+in foo::overload1arg\\(char\\) at.*$srcfile:110\r
-\[0-9\]+.12\[\t \]+y\[\t \]+$hex\[\t \]+in foo::overload1arg\\((void|)\\) at.*$srcfile:107" \
-    "breakpoint info (after setting on all)"
+# Create the breakpoint table for "info breakpoint".
+set bptable "Num     Type\[\t \]+Disp Enb Address\[\t \]+What.*\[\r\n]+"
+append bptable "\[0-9\]+\[\t \]+breakpoint\[\t \]+keep\[\t \]y\[\t \]+<MULTIPLE>.*\[\r\n\]+"
+foreach ovld {void char signed_char unsigned_char short_int \
+		  unsigned_short_int int unsigned_int long_int \
+		  unsigned_long_int float double} {
+  append bptable [format "\[0-9\]+.\[0-9\]+\[\t \]+y\[\t \]+$hex\[\t \]+in foo::overload1arg\\(%s\\) at.*$srcfile:%d\[\r\n\]+" \
+		      $types($ovld) $line($ovld)]
+}
 
+gdb_test "info break" $bptable "breakpoint info (after setting on all)"
 
 # Run through each breakpoint.
 proc continue_to_bp_overloaded {bpnumber might_fail line argtype argument} {
diff --git a/gdb/testsuite/gdb.linespec/ls-errs.c b/gdb/testsuite/gdb.linespec/ls-errs.c
new file mode 100644
index 0000000..bb4c096
--- /dev/null
+++ b/gdb/testsuite/gdb.linespec/ls-errs.c
@@ -0,0 +1,29 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2012 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+int myfunction (void) { return 0; }
+
+int
+main (void)
+{  
+  int a;
+
+  a = myfunction ();
+
+ here:
+  return a;
+}
diff --git a/gdb/testsuite/gdb.linespec/ls-errs.exp b/gdb/testsuite/gdb.linespec/ls-errs.exp
new file mode 100644
index 0000000..ce83807
--- /dev/null
+++ b/gdb/testsuite/gdb.linespec/ls-errs.exp
@@ -0,0 +1,189 @@
+# Copyright 2012 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Tests for linespec error conditions
+
+set base ls-errs
+set srcfile "$base.c"
+set testfile "$base.exp"
+set exefile $base
+
+if {[prepare_for_testing $testfile $exefile $srcfile \
+	 {debug nowarnings}]} {
+    return -1
+}
+
+# Turn off the pending breakpoint queries.
+gdb_test_no_output "set breakpoint pending off"
+
+# We intentionally do not use gdb_breakpoint for these tests.
+
+# Add the (invalid) LINESPEC to the test array named in ARRAY_NAME.
+# Use the index into ::error_messages MSG_ID and ARGS to create
+# an error message which is the expect result of attempting to
+# break on the given LINESPEC.
+proc add {array_name linespec msg_id args} {
+    global error_messages
+    upvar $array_name tests
+
+    lappend tests(linespecs) $linespec
+    set tests("$linespec") [string_to_regexp \
+				[eval format \$error_messages($msg_id) $args]]
+}
+
+# Common error message format strings.
+array set error_messages {
+    invalid_file "No source file named %s."
+    invalid_function "Function \"%s\" not defined."
+    invalid_var_or_func "Undefined convenience variable or function \"%s\" not defined."
+    invalid_function_f "Function \"%s\" not defined in \"%s\"."
+    invalid_var_or_func_f \
+	"Undefined convenience variable or function \"%s\" not defined in \"%s\"."
+    invalid_label "No label \"%s\" defined in function \"%s\"."
+    invalid_offset "No line %d in the current file."
+    invalid_offset_f "No line %d in file \"%s\"."
+    unexpected "malformed linespec error: unexpected %s"
+    unexpected_opt "malformed linespec error: unexpected %s, \"%s\""
+    unmatched_quote "unmatched quote"
+}
+
+# Some commonly used whitespace tests around ':'.
+set spaces [list ":" ": " " :" " : " "\t:  " "  :\t" "\t:\t" " \t:\t " \
+		"\t  \t:\t  \t  \t"]
+
+# A list of invalid offsets.
+set invalid_offsets [list -100 +500 1000]
+
+# THE_TESTS will hold all of our test information.  Array index
+# "linespecs" will contain the complete list of all linespecs
+# to be tested.  An array index of \"$linespec\" will contain
+# the expected result.
+set the_tests(linespecs) {}
+
+# Try some simple, invalid linespecs involving spaces.
+foreach x $spaces {
+    add the_tests $x unexpected "colon"
+}
+
+# Test invalid filespecs starting with offset.  This is done
+# first so that default offsets are tested.
+foreach x $invalid_offsets {
+    set offset $x
+
+    # Relative offsets are relative to line 16.  Adjust
+    # expected offset from error message accordingly.
+    if {[string index $x 0] == "+" ||
+	[string index $x 0] == "-"} {
+	incr offset 16
+    }
+    add the_tests $x invalid_offset $offset
+}
+
+# Test offsets with trailing tokens w/ and w/o spaces.
+foreach x $spaces {
+    add the_tests "3$x" unexpected "colon"
+    add the_tests "+10$x" unexpected "colon"
+    add the_tests "-10$x" unexpected "colon"
+}
+
+foreach x {1 +1 +100 -10} {
+    add the_tests "3 $x" unexpected_opt "number" $x
+    add the_tests "+10 $x" unexpected_opt "number" $x
+    add the_tests "-10 $x" unexpected_opt "number" $x
+}
+
+add the_tests "3 foo" unexpected_opt "string" "foo"
+add the_tests "+10 foo" unexpected_opt "string" "foo"
+add the_tests "-10 foo" unexpected_opt "string" "foo"
+
+# Test invalid linespecs starting with filename.
+foreach x [list "this_file_doesn't_exist.c" \
+	       "this file has spaces.c" \
+	       "\"file::colons.c\"" \
+	       "'file::colons.c'" \
+	       "\"this \"file\" has quotes.c\"" \
+	       "'this \"file\" has quotes.c'" \
+	       "'this 'file' has quotes.c'" \
+	       "\"this 'file' has quotes.c\""] {
+    # Remove any quoting from FILENAME for the error message.
+    add the_tests "$x:3" invalid_file [string trim $x \"']
+}
+
+# Test unmatched quotes.
+foreach x {"\"src-file.c'" "'src-file.c"} {
+    add the_tests "$x:3" unmatched_quote
+}
+
+add the_tests $srcfile invalid_function $srcfile
+foreach x {"foo" " foo" " foo "} {
+    # Trim any leading/trailing whitespace for error messages.
+    add the_tests "$srcfile:$x" invalid_function_f [string trim $x] $srcfile
+    add the_tests "$srcfile:main:$x" invalid_label [string trim $x] "main" 
+}
+
+foreach x $spaces {
+    add the_tests "$srcfile$x" unexpected "end of input"
+    add the_tests "$srcfile:main$x" unexpected "end of input"
+}
+
+add the_tests "${srcfile}::" invalid_function "${srcfile}::"
+add the_tests "$srcfile:3 1" unexpected_opt "number" "1"
+add the_tests "$srcfile:3 +100" unexpected_opt "number" "+100"
+add the_tests "$srcfile:3 -100" unexpected_opt "number" "-100"
+add the_tests "$srcfile:3 foo" unexpected_opt "string" "foo"
+
+foreach x $invalid_offsets {
+    add the_tests "$srcfile:$x" invalid_offset_f $x $srcfile
+}
+
+# Test invalid filespecs starting with function.
+foreach x {"foobar" "foo::bar" "foo.bar" "foo ." "foo bar" "foo 1" \
+	       "foo 0" "foo +10" "foo -10" "foo +100" "foo -100"} {
+    add the_tests $x invalid_function $x
+}
+
+foreach x $spaces {
+    add the_tests "main${x}there" invalid_label "there" "main"
+    add the_tests "main:here${x}" unexpected "end of input"
+}
+
+add the_tests "main 3" invalid_function "main 3"
+add the_tests "main +100" invalid_function "main +100"
+add the_tests "main -100" invalid_function "main -100"
+add the_tests "main foo" invalid_function "main foo"
+
+foreach x {"3" "+100" "-100" "foo"} {
+    add the_tests "main:here $x" invalid_label "here $x" "main"
+}
+
+foreach x {"if" "task" "thread"} {
+    add the_tests $x unexpected_opt "keyword" $x
+}
+
+add the_tests "'main.c'flubber" unexpected_opt "string" "flubber"
+add the_tests "'main.c',21" unexpected_opt "string" ",21"
+add the_tests "'main.c' " invalid_function "main.c"
+add the_tests "'main.c'3" unexpected_opt "number" "3"
+add the_tests "'main.c'+3" unexpected_opt "number" "+3"
+
+# Test undefined convenience variables.
+set x {$zippo}
+add the_tests $x invalid_var_or_func $x
+add the_tests "$srcfile:$x" invalid_var_or_func_f $x $srcfile
+
+# Run the tests
+foreach linespec $the_tests(linespecs) {
+    gdb_test "break $linespec" $the_tests("$linespec")
+}
diff --git a/gdb/testsuite/gdb.cp/cplabel.cc b/gdb/testsuite/gdb.cp/cplabel.cc
new file mode 100644
index 0000000..876f802
--- /dev/null
+++ b/gdb/testsuite/gdb.cp/cplabel.cc
@@ -0,0 +1,80 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2012 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+class foo
+{
+public:
+  static int bar (void)
+  {
+    int i = 5;
+    bool first = true;
+
+  to_the_top:  /* bar:to_the_top */
+    while (1)
+      {
+	if (i == 1)
+	  {
+	    if (first)
+	      {
+		first = false;
+		goto to_the_top;
+	      }
+	    else
+	      goto get_out_of_here;
+	  }
+
+	--i;
+      }
+
+  get_out_of_here: /* bar:get_out_of_here */
+    return i;
+  }
+
+  int baz (int a)
+  {
+    int i = a;
+    bool first = true;
+
+  to_the_top: /* baz:to_the_top */
+    while (1)
+      {
+	if (i == 1)
+	  {
+	    if (first)
+	      {
+		first = false;
+		goto to_the_top;
+	      }
+	    else
+	      goto get_out_of_here;
+	  }
+
+	--i;
+      }
+
+  get_out_of_here: /* baz:get_out_of_here */
+    return i;
+  }
+};
+
+int
+main (void)
+{
+  foo f;
+  return f.baz (foo::bar () + 3); 
+}
+
diff --git a/gdb/testsuite/gdb.cp/cplabel.exp b/gdb/testsuite/gdb.cp/cplabel.exp
new file mode 100644
index 0000000..4f99b55
--- /dev/null
+++ b/gdb/testsuite/gdb.cp/cplabel.exp
@@ -0,0 +1,38 @@
+# Copyright 2012 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Tests for breakpoint on labels in methods.
+
+set testfile cplabel
+set srcfile "$testfile.cc"
+if {[prepare_for_testing "$testfile.exp" $testfile $srcfile]} {
+    return -1
+}
+
+if {![runto_main]} {
+    untested "could not run to main"
+    return -1
+}
+
+set methods {"bar" "baz"}
+set labels {"to_the_top" "get_out_of_here"}
+
+foreach m $methods {
+    foreach l $labels {
+	set line [gdb_get_line_number "$m:$l"]
+	gdb_test "break foo::$m:$l" \
+	    "Breakpoint $decimal at $hex: file .*/$srcfile, line $line\."
+    }
+}
diff --git a/gdb/testsuite/gdb.cp/userdef.exp b/gdb/testsuite/gdb.cp/userdef.exp
index 9c7cb57..90eb99d 100644
--- a/gdb/testsuite/gdb.cp/userdef.exp
+++ b/gdb/testsuite/gdb.cp/userdef.exp
@@ -136,8 +136,8 @@ gdb_test "print one += 7" "\\\$\[0-9\]* = {x = 9, y = 10}"
 gdb_test "print two = one" "\\\$\[0-9\]* = {x = 9, y = 10}"
 
 # Check that GDB tolerates whitespace in operator names.
-gdb_test "break A2::'operator+'" ".*Breakpoint $decimal at.*"
-gdb_test "break A2::'operator +'" ".*Breakpoint $decimal at.*"
+gdb_test "break A2::operator+" ".*Breakpoint $decimal at.*"
+gdb_test "break A2::operator +" ".*Breakpoint $decimal at.*"
 
 # Check that GDB handles operator* correctly.
 gdb_test "print c" "\\\$\[0-9\]* = {m = {z = .*}}"
diff --git a/gdb/testsuite/gdb.linespec/ls-dollar.cc b/gdb/testsuite/gdb.linespec/ls-dollar.cc

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]