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]

[PATCH] PR gdb/17960 - Move completion_tracker_t into the completion API.


The bug itself is easily demonstrated:

$ gdb -nx -q gdb
(gdb) b gdb.c:ma<TAB>
./../src/gdb/completer.c:837: internal-error: maybe_add_completion:
Assertion `tracker != NULL' failed.
A problem internal to GDB has been detected,
further debugging may prove unreliable.
Quit this debugging session? (y or n)

This occurs because the code path in this case is:
complete_line
- complete_line_internal
-- location_completer
--- make_file_symbol_completion_list
(...)
---- completion_list_add_name
----- maybe_add_completion
----> gdb_assert (tracker != NULL);

The tracker in maybe_add_completion is a static global in symtab.c called
completion_tracker.  It is initialized by
default_make_symbol_completion_list_break_on_1 (which is a defaulted
language vector method).  In this case, this function is never called
and completion_tracker is left NULL/uninitialized.

This patch refactors the completion API, adding a completion_tracker_t
to the list of function arguments for all completion functions, removing
the (file) global completion_tracker in symtab.c that is the cause of this bug.

gdb/ChangeLog

	PR gdb/17960
	* cli/cli-decode.c (find_cmd): Use maybe_add_completion to
	implement completion tracking.
	* command.h (completer_ftype, completer_ftype_void,
	set_cmd_completer, set_cmd_completer_handle_brkchars,
	lookup_cmd, lookup_cmd_1, complete_on_cmdlist,
	complete_on_enum): Move to completer.h.
	* completer.c (location_completer): Use TRY_CATCH to catch
	max completion exception from make_file_symbol_completion_list,
	make_symbol_completion_list, and make_source_files_completion_list.
	(complete_line_internal): Empty the tracker when a unique
	command completion is found.
	(complete_line): Instantiate a new tracker earlier and pass this
	to complete_line_internal.
	If the list of completions is bigger than those tracked,
	completion tracking was not implemented for the requested completion;
	re-filter the list for maximum completions.  Otherwise simply
	copy the result.
	* completer.h (max_completions, new_completion_tracker,
	make_cleanup_free_completion_tracker, enum maybe_add_completion_enum,
	maybe_add_completion, throw_max_completions_reached_error): Move
	declarations to earlier in file.
	(completer_ftype, completer_ftype_void, set_cmd_completer,
	set_cmd_completer_handle_brkchars, lookup_cmd, lookup_cmd_1,
	complete_on_cmdlist, complete_on_enum): Move declarations here
	from command.h.
	* symtab.c (COMPLETION_LIST_ADD_SYMBOL,
	MCOMPLETION_LIST_ADD_SYMBOL): Add `tracker' as first argument.
	All users updated.
	(struct add_name_data): Add `tracker' member.
	(default_make_symbol_completion_list_break_on_1): Add tracker
	to call data.
	Do not instantiate a completion tracker here -- use the one
	passed instead.
	(add_filename_to_list): Implement completion tracking.
	(struct add_partial_filename_data): Add `tracker' member.
	(make_source_files_completion_list): Add tracker to call data.
	* symtab.h: Include completer.h.
	* ada-lang.c, breakpoint.c, cli/cli-cmds.c, cli/cli-decode.h,
	corefile.c, cp-abi.c, f-lang.c, infrun.c, interps.c, language.h,
	python/py-cmd.c, top.c, symtab.c: Updated for completion API change.

gdb/testsuite/ChangeLog

	PR gdb/17960
	* gdb.base/completion.exp: Add location completer tests.
---
 gdb/ada-lang.c                        |    3 -
 gdb/breakpoint.c                      |    6 +
 gdb/cli/cli-cmds.c                    |    6 +
 gdb/cli/cli-decode.c                  |   58 ++++++++---
 gdb/cli/cli-decode.h                  |    1 
 gdb/command.h                         |   31 ------
 gdb/completer.c                       |  177 +++++++++++++++++++++++----------
 gdb/completer.h                       |  168 +++++++++++++++++++------------
 gdb/corefile.c                        |    3 -
 gdb/cp-abi.c                          |    3 -
 gdb/f-lang.c                          |    6 +
 gdb/infrun.c                          |    5 +
 gdb/interps.c                         |    1 
 gdb/language.h                        |    3 -
 gdb/python/py-cmd.c                   |    5 +
 gdb/symtab.c                          |  166 ++++++++++++++++++-------------
 gdb/symtab.h                          |   35 +++----
 gdb/testsuite/gdb.base/completion.exp |   78 +++++++++++++++
 gdb/top.c                             |    2 
 19 files changed, 492 insertions(+), 265 deletions(-)

diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c
index 562627a..161e3aa 100644
--- a/gdb/ada-lang.c
+++ b/gdb/ada-lang.c
@@ -6191,7 +6191,8 @@ ada_complete_symbol_matcher (const char *name, void *user_data)
    the entire command on which completion is made.  */
 
 static VEC (char_ptr) *
-ada_make_symbol_completion_list (const char *text0, const char *word,
+ada_make_symbol_completion_list (completion_tracker_t tracker,
+				 const char *text0, const char *word,
 				 enum type_code code)
 {
   char *text;
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index a7cc6cb..2773f53 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -1020,6 +1020,7 @@ set_breakpoint_condition (struct breakpoint *b, char *exp,
 
 static VEC (char_ptr) *
 condition_completer (struct cmd_list_element *cmd,
+		     completion_tracker_t tracker,
 		     const char *text, const char *word)
 {
   const char *space;
@@ -1058,7 +1059,7 @@ condition_completer (struct cmd_list_element *cmd,
 
   /* We're completing the expression part.  */
   text = skip_spaces_const (space);
-  return expression_completer (cmd, text, word);
+  return expression_completer (cmd, tracker, text, word);
 }
 
 /* condition N EXP -- set break condition of breakpoint N to EXP.  */
@@ -15393,11 +15394,12 @@ catching_syscall_number (int syscall_number)
 /* Complete syscall names.  Used by "catch syscall".  */
 static VEC (char_ptr) *
 catch_syscall_completer (struct cmd_list_element *cmd,
+			 completion_tracker_t tracker,
                          const char *text, const char *word)
 {
   const char **list = get_syscall_names (get_current_arch ());
   VEC (char_ptr) *retlist
-    = (list == NULL) ? NULL : complete_on_enum (list, word, word);
+    = (list == NULL) ? NULL : complete_on_enum (tracker, list, word, word);
 
   xfree (list);
   return retlist;
diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c
index e46f036..c2b4604 100644
--- a/gdb/cli/cli-cmds.c
+++ b/gdb/cli/cli-cmds.c
@@ -1344,7 +1344,7 @@ valid_command_p (const char *command)
 {
   struct cmd_list_element *c;
 
-  c = lookup_cmd_1 (& command, cmdlist, NULL, 1);
+  c = lookup_cmd_1 (NULL, &command, cmdlist, NULL, 1);
 
   if (c == NULL || c == (struct cmd_list_element *) -1)
     return FALSE;
@@ -1467,12 +1467,12 @@ alias_command (char *args, int from_tty)
       alias_prefix = dyn_string_buf (alias_prefix_dyn_string);
       command_prefix = dyn_string_buf (command_prefix_dyn_string);
 
-      c_command = lookup_cmd_1 (& command_prefix, cmdlist, NULL, 1);
+      c_command = lookup_cmd_1 (NULL, &command_prefix, cmdlist, NULL, 1);
       /* We've already tried to look up COMMAND.  */
       gdb_assert (c_command != NULL
 		  && c_command != (struct cmd_list_element *) -1);
       gdb_assert (c_command->prefixlist != NULL);
-      c_alias = lookup_cmd_1 (& alias_prefix, cmdlist, NULL, 1);
+      c_alias = lookup_cmd_1 (NULL, &alias_prefix, cmdlist, NULL, 1);
       if (c_alias != c_command)
 	error (_("ALIAS and COMMAND prefixes do not match."));
 
diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
index 4ec6ec0..b31772c 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -35,7 +35,8 @@ static struct cmd_list_element *delete_cmd (const char *name,
 					    struct cmd_list_element **posthook,
 					    struct cmd_list_element **posthookee);
 
-static struct cmd_list_element *find_cmd (const char *command,
+static struct cmd_list_element *find_cmd (completion_tracker_t tracker,
+					  const char *command,
 					  int len,
 					  struct cmd_list_element *clist,
 					  int ignore_help_classes,
@@ -649,6 +650,7 @@ add_setshow_optional_filename_cmd (const char *name, enum command_class class,
 
 static VEC (char_ptr) *
 integer_unlimited_completer (struct cmd_list_element *ignore,
+			     completion_tracker_t tracker,
 			     const char *text, const char *word)
 {
   static const char * const keywords[] =
@@ -657,7 +659,7 @@ integer_unlimited_completer (struct cmd_list_element *ignore,
       NULL,
     };
 
-  return complete_on_enum (keywords, text, word);
+  return complete_on_enum (tracker, keywords, text, word);
 }
 
 /* Add element named NAME to both the set and show command LISTs (the
@@ -1215,22 +1217,43 @@ help_cmd_list (struct cmd_list_element *list, enum command_class class,
    found in nfound.  */
 
 static struct cmd_list_element *
-find_cmd (const char *command, int len, struct cmd_list_element *clist,
+find_cmd (completion_tracker_t tracker,
+	  const char *command, int len, struct cmd_list_element *clist,
 	  int ignore_help_classes, int *nfound)
 {
   struct cmd_list_element *found, *c;
+  int max_reached;
 
   found = (struct cmd_list_element *) NULL;
   *nfound = 0;
-  for (c = clist; c; c = c->next)
+  for (c = clist, max_reached = 0; c; c = c->next)
     if (!strncmp (command, c->name, len)
 	&& (!ignore_help_classes || c->func))
       {
-	found = c;
-	(*nfound)++;
-	if (c->name[len] == '\0')
+	enum maybe_add_completion_enum add_status;
+
+	add_status = (tracker == NULL
+		      ? MAYBE_ADD_COMPLETION_OK
+		      : maybe_add_completion (tracker, c->name));
+	switch (add_status)
 	  {
-	    *nfound = 1;
+	  case MAYBE_ADD_COMPLETION_OK:
+	    found = c;
+	    (*nfound)++;
+	    if (c->name[len] == '\0')
+	      {
+		*nfound = 1;
+		return found;
+	      }
+	    break;
+	  case MAYBE_ADD_COMPLETION_OK_MAX_REACHED:
+	    found = c;
+	    (*nfound)++;
+	    return found;
+	  case MAYBE_ADD_COMPLETION_MAX_REACHED:
+	    return found;
+	  case MAYBE_ADD_COMPLETION_DUPLICATE:
+	    continue;
 	    break;
 	  }
       }
@@ -1336,7 +1359,8 @@ valid_user_defined_cmd_name_p (const char *name)
    the struct cmd_list_element is NULL).  */
 
 struct cmd_list_element *
-lookup_cmd_1 (const char **text, struct cmd_list_element *clist,
+lookup_cmd_1 (completion_tracker_t tracker,
+	      const char **text, struct cmd_list_element *clist,
 	      struct cmd_list_element **result_list, int ignore_help_classes)
 {
   char *command;
@@ -1365,7 +1389,8 @@ lookup_cmd_1 (const char **text, struct cmd_list_element *clist,
   /* Look it up.  */
   found = 0;
   nfound = 0;
-  found = find_cmd (command, len, clist, ignore_help_classes, &nfound);
+  found = find_cmd (tracker, command, len, clist, ignore_help_classes,
+		    &nfound);
 
   /* We didn't find the command in the entered case, so lower case it
      and search again.  */
@@ -1377,7 +1402,8 @@ lookup_cmd_1 (const char **text, struct cmd_list_element *clist,
 
 	  command[tmp] = isupper (x) ? tolower (x) : x;
 	}
-      found = find_cmd (command, len, clist, ignore_help_classes, &nfound);
+      found = find_cmd (tracker, command, len, clist, ignore_help_classes,
+			&nfound);
     }
 
   /* If nothing matches, we have a simple failure.  */
@@ -1414,7 +1440,7 @@ lookup_cmd_1 (const char **text, struct cmd_list_element *clist,
 
   if (found->prefixlist)
     {
-      c = lookup_cmd_1 (text, *found->prefixlist, result_list,
+      c = lookup_cmd_1 (tracker, text, *found->prefixlist, result_list,
 			ignore_help_classes);
       if (!c)
 	{
@@ -1491,7 +1517,7 @@ lookup_cmd (const char **line, struct cmd_list_element *list, char *cmdtype,
   if (!*line)
     error (_("Lack of needed %scommand"), cmdtype);
 
-  c = lookup_cmd_1 (line, list, &last_list, ignore_help_classes);
+  c = lookup_cmd_1 (NULL, line, list, &last_list, ignore_help_classes);
 
   if (!c)
     {
@@ -1718,7 +1744,7 @@ lookup_cmd_composition (const char *text,
       /* Look it up.  */
       *cmd = 0;
       nfound = 0;
-      *cmd = find_cmd (command, len, cur_list, 1, &nfound);
+      *cmd = find_cmd (NULL, command, len, cur_list, 1, &nfound);
       
       /* We didn't find the command in the entered case, so lower case
 	 it and search again.
@@ -1731,7 +1757,7 @@ lookup_cmd_composition (const char *text,
 
 	      command[tmp] = isupper (x) ? tolower (x) : x;
 	    }
-	  *cmd = find_cmd (command, len, cur_list, 1, &nfound);
+	  *cmd = find_cmd (NULL, command, len, cur_list, 1, &nfound);
 	}
       
       if (*cmd == CMD_LIST_AMBIGUOUS)
@@ -1842,7 +1868,7 @@ complete_on_cmdlist (struct cmd_list_element *list,
    "oobar"; if WORD is "baz/foo", return "baz/foobar".  */
 
 VEC (char_ptr) *
-complete_on_enum (const char *const *enumlist,
+complete_on_enum (completion_tracker_t ignore, const char *const *enumlist,
 		  const char *text, const char *word)
 {
   VEC (char_ptr) *matchlist = NULL;
diff --git a/gdb/cli/cli-decode.h b/gdb/cli/cli-decode.h
index ec89325..bbedb52 100644
--- a/gdb/cli/cli-decode.h
+++ b/gdb/cli/cli-decode.h
@@ -23,6 +23,7 @@
 
 /* Include the public interfaces.  */
 #include "command.h"
+#include "completer.h"
 
 struct re_pattern_buffer;
 
diff --git a/gdb/command.h b/gdb/command.h
index 956eeaa..35d61bd 100644
--- a/gdb/command.h
+++ b/gdb/command.h
@@ -156,19 +156,6 @@ typedef void cmd_sfunc_ftype (char *args, int from_tty,
 extern void set_cmd_sfunc (struct cmd_list_element *cmd,
 			   cmd_sfunc_ftype *sfunc);
 
-typedef VEC (char_ptr) *completer_ftype (struct cmd_list_element *,
-					 const char *, const char *);
-
-typedef void completer_ftype_void (struct cmd_list_element *,
-				   const char *, const char *);
-
-extern void set_cmd_completer (struct cmd_list_element *, completer_ftype *);
-
-/* Set the completer_handle_brkchars callback.  */
-
-extern void set_cmd_completer_handle_brkchars (struct cmd_list_element *,
-					       completer_ftype_void *);
-
 /* HACK: cagney/2002-02-23: Code, mostly in tracepoints.c, grubs
    around in cmd objects to test the value of the commands sfunc().  */
 extern int cmd_cfunc_eq (struct cmd_list_element *cmd,
@@ -189,18 +176,6 @@ extern void execute_cmd_post_hook (struct cmd_list_element *cmd);
 /* Return the type of the command.  */
 extern enum cmd_types cmd_type (struct cmd_list_element *cmd);
 
-/* Flag for an ambiguous cmd_list result.  */
-#define CMD_LIST_AMBIGUOUS ((struct cmd_list_element *) -1)
-
-extern struct cmd_list_element *lookup_cmd (const char **,
-					    struct cmd_list_element *, char *,
-					    int, int);
-
-extern struct cmd_list_element *lookup_cmd_1 (const char **,
-					      struct cmd_list_element *,
-					      struct cmd_list_element **,
-					      int);
-
 extern struct cmd_list_element *deprecate_cmd (struct cmd_list_element *,
 					       const char * );
 
@@ -225,12 +200,6 @@ extern struct cmd_list_element *add_info (const char *,
 extern struct cmd_list_element *add_info_alias (const char *, const char *,
 						int);
 
-extern VEC (char_ptr) *complete_on_cmdlist (struct cmd_list_element *,
-					    const char *, const char *, int);
-
-extern VEC (char_ptr) *complete_on_enum (const char *const *enumlist,
-					 const char *, const char *);
-
 /* Functions that implement commands about CLI commands.  */
 
 extern void help_list (struct cmd_list_element *, const char *,
diff --git a/gdb/completer.c b/gdb/completer.c
index add79cc..67644d1 100644
--- a/gdb/completer.c
+++ b/gdb/completer.c
@@ -107,7 +107,8 @@ readline_line_completion_function (const char *text, int matches)
 /* This can be used for functions which don't want to complete on
    symbols but don't want to complete on anything else either.  */
 VEC (char_ptr) *
-noop_completer (struct cmd_list_element *ignore, 
+noop_completer (struct cmd_list_element *ignore,
+		completion_tracker_t ignore2,
 		const char *text, const char *prefix)
 {
   return NULL;
@@ -115,7 +116,8 @@ noop_completer (struct cmd_list_element *ignore,
 
 /* Complete on filenames.  */
 VEC (char_ptr) *
-filename_completer (struct cmd_list_element *ignore, 
+filename_completer (struct cmd_list_element *ignore,
+		    completion_tracker_t ignore2,
 		    const char *text, const char *word)
 {
   int subsequent_name;
@@ -184,7 +186,8 @@ filename_completer (struct cmd_list_element *ignore,
    etc.  */
 
 VEC (char_ptr) *
-location_completer (struct cmd_list_element *ignore, 
+location_completer (struct cmd_list_element *ignore,
+		    completion_tracker_t tracker,
 		    const char *text, const char *word)
 {
   int n_syms, n_files, ix;
@@ -260,18 +263,41 @@ location_completer (struct cmd_list_element *ignore,
      symbols as well as on files.  */
   if (colon)
     {
-      list = make_file_symbol_completion_list (symbol_start, word,
-					       file_to_match);
+      struct gdb_exception e;
+
+      TRY_CATCH (e, RETURN_MASK_ERROR)
+	{
+	  list = make_file_symbol_completion_list (tracker, symbol_start,
+						   word, file_to_match);
+	}
+      if (e.reason < 0)
+	list = NULL;
       xfree (file_to_match);
     }
   else
     {
-      list = make_symbol_completion_list (symbol_start, word);
+      struct gdb_exception e;
+
+      TRY_CATCH (e, RETURN_MASK_ERROR)
+	{
+	  list = make_symbol_completion_list (tracker, symbol_start, word);
+	}
+      if (e.reason < 0)
+	list = NULL;
+
       /* If text includes characters which cannot appear in a file
 	 name, they cannot be asking for completion on files.  */
       if (strcspn (text, 
 		   gdb_completer_file_name_break_characters) == text_len)
-	fn_list = make_source_files_completion_list (text, text);
+	{
+	  TRY_CATCH (e, RETURN_MASK_ERROR)
+	    {
+	      fn_list = make_source_files_completion_list (tracker, text,
+							   text);
+	    }
+	  if (e.reason < 0)
+	    fn_list = NULL;
+	}
     }
 
   n_syms = VEC_length (char_ptr, list);
@@ -326,7 +352,14 @@ location_completer (struct cmd_list_element *ignore,
     {
       /* No completions at all.  As the final resort, try completing
 	 on the entire text as a symbol.  */
-      list = make_symbol_completion_list (orig_text, word);
+      struct gdb_exception e;
+
+      TRY_CATCH (e, RETURN_MASK_ERROR)
+	{
+	  list = make_symbol_completion_list (tracker, orig_text, word);
+	}
+      if (e.reason < 0)
+	list = NULL;
     }
 
   return list;
@@ -389,7 +422,8 @@ add_struct_fields (struct type *type, VEC (char_ptr) **output,
    names, but some language parsers also have support for completing
    field names.  */
 VEC (char_ptr) *
-expression_completer (struct cmd_list_element *ignore, 
+expression_completer (struct cmd_list_element *ignore,
+		      completion_tracker_t tracker,
 		      const char *text, const char *word)
 {
   struct type *type = NULL;
@@ -434,7 +468,8 @@ expression_completer (struct cmd_list_element *ignore,
       VEC (char_ptr) *result;
       struct cleanup *cleanup = make_cleanup (xfree, fieldname);
 
-      result = make_symbol_completion_type (fieldname, fieldname, code);
+      result = make_symbol_completion_type (tracker, fieldname, fieldname,
+					    code);
       do_cleanups (cleanup);
       return result;
     }
@@ -448,7 +483,7 @@ expression_completer (struct cmd_list_element *ignore,
     ;
 
   /* Not ideal but it is what we used to do before...  */
-  return location_completer (ignore, p, word);
+  return location_completer (ignore, tracker, p, word);
 }
 
 /* See definition in completer.h.  */
@@ -526,7 +561,7 @@ complete_line_internal_reason;
  */
 
 static VEC (char_ptr) *
-complete_line_internal (const char *text, 
+complete_line_internal (completion_tracker_t tracker, const char *text,
 			const char *line_buffer, int point,
 			complete_line_internal_reason reason)
 {
@@ -572,7 +607,21 @@ complete_line_internal (const char *text,
     }
   else
     {
-      c = lookup_cmd_1 (&p, cmdlist, &result_list, ignore_help_classes);
+      /* There are two possible scenarios:
+	 1) The user is searching for completions to a command
+	 2) The user is attempting to complete the argument of
+	    a specific command.
+
+	In either case, lookup_cmd_1 will increment the tracker.
+	However, for case #2, the tracker's results must be cleared,
+	since we don't want the actual command lookup to be counted
+	by that command's completer function.  If we didn't do this,
+	we'd end up with an off-by-one error between the tracker
+	and the returned completion list.  */
+      c = lookup_cmd_1 (tracker, &p, cmdlist, &result_list,
+			ignore_help_classes);
+      if (c != CMD_LIST_AMBIGUOUS && tracker != NULL)
+	htab_empty (tracker);
     }
 
   /* Move p up to the next interesting thing.  */
@@ -658,7 +707,7 @@ complete_line_internal (const char *text,
 	      else if (c->enums)
 		{
 		  if (reason != handle_brkchars)
-		    list = complete_on_enum (c->enums, p, word);
+		    list = complete_on_enum (tracker, c->enums, p, word);
 		  rl_completer_word_break_characters =
 		    gdb_completer_command_word_break_characters;
 		}
@@ -698,7 +747,7 @@ complete_line_internal (const char *text,
 		      && c->completer_handle_brkchars != NULL)
 		    (*c->completer_handle_brkchars) (c, p, word);
 		  if (reason != handle_brkchars && c->completer != NULL)
-		    list = (*c->completer) (c, p, word);
+		    list = (*c->completer) (c, tracker, p, word);
 		}
 	    }
 	  else
@@ -744,7 +793,7 @@ complete_line_internal (const char *text,
 	  else if (c->enums)
 	    {
 	      if (reason != handle_brkchars)
-		list = complete_on_enum (c->enums, p, word);
+		list = complete_on_enum (tracker, c->enums, p, word);
 	    }
 	  else
 	    {
@@ -774,7 +823,7 @@ complete_line_internal (const char *text,
 		  && c->completer_handle_brkchars != NULL)
 		(*c->completer_handle_brkchars) (c, p, word);
 	      if (reason != handle_brkchars && c->completer != NULL)
-		list = (*c->completer) (c, p, word);
+		list = (*c->completer) (c, tracker, p, word);
 	    }
 	}
     }
@@ -825,7 +874,7 @@ make_cleanup_free_completion_tracker (completion_tracker_t *tracker_ptr)
 /* See completer.h.  */
 
 enum maybe_add_completion_enum
-maybe_add_completion (completion_tracker_t tracker, char *name)
+maybe_add_completion (completion_tracker_t tracker, const char *name)
 {
   void **slot;
 
@@ -844,7 +893,7 @@ maybe_add_completion (completion_tracker_t tracker, char *name)
   if (*slot != HTAB_EMPTY_ENTRY)
     return MAYBE_ADD_COMPLETION_DUPLICATE;
 
-  *slot = name;
+  *slot = (void *) name;
 
   return (htab_elements (tracker) < max_completions
 	  ? MAYBE_ADD_COMPLETION_OK
@@ -875,52 +924,75 @@ complete_line (const char *text, const char *line_buffer, int point)
 {
   VEC (char_ptr) *list;
   VEC (char_ptr) *result = NULL;
-  struct cleanup *cleanups;
+  struct cleanup *tracker_cleanup, *cleanups;
   completion_tracker_t tracker;
   char *candidate;
   int ix, max_reached;
 
   if (max_completions == 0)
     return NULL;
-  list = complete_line_internal (text, line_buffer, point,
-				 handle_completions);
-  if (max_completions < 0)
-    return list;
 
   tracker = new_completion_tracker ();
   cleanups = make_cleanup_free_completion_tracker (&tracker);
-  make_cleanup_free_char_ptr_vec (list);
 
-  /* Do a final test for too many completions.  Individual completers may
-     do some of this, but are not required to.  Duplicates are also removed
-     here.  Otherwise the user is left scratching his/her head: readline and
-     complete_command will remove duplicates, and if removal of duplicates
-     there brings the total under max_completions the user may think gdb quit
-     searching too early.  */
+  list = complete_line_internal (tracker, text, line_buffer, point,
+				 handle_completions);
 
-  for (ix = 0, max_reached = 0;
-       !max_reached && VEC_iterate (char_ptr, list, ix, candidate);
-       ++ix)
+  if (max_completions < 0)
     {
-      enum maybe_add_completion_enum add_status;
+      do_cleanups (cleanups);
+      return list;
+    }
+
+  make_cleanup_free_char_ptr_vec (list);
+
+  /* If complete_line_internal returned more completions than were
+     recorded by the completion tracker, then the completer function that
+     was run does not support completion tracking.  In this case,
+     do a final test for too many completions.
 
-      add_status = maybe_add_completion (tracker, candidate);
+     Duplicates are also removed here.  Otherwise the user is left
+     scratching his/her head: readline and complete_command will remove
+     duplicates, and if removal of duplicates there brings the total under
+     max_completions the user may think gdb quit searching too early.  */
 
-      switch (add_status)
+  if (VEC_length (char_ptr, list) > htab_elements (tracker))
+    {
+      /* Clear the tracker so that we can re-use it to count the number
+	 of returned completions.  */
+      htab_empty (tracker);
+
+      for (ix = 0, max_reached = 0;
+	   !max_reached && VEC_iterate (char_ptr, list, ix, candidate);
+	   ++ix)
 	{
-	  case MAYBE_ADD_COMPLETION_OK:
-	    VEC_safe_push (char_ptr, result, xstrdup (candidate));
-	    break;
-	  case MAYBE_ADD_COMPLETION_OK_MAX_REACHED:
-	    VEC_safe_push (char_ptr, result, xstrdup (candidate));
-	    max_reached = 1;
-	    break;
-      	  case MAYBE_ADD_COMPLETION_MAX_REACHED:
-	    gdb_assert_not_reached ("more than max completions reached");
-	  case MAYBE_ADD_COMPLETION_DUPLICATE:
-	    break;
+	  enum maybe_add_completion_enum add_status;
+
+	  add_status = maybe_add_completion (tracker, candidate);
+
+	  switch (add_status)
+	    {
+	    case MAYBE_ADD_COMPLETION_OK:
+	      VEC_safe_push (char_ptr, result, xstrdup (candidate));
+	      break;
+	    case MAYBE_ADD_COMPLETION_OK_MAX_REACHED:
+	      VEC_safe_push (char_ptr, result, xstrdup (candidate));
+	      max_reached = 1;
+	      break;
+	    case MAYBE_ADD_COMPLETION_MAX_REACHED:
+	      gdb_assert_not_reached ("more than max completions reached");
+	    case MAYBE_ADD_COMPLETION_DUPLICATE:
+	      break;
+	    }
 	}
     }
+  else
+    {
+      /* There is a valid tracker for the completion -- simply copy
+	 the returned list into the return result.  */
+      for (ix = 0; VEC_iterate (char_ptr, list, ix, candidate); ++ix)
+	VEC_safe_push (char_ptr, result, xstrdup (candidate));
+    }
 
   do_cleanups (cleanups);
 
@@ -929,10 +1001,11 @@ complete_line (const char *text, const char *line_buffer, int point)
 
 /* Complete on command names.  Used by "help".  */
 VEC (char_ptr) *
-command_completer (struct cmd_list_element *ignore, 
+command_completer (struct cmd_list_element *ignore,
+		   completion_tracker_t tracker,
 		   const char *text, const char *word)
 {
-  return complete_line_internal (word, text, 
+  return complete_line_internal (tracker, word, text,
 				 strlen (text), handle_help);
 }
 
@@ -940,6 +1013,7 @@ command_completer (struct cmd_list_element *ignore,
 
 VEC (char_ptr) *
 signal_completer (struct cmd_list_element *ignore,
+		  completion_tracker_t ignore2,
 		  const char *text, const char *word)
 {
   VEC (char_ptr) *return_val = NULL;
@@ -970,6 +1044,7 @@ signal_completer (struct cmd_list_element *ignore,
 
 VEC (char_ptr) *
 reg_or_group_completer (struct cmd_list_element *ignore,
+			completion_tracker_t ingore2,
 			const char *text, const char *word)
 {
   VEC (char_ptr) *result = NULL;
@@ -1013,7 +1088,7 @@ gdb_completion_word_break_characters (void)
 {
   VEC (char_ptr) *list;
 
-  list = complete_line_internal (rl_line_buffer, rl_line_buffer, rl_point,
+  list = complete_line_internal (NULL, rl_line_buffer, rl_line_buffer, rl_point,
 				 handle_brkchars);
   gdb_assert (list == NULL);
   return rl_completer_word_break_characters;
diff --git a/gdb/completer.h b/gdb/completer.h
index 56e1a2b..7bc0543 100644
--- a/gdb/completer.h
+++ b/gdb/completer.h
@@ -18,7 +18,6 @@
 #define COMPLETER_H 1
 
 #include "gdb_vecs.h"
-#include "command.h"
 
 /* Types of functions in struct match_list_displayer.  */
 
@@ -33,6 +32,70 @@ typedef void mld_erase_entire_line_ftype (const struct match_list_displayer *);
 typedef void mld_beep_ftype (const struct match_list_displayer *);
 typedef int mld_read_key_ftype (const struct match_list_displayer *);
 
+/* Maximum number of candidates to consider before the completer
+   bails by throwing MAX_COMPLETIONS_REACHED_ERROR.  Negative values
+   disable limiting.  */
+
+extern int max_completions;
+
+/* Object to track how many unique completions have been generated.
+   Used to limit the size of generated completion lists.  */
+
+typedef htab_t completion_tracker_t;
+
+/* Create a new completion tracker.
+   The result is a hash table to track added completions, or NULL
+   if max_completions <= 0.  If max_completions < 0, tracking is disabled.
+   If max_completions == 0, the max is indeed zero.  */
+
+extern completion_tracker_t new_completion_tracker (void);
+
+/* Make a cleanup to free a completion tracker, and reset its pointer
+   to NULL.  */
+
+extern struct cleanup *make_cleanup_free_completion_tracker
+		      (completion_tracker_t *tracker_ptr);
+
+/* Return values for maybe_add_completion.  */
+
+enum maybe_add_completion_enum
+{
+  /* NAME has been recorded and max_completions has not been reached,
+     or completion tracking is disabled (max_completions < 0).  */
+  MAYBE_ADD_COMPLETION_OK,
+
+  /* NAME has been recorded and max_completions has been reached
+     (thus the caller can stop searching).  */
+  MAYBE_ADD_COMPLETION_OK_MAX_REACHED,
+
+  /* max-completions entries has been reached.
+     Whether NAME is a duplicate or not is not determined.  */
+  MAYBE_ADD_COMPLETION_MAX_REACHED,
+
+  /* NAME has already been recorded.
+     Note that this is never returned if completion tracking is disabled
+     (max_completions < 0).  */
+  MAYBE_ADD_COMPLETION_DUPLICATE
+};
+
+/* Add the completion NAME to the list of generated completions if
+   it is not there already.
+   If max_completions is negative, nothing is done, not even watching
+   for duplicates, and MAYBE_ADD_COMPLETION_OK is always returned.
+
+   If MAYBE_ADD_COMPLETION_MAX_REACHED is returned, callers are required to
+   record at least one more completion.  The final list will be pruned to
+   max_completions, but recording at least one more than max_completions is
+   the signal to the completion machinery that too many completions were
+   found.  */
+
+extern enum maybe_add_completion_enum
+  maybe_add_completion (completion_tracker_t tracker, const char *name);
+
+/* Wrapper to throw MAX_COMPLETIONS_REACHED_ERROR.  */
+
+extern void throw_max_completions_reached_error (void);
+
 /* Interface between CLI/TUI and gdb_match_list_displayer.  */
 
 struct match_list_displayer
@@ -76,106 +139,83 @@ extern char *readline_line_completion_function (const char *text,
 						int matches);
 
 extern VEC (char_ptr) *noop_completer (struct cmd_list_element *,
+				       completion_tracker_t,
 				       const char *, const char *);
 
 extern VEC (char_ptr) *filename_completer (struct cmd_list_element *,
+					   completion_tracker_t,
 					   const char *, const char *);
 
 extern VEC (char_ptr) *expression_completer (struct cmd_list_element *,
+					     completion_tracker_t,
 					     const char *, const char *);
 
 extern VEC (char_ptr) *location_completer (struct cmd_list_element *,
+					   completion_tracker_t,
 					   const char *, const char *);
 
 extern VEC (char_ptr) *command_completer (struct cmd_list_element *,
+					  completion_tracker_t,
 					  const char *, const char *);
 
 extern VEC (char_ptr) *signal_completer (struct cmd_list_element *,
+					 completion_tracker_t,
 					 const char *, const char *);
 
 extern VEC (char_ptr) *reg_or_group_completer (struct cmd_list_element *,
+					       completion_tracker_t,
 					       const char *, const char *);
 
 extern char *get_gdb_completer_quote_characters (void);
 
 extern char *gdb_completion_word_break_characters (void);
 
-/* Set the word break characters array to the corresponding set of
-   chars, based on FN.  This function is useful for cases when the
-   completer doesn't know the type of the completion until some
-   calculation is done (e.g., for Python functions).  */
-
-extern void set_gdb_completion_word_break_characters (completer_ftype *fn);
-
-/* Exported to linespec.c */
-
-extern const char *skip_quoted_chars (const char *, const char *,
-				      const char *);
-
-extern const char *skip_quoted (const char *);
-
-/* Maximum number of candidates to consider before the completer
-   bails by throwing MAX_COMPLETIONS_REACHED_ERROR.  Negative values
-   disable limiting.  */
-
-extern int max_completions;
-
-/* Object to track how many unique completions have been generated.
-   Used to limit the size of generated completion lists.  */
+typedef VEC (char_ptr) *completer_ftype (struct cmd_list_element *,
+					 completion_tracker_t,
+					 const char *, const char *);
 
-typedef htab_t completion_tracker_t;
+typedef void completer_ftype_void (struct cmd_list_element *,
+				   const char *, const char *);
 
-/* Create a new completion tracker.
-   The result is a hash table to track added completions, or NULL
-   if max_completions <= 0.  If max_completions < 0, tracking is disabled.
-   If max_completions == 0, the max is indeed zero.  */
+extern void set_cmd_completer (struct cmd_list_element *, completer_ftype *);
 
-extern completion_tracker_t new_completion_tracker (void);
+/* Set the completer_handle_brkchars callback.  */
 
-/* Make a cleanup to free a completion tracker, and reset its pointer
-   to NULL.  */
+extern void set_cmd_completer_handle_brkchars (struct cmd_list_element *,
+					       completer_ftype_void *);
 
-extern struct cleanup *make_cleanup_free_completion_tracker
-		      (completion_tracker_t *tracker_ptr);
-
-/* Return values for maybe_add_completion.  */
+/* Set the word break characters array to the corresponding set of
+   chars, based on FN.  This function is useful for cases when the
+   completer doesn't know the type of the completion until some
+   calculation is done (e.g., for Python functions).  */
 
-enum maybe_add_completion_enum
-{
-  /* NAME has been recorded and max_completions has not been reached,
-     or completion tracking is disabled (max_completions < 0).  */
-  MAYBE_ADD_COMPLETION_OK,
+extern void set_gdb_completion_word_break_characters (completer_ftype *fn);
 
-  /* NAME has been recorded and max_completions has been reached
-     (thus the caller can stop searching).  */
-  MAYBE_ADD_COMPLETION_OK_MAX_REACHED,
+extern VEC (char_ptr) *complete_on_cmdlist (struct cmd_list_element *,
+					    const char *, const char *, int);
 
-  /* max-completions entries has been reached.
-     Whether NAME is a duplicate or not is not determined.  */
-  MAYBE_ADD_COMPLETION_MAX_REACHED,
+extern VEC (char_ptr) *complete_on_enum (completion_tracker_t,
+					 const char *const *enumlist,
+					 const char *, const char *);
 
-  /* NAME has already been recorded.
-     Note that this is never returned if completion tracking is disabled
-     (max_completions < 0).  */
-  MAYBE_ADD_COMPLETION_DUPLICATE
-};
+/* Flag for an ambiguous cmd_list result.  */
+#define CMD_LIST_AMBIGUOUS ((struct cmd_list_element *) -1)
 
-/* Add the completion NAME to the list of generated completions if
-   it is not there already.
-   If max_completions is negative, nothing is done, not even watching
-   for duplicates, and MAYBE_ADD_COMPLETION_OK is always returned.
+extern struct cmd_list_element *lookup_cmd (const char **,
+					    struct cmd_list_element *, char *,
+					    int, int);
 
-   If MAYBE_ADD_COMPLETION_MAX_REACHED is returned, callers are required to
-   record at least one more completion.  The final list will be pruned to
-   max_completions, but recording at least one more than max_completions is
-   the signal to the completion machinery that too many completions were
-   found.  */
+extern struct cmd_list_element *lookup_cmd_1 (completion_tracker_t,
+					      const char **,
+					      struct cmd_list_element *,
+					      struct cmd_list_element **,
+					      int);
 
-extern enum maybe_add_completion_enum
-  maybe_add_completion (completion_tracker_t tracker, char *name);
+/* Exported to linespec.c */
 
-/* Wrapper to throw MAX_COMPLETIONS_REACHED_ERROR.  */ 
+extern const char *skip_quoted_chars (const char *, const char *,
+				      const char *);
 
-extern void throw_max_completions_reached_error (void);
+extern const char *skip_quoted (const char *);
 
 #endif /* defined (COMPLETER_H) */
diff --git a/gdb/corefile.c b/gdb/corefile.c
index a042e6d..309a93d 100644
--- a/gdb/corefile.c
+++ b/gdb/corefile.c
@@ -469,6 +469,7 @@ set_gnutarget_command (char *ignore, int from_tty,
 
 static VEC (char_ptr) *
 complete_set_gnutarget (struct cmd_list_element *cmd,
+			completion_tracker_t tracker,
 			const char *text, const char *word)
 {
   static const char **bfd_targets;
@@ -486,7 +487,7 @@ complete_set_gnutarget (struct cmd_list_element *cmd,
       bfd_targets[last + 1] = NULL;
     }
 
-  return complete_on_enum (bfd_targets, text, word);
+  return complete_on_enum (tracker, bfd_targets, text, word);
 }
 
 /* Set the gnutarget.  */
diff --git a/gdb/cp-abi.c b/gdb/cp-abi.c
index 9316c4c..4086d0c 100644
--- a/gdb/cp-abi.c
+++ b/gdb/cp-abi.c
@@ -355,6 +355,7 @@ set_cp_abi_cmd (char *args, int from_tty)
 
 static VEC (char_ptr) *
 cp_abi_completer (struct cmd_list_element *ignore,
+		  completion_tracker_t tracker,
 		  const char *text, const char *word)
 {
   static const char **cp_abi_names;
@@ -369,7 +370,7 @@ cp_abi_completer (struct cmd_list_element *ignore,
       cp_abi_names[i] = NULL;
     }
 
-  return complete_on_enum (cp_abi_names, text, word);
+  return complete_on_enum (tracker, cp_abi_names, text, word);
 }
 
 /* Show the currently selected C++ ABI.  */
diff --git a/gdb/f-lang.c b/gdb/f-lang.c
index 8b61028..d772cfa 100644
--- a/gdb/f-lang.c
+++ b/gdb/f-lang.c
@@ -229,10 +229,12 @@ f_word_break_characters (void)
    class.  */
 
 static VEC (char_ptr) *
-f_make_symbol_completion_list (const char *text, const char *word,
+f_make_symbol_completion_list (completion_tracker_t tracker,
+			       const char *text, const char *word,
 			       enum type_code code)
 {
-  return default_make_symbol_completion_list_break_on (text, word, ":", code);
+  return default_make_symbol_completion_list_break_on (tracker, text, word,
+						       ":", code);
 }
 
 const struct language_defn f_language_defn =
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 15589b6..75fe2e3 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -6934,6 +6934,7 @@ Are you sure you want to change it? "),
 
 static VEC (char_ptr) *
 handle_completer (struct cmd_list_element *ignore,
+		  completion_tracker_t tracker,
 		  const char *text, const char *word)
 {
   VEC (char_ptr) *vec_signals, *vec_keywords, *return_val;
@@ -6951,8 +6952,8 @@ handle_completer (struct cmd_list_element *ignore,
       NULL,
     };
 
-  vec_signals = signal_completer (ignore, text, word);
-  vec_keywords = complete_on_enum (keywords, word, word);
+  vec_signals = signal_completer (ignore, tracker, text, word);
+  vec_keywords = complete_on_enum (tracker, keywords, word, word);
 
   return_val = VEC_merge (char_ptr, vec_signals, vec_keywords);
   VEC_free (char_ptr, vec_signals);
diff --git a/gdb/interps.c b/gdb/interps.c
index 90b5b2d..2215ac1 100644
--- a/gdb/interps.c
+++ b/gdb/interps.c
@@ -438,6 +438,7 @@ interpreter_exec_cmd (char *args, int from_tty)
 /* List the possible interpreters which could complete the given text.  */
 static VEC (char_ptr) *
 interpreter_completer (struct cmd_list_element *ignore,
+		       completion_tracker_t ingore2,
 		       const char *text, const char *word)
 {
   int textlen;
diff --git a/gdb/language.h b/gdb/language.h
index 436fd6e..b095093 100644
--- a/gdb/language.h
+++ b/gdb/language.h
@@ -301,7 +301,8 @@ struct language_defn
        completion is being made.  If CODE is TYPE_CODE_UNDEF, then all
        symbols should be examined; otherwise, only STRUCT_DOMAIN
        symbols whose type has a code of CODE should be matched.  */
-    VEC (char_ptr) *(*la_make_symbol_completion_list) (const char *text,
+    VEC (char_ptr) *(*la_make_symbol_completion_list) (completion_tracker_t,
+						       const char *text,
 						       const char *word,
 						       enum type_code code);
 
diff --git a/gdb/python/py-cmd.c b/gdb/python/py-cmd.c
index a5e96d6..2f95b46 100644
--- a/gdb/python/py-cmd.c
+++ b/gdb/python/py-cmd.c
@@ -347,6 +347,7 @@ cmdpy_completer_handle_brkchars (struct cmd_list_element *command,
 
 static VEC (char_ptr) *
 cmdpy_completer (struct cmd_list_element *command,
+		 completion_tracker_t tracker,
 		 const char *text, const char *word)
 {
   PyObject *resultobj = NULL;
@@ -378,7 +379,7 @@ cmdpy_completer (struct cmd_list_element *command,
 	  PyErr_Clear ();
 	}
       else if (value >= 0 && value < (long) N_COMPLETERS)
-	result = completers[value].completer (command, text, word);
+	result = completers[value].completer (command, tracker, text, word);
     }
   else
     {
@@ -485,7 +486,7 @@ gdbpy_parse_command_name (const char *name,
   prefix_text[i + 1] = '\0';
 
   prefix_text2 = prefix_text;
-  elt = lookup_cmd_1 (&prefix_text2, *start_list, NULL, 1);
+  elt = lookup_cmd_1 (NULL, &prefix_text2, *start_list, NULL, 1);
   if (elt == NULL || elt == CMD_LIST_AMBIGUOUS)
     {
       PyErr_Format (PyExc_RuntimeError, _("Could not find command prefix %s."),
diff --git a/gdb/symtab.c b/gdb/symtab.c
index 5302afa..9f04966 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -4994,29 +4994,21 @@ do_free_completion_list (void *list)
 
 static VEC (char_ptr) *return_val;
 
-#define COMPLETION_LIST_ADD_SYMBOL(symbol, sym_text, len, text, word) \
-      completion_list_add_name \
-	(SYMBOL_NATURAL_NAME (symbol), (sym_text), (len), (text), (word))
+#define COMPLETION_LIST_ADD_SYMBOL(tracker, symbol, sym_text, len, text, word) \
+  completion_list_add_name \
+    (tracker, SYMBOL_NATURAL_NAME (symbol), (sym_text), (len), (text), (word))
 
-#define MCOMPLETION_LIST_ADD_SYMBOL(symbol, sym_text, len, text, word) \
-      completion_list_add_name \
-	(MSYMBOL_NATURAL_NAME (symbol), (sym_text), (len), (text), (word))
-
-/* Tracker for how many unique completions have been generated.  Used
-   to terminate completion list generation early if the list has grown
-   to a size so large as to be useless.  This helps avoid GDB seeming
-   to lock up in the event the user requests to complete on something
-   vague that necessitates the time consuming expansion of many symbol
-   tables.  */
-
-static completion_tracker_t completion_tracker;
+#define MCOMPLETION_LIST_ADD_SYMBOL(tracker, symbol, sym_text, len, text, word) \
+  completion_list_add_name \
+    ((tracker), MSYMBOL_NATURAL_NAME (symbol), (sym_text), (len), (text), (word))
 
 /*  Test to see if the symbol specified by SYMNAME (which is already
    demangled for C++ symbols) matches SYM_TEXT in the first SYM_TEXT_LEN
    characters.  If so, add it to the current completion list.  */
 
 static void
-completion_list_add_name (const char *symname,
+completion_list_add_name (completion_tracker_t tracker,
+			  const char *symname,
 			  const char *sym_text, int sym_text_len,
 			  const char *text, const char *word)
 {
@@ -5051,7 +5043,7 @@ completion_list_add_name (const char *symname,
 	strcat (new, symname);
       }
 
-    add_status = maybe_add_completion (completion_tracker, new);
+    add_status = maybe_add_completion (tracker, new);
 
     switch (add_status)
       {
@@ -5075,7 +5067,8 @@ completion_list_add_name (const char *symname,
    again and feed all the selectors into the mill.  */
 
 static void
-completion_list_objc_symbol (struct minimal_symbol *msymbol,
+completion_list_objc_symbol (completion_tracker_t tracker,
+			     struct minimal_symbol *msymbol,
 			     const char *sym_text, int sym_text_len,
 			     const char *text, const char *word)
 {
@@ -5093,7 +5086,8 @@ completion_list_objc_symbol (struct minimal_symbol *msymbol,
 
   if (sym_text[0] == '[')
     /* Complete on shortened method method.  */
-    completion_list_add_name (method + 1, sym_text, sym_text_len, text, word);
+    completion_list_add_name (tracker, method + 1, sym_text, sym_text_len,
+			      text, word);
 
   while ((strlen (method) + 1) >= tmplen)
     {
@@ -5114,9 +5108,11 @@ completion_list_objc_symbol (struct minimal_symbol *msymbol,
       memcpy (tmp, method, (category - method));
       tmp[category - method] = ' ';
       memcpy (tmp + (category - method) + 1, selector, strlen (selector) + 1);
-      completion_list_add_name (tmp, sym_text, sym_text_len, text, word);
+      completion_list_add_name (tracker, tmp, sym_text, sym_text_len, text,
+				word);
       if (sym_text[0] == '[')
-	completion_list_add_name (tmp + 1, sym_text, sym_text_len, text, word);
+	completion_list_add_name (tracker, tmp + 1, sym_text, sym_text_len,
+				  text, word);
     }
 
   if (selector != NULL)
@@ -5127,7 +5123,8 @@ completion_list_objc_symbol (struct minimal_symbol *msymbol,
       if (tmp2 != NULL)
 	*tmp2 = '\0';
 
-      completion_list_add_name (tmp, sym_text, sym_text_len, text, word);
+      completion_list_add_name (tracker, tmp, sym_text, sym_text_len, text,
+				word);
     }
 }
 
@@ -5178,9 +5175,9 @@ language_search_unquoted_string (const char *text, const char *p)
 }
 
 static void
-completion_list_add_fields (struct symbol *sym, const char *sym_text,
-			    int sym_text_len, const char *text,
-			    const char *word)
+completion_list_add_fields (completion_tracker_t tracker, struct symbol *sym,
+			    const char *sym_text, int sym_text_len,
+			    const char *text, const char *word)
 {
   if (SYMBOL_CLASS (sym) == LOC_TYPEDEF)
     {
@@ -5191,7 +5188,7 @@ completion_list_add_fields (struct symbol *sym, const char *sym_text,
       if (c == TYPE_CODE_UNION || c == TYPE_CODE_STRUCT)
 	for (j = TYPE_N_BASECLASSES (t); j < TYPE_NFIELDS (t); j++)
 	  if (TYPE_FIELD_NAME (t, j))
-	    completion_list_add_name (TYPE_FIELD_NAME (t, j),
+	    completion_list_add_name (tracker, TYPE_FIELD_NAME (t, j),
 				      sym_text, sym_text_len, text, word);
     }
 }
@@ -5206,6 +5203,7 @@ struct add_name_data
   int sym_text_len;
   const char *text;
   const char *word;
+  completion_tracker_t tracker;
 
   /* Extra argument required for add_symtab_completions.  */
   enum type_code code;
@@ -5221,7 +5219,7 @@ add_macro_name (const char *name, const struct macro_definition *ignore,
 {
   struct add_name_data *datum = (struct add_name_data *) user_data;
 
-  completion_list_add_name (name,
+  completion_list_add_name (datum->tracker, name,
 			    datum->sym_text, datum->sym_text_len,
 			    datum->text, datum->word);
 }
@@ -5240,6 +5238,7 @@ symbol_completion_matcher (const char *name, void *user_data)
 
 static void
 add_symtab_completions (struct compunit_symtab *cust,
+			completion_tracker_t tracker,
 			const char *sym_text, int sym_text_len,
 			const char *text, const char *word,
 			enum type_code code)
@@ -5258,7 +5257,7 @@ add_symtab_completions (struct compunit_symtab *cust,
 	  if (code == TYPE_CODE_UNDEF
 	      || (SYMBOL_DOMAIN (sym) == STRUCT_DOMAIN
 		  && TYPE_CODE (SYMBOL_TYPE (sym)) == code))
-	    COMPLETION_LIST_ADD_SYMBOL (sym,
+	    COMPLETION_LIST_ADD_SYMBOL (tracker, sym,
 					sym_text, sym_text_len,
 					text, word);
 	}
@@ -5274,14 +5273,15 @@ symtab_expansion_callback (struct compunit_symtab *symtab,
 {
   struct add_name_data *datum = (struct add_name_data *) user_data;
 
-  add_symtab_completions (symtab,
+  add_symtab_completions (symtab, datum->tracker,
 			  datum->sym_text, datum->sym_text_len,
 			  datum->text, datum->word,
 			  datum->code);
 }
 
 static void
-default_make_symbol_completion_list_break_on_1 (const char *text,
+default_make_symbol_completion_list_break_on_1 (completion_tracker_t tracker,
+						const char *text,
 						const char *word,
 						const char *break_on,
 						enum type_code code)
@@ -5302,7 +5302,6 @@ default_make_symbol_completion_list_break_on_1 (const char *text,
   /* Length of sym_text.  */
   int sym_text_len;
   struct add_name_data datum;
-  struct cleanup *cleanups;
 
   /* Now look for the symbol we are supposed to complete on.  */
   {
@@ -5373,14 +5372,12 @@ default_make_symbol_completion_list_break_on_1 (const char *text,
     }
   gdb_assert (sym_text[sym_text_len] == '\0' || sym_text[sym_text_len] == '(');
 
-  completion_tracker = new_completion_tracker ();
-  cleanups = make_cleanup_free_completion_tracker (&completion_tracker);
-
   datum.sym_text = sym_text;
   datum.sym_text_len = sym_text_len;
   datum.text = text;
   datum.word = word;
   datum.code = code;
+  datum.tracker = tracker;
 
   /* At this point scan through the misc symbol vectors and add each
      symbol you find to the list.  Eventually we want to ignore
@@ -5392,17 +5389,17 @@ default_make_symbol_completion_list_break_on_1 (const char *text,
       ALL_MSYMBOLS (objfile, msymbol)
 	{
 	  QUIT;
-	  MCOMPLETION_LIST_ADD_SYMBOL (msymbol, sym_text, sym_text_len, text,
-				       word);
+	  MCOMPLETION_LIST_ADD_SYMBOL (tracker, msymbol, sym_text,
+				       sym_text_len, text, word);
 
-	  completion_list_objc_symbol (msymbol, sym_text, sym_text_len, text,
-				       word);
+	  completion_list_objc_symbol (tracker, msymbol, sym_text,
+				       sym_text_len, text, word);
 	}
     }
 
   /* Add completions for all currently loaded symbol tables.  */
   ALL_COMPUNITS (objfile, cust)
-    add_symtab_completions (cust, sym_text, sym_text_len, text, word,
+    add_symtab_completions (cust, tracker, sym_text, sym_text_len, text, word,
 			    code);
 
   /* Look through the partial symtabs for all symbols which begin
@@ -5430,15 +5427,15 @@ default_make_symbol_completion_list_break_on_1 (const char *text,
 	  {
 	    if (code == TYPE_CODE_UNDEF)
 	      {
-		COMPLETION_LIST_ADD_SYMBOL (sym, sym_text, sym_text_len, text,
-					    word);
-		completion_list_add_fields (sym, sym_text, sym_text_len, text,
-					    word);
+		COMPLETION_LIST_ADD_SYMBOL (tracker, sym, sym_text,
+					    sym_text_len, text, word);
+		completion_list_add_fields (tracker, sym, sym_text,
+					    sym_text_len, text, word);
 	      }
 	    else if (SYMBOL_DOMAIN (sym) == STRUCT_DOMAIN
 		     && TYPE_CODE (SYMBOL_TYPE (sym)) == code)
-	      COMPLETION_LIST_ADD_SYMBOL (sym, sym_text, sym_text_len, text,
-					  word);
+	      COMPLETION_LIST_ADD_SYMBOL (tracker, sym, sym_text,
+					  sym_text_len, text, word);
 	  }
 
 	/* Stop when we encounter an enclosing function.  Do not stop for
@@ -5455,11 +5452,13 @@ default_make_symbol_completion_list_break_on_1 (const char *text,
     {
       if (surrounding_static_block != NULL)
 	ALL_BLOCK_SYMBOLS (surrounding_static_block, iter, sym)
-	  completion_list_add_fields (sym, sym_text, sym_text_len, text, word);
+	  completion_list_add_fields (tracker, sym, sym_text, sym_text_len,
+				      text, word);
 
       if (surrounding_global_block != NULL)
 	ALL_BLOCK_SYMBOLS (surrounding_global_block, iter, sym)
-	  completion_list_add_fields (sym, sym_text, sym_text_len, text, word);
+	  completion_list_add_fields (tracker, sym, sym_text, sym_text_len,
+				      text, word);
     }
 
   /* Skip macros if we are completing a struct tag -- arguable but
@@ -5487,12 +5486,11 @@ default_make_symbol_completion_list_break_on_1 (const char *text,
       /* User-defined macros are always visible.  */
       macro_for_each (macro_user_macros, add_macro_name, &datum);
     }
-
-  do_cleanups (cleanups);
 }
 
 VEC (char_ptr) *
-default_make_symbol_completion_list_break_on (const char *text,
+default_make_symbol_completion_list_break_on (completion_tracker_t tracker,
+					      const char *text,
 					      const char *word,
 					      const char *break_on,
 					      enum type_code code)
@@ -5505,7 +5503,7 @@ default_make_symbol_completion_list_break_on (const char *text,
 
   TRY_CATCH (except, RETURN_MASK_ERROR)
     {
-      default_make_symbol_completion_list_break_on_1 (text, word,
+      default_make_symbol_completion_list_break_on_1 (tracker, text, word,
 						      break_on, code);
     }
   if (except.reason < 0)
@@ -5519,10 +5517,12 @@ default_make_symbol_completion_list_break_on (const char *text,
 }
 
 VEC (char_ptr) *
-default_make_symbol_completion_list (const char *text, const char *word,
+default_make_symbol_completion_list (completion_tracker_t tracker,
+				     const char *text, const char *word,
 				     enum type_code code)
 {
-  return default_make_symbol_completion_list_break_on (text, word, "", code);
+  return default_make_symbol_completion_list_break_on (tracker, text, word,
+						       "", code);
 }
 
 /* Return a vector of all symbols (regardless of class) which begin by
@@ -5530,9 +5530,10 @@ default_make_symbol_completion_list (const char *text, const char *word,
    is NULL.  */
 
 VEC (char_ptr) *
-make_symbol_completion_list (const char *text, const char *word)
+make_symbol_completion_list (completion_tracker_t tracker, const char *text,
+			     const char *word)
 {
-  return current_language->la_make_symbol_completion_list (text, word,
+  return current_language->la_make_symbol_completion_list (tracker, text, word,
 							   TYPE_CODE_UNDEF);
 }
 
@@ -5540,13 +5541,14 @@ make_symbol_completion_list (const char *text, const char *word)
    symbols whose type code is CODE.  */
 
 VEC (char_ptr) *
-make_symbol_completion_type (const char *text, const char *word,
-			     enum type_code code)
+make_symbol_completion_type (completion_tracker_t tracker, const char *text,
+			     const char *word, enum type_code code)
 {
   gdb_assert (code == TYPE_CODE_UNION
 	      || code == TYPE_CODE_STRUCT
 	      || code == TYPE_CODE_ENUM);
-  return current_language->la_make_symbol_completion_list (text, word, code);
+  return current_language->la_make_symbol_completion_list (tracker, text,
+							   word, code);
 }
 
 /* Like make_symbol_completion_list, but suitable for use as a
@@ -5554,16 +5556,18 @@ make_symbol_completion_type (const char *text, const char *word,
 
 VEC (char_ptr) *
 make_symbol_completion_list_fn (struct cmd_list_element *ignore,
+				completion_tracker_t tracker,
 				const char *text, const char *word)
 {
-  return make_symbol_completion_list (text, word);
+  return make_symbol_completion_list (tracker, text, word);
 }
 
 /* Like make_symbol_completion_list, but returns a list of symbols
    defined in a source file FILE.  */
 
 VEC (char_ptr) *
-make_file_symbol_completion_list (const char *text, const char *word,
+make_file_symbol_completion_list (completion_tracker_t tracker,
+				  const char *text, const char *word,
 				  const char *srcfile)
 {
   struct symbol *sym;
@@ -5645,13 +5649,15 @@ make_file_symbol_completion_list (const char *text, const char *word,
   b = BLOCKVECTOR_BLOCK (SYMTAB_BLOCKVECTOR (s), GLOBAL_BLOCK);
   ALL_BLOCK_SYMBOLS (b, iter, sym)
     {
-      COMPLETION_LIST_ADD_SYMBOL (sym, sym_text, sym_text_len, text, word);
+      COMPLETION_LIST_ADD_SYMBOL (tracker, sym, sym_text, sym_text_len, text,
+				  word);
     }
 
   b = BLOCKVECTOR_BLOCK (SYMTAB_BLOCKVECTOR (s), STATIC_BLOCK);
   ALL_BLOCK_SYMBOLS (b, iter, sym)
     {
-      COMPLETION_LIST_ADD_SYMBOL (sym, sym_text, sym_text_len, text, word);
+      COMPLETION_LIST_ADD_SYMBOL (tracker, sym, sym_text, sym_text_len, text,
+				  word);
     }
 
   return (return_val);
@@ -5662,11 +5668,13 @@ make_file_symbol_completion_list (const char *text, const char *word,
    list as necessary.  */
 
 static void
-add_filename_to_list (const char *fname, const char *text, const char *word,
+add_filename_to_list (completion_tracker_t tracker,
+		      const char *fname, const char *text, const char *word,
 		      VEC (char_ptr) **list)
 {
   char *new;
   size_t fnlen = strlen (fname);
+  enum maybe_add_completion_enum add_status;
 
   if (word == text)
     {
@@ -5688,7 +5696,22 @@ add_filename_to_list (const char *fname, const char *text, const char *word,
       new[text - word] = '\0';
       strcat (new, fname);
     }
-  VEC_safe_push (char_ptr, *list, new);
+
+  add_status = maybe_add_completion (tracker, new);
+  switch (add_status)
+    {
+    case MAYBE_ADD_COMPLETION_OK:
+      VEC_safe_push (char_ptr, *list, new);
+      break;
+    case MAYBE_ADD_COMPLETION_OK_MAX_REACHED:
+      VEC_safe_push (char_ptr, *list, new);
+      /* fall through  */
+    case MAYBE_ADD_COMPLETION_MAX_REACHED:
+      throw_max_completions_reached_error ();
+      break;
+    case MAYBE_ADD_COMPLETION_DUPLICATE:
+      xfree (new);
+    }
 }
 
 static int
@@ -5717,6 +5740,7 @@ struct add_partial_filename_data
   const char *word;
   int text_len;
   VEC (char_ptr) **list;
+  completion_tracker_t tracker;
 };
 
 /* A callback for map_partial_symbol_filenames.  */
@@ -5734,7 +5758,8 @@ maybe_add_partial_symtab_filename (const char *filename, const char *fullname,
     {
       /* This file matches for a completion; add it to the
 	 current list of matches.  */
-      add_filename_to_list (filename, data->text, data->word, data->list);
+      add_filename_to_list (data->tracker, filename, data->text, data->word,
+			    data->list);
     }
   else
     {
@@ -5743,7 +5768,8 @@ maybe_add_partial_symtab_filename (const char *filename, const char *fullname,
       if (base_name != filename
 	  && !filename_seen (data->filename_seen_cache, base_name, 1)
 	  && filename_ncmp (base_name, data->text, data->text_len) == 0)
-	add_filename_to_list (base_name, data->text, data->word, data->list);
+	add_filename_to_list (data->tracker, base_name, data->text,
+			      data->word, data->list);
     }
 }
 
@@ -5753,7 +5779,8 @@ maybe_add_partial_symtab_filename (const char *filename, const char *fullname,
    NULL.  */
 
 VEC (char_ptr) *
-make_source_files_completion_list (const char *text, const char *word)
+make_source_files_completion_list (completion_tracker_t tracker,
+				   const char *text, const char *word)
 {
   struct compunit_symtab *cu;
   struct symtab *s;
@@ -5783,7 +5810,7 @@ make_source_files_completion_list (const char *text, const char *word)
 	{
 	  /* This file matches for a completion; add it to the current
 	     list of matches.  */
-	  add_filename_to_list (s->filename, text, word, &list);
+	  add_filename_to_list (tracker, s->filename, text, word, &list);
 	}
       else
 	{
@@ -5795,7 +5822,7 @@ make_source_files_completion_list (const char *text, const char *word)
 	  if (base_name != s->filename
 	      && !filename_seen (filename_seen_cache, base_name, 1)
 	      && filename_ncmp (base_name, text, text_len) == 0)
-	    add_filename_to_list (base_name, text, word, &list);
+	    add_filename_to_list (tracker, base_name, text, word, &list);
 	}
     }
 
@@ -5804,6 +5831,7 @@ make_source_files_completion_list (const char *text, const char *word)
   datum.word = word;
   datum.text_len = text_len;
   datum.list = &list;
+  datum.tracker = tracker;
   map_symbol_filenames (maybe_add_partial_symtab_filename, &datum,
 			0 /*need_fullname*/);
 
diff --git a/gdb/symtab.h b/gdb/symtab.h
index 0eb3a5b..572738a 100644
--- a/gdb/symtab.h
+++ b/gdb/symtab.h
@@ -23,6 +23,7 @@
 #include "vec.h"
 #include "gdb_vecs.h"
 #include "gdbtypes.h"
+#include "completer.h"
 
 /* Opaque declarations.  */
 struct ui_file;
@@ -1444,24 +1445,22 @@ extern void forget_cached_source_info (void);
 extern void select_source_symtab (struct symtab *);
 
 extern VEC (char_ptr) *default_make_symbol_completion_list_break_on
-  (const char *text, const char *word, const char *break_on,
-   enum type_code code);
-extern VEC (char_ptr) *default_make_symbol_completion_list (const char *,
-							    const char *,
-							    enum type_code);
-extern VEC (char_ptr) *make_symbol_completion_list (const char *, const char *);
-extern VEC (char_ptr) *make_symbol_completion_type (const char *, const char *,
-						    enum type_code);
-extern VEC (char_ptr) *make_symbol_completion_list_fn (struct cmd_list_element *,
-						       const char *,
-						       const char *);
-
-extern VEC (char_ptr) *make_file_symbol_completion_list (const char *,
-							 const char *,
-							 const char *);
-
-extern VEC (char_ptr) *make_source_files_completion_list (const char *,
-							  const char *);
+  (completion_tracker_t tracker, const char *text, const char *word,
+   const char *break_on, enum type_code code);
+extern VEC (char_ptr) *default_make_symbol_completion_list
+  (completion_tracker_t tracker, const char *, const char *, enum type_code);
+extern VEC (char_ptr) *make_symbol_completion_list
+  (completion_tracker_t tracker, const char *, const char *);
+extern VEC (char_ptr) *make_symbol_completion_type
+  (completion_tracker_t, const char *, const char *, enum type_code);
+extern VEC (char_ptr) *make_symbol_completion_list_fn
+  (struct cmd_list_element *, completion_tracker_t, const char *, const char *);
+
+extern VEC (char_ptr) *make_file_symbol_completion_list
+  (completion_tracker_t, const char *, const char *, const char *);
+
+extern VEC (char_ptr) *make_source_files_completion_list
+  (completion_tracker_t, const char *, const char *);
 
 /* symtab.c */
 
diff --git a/gdb/testsuite/gdb.base/completion.exp b/gdb/testsuite/gdb.base/completion.exp
index f77bfe2..5afd851 100644
--- a/gdb/testsuite/gdb.base/completion.exp
+++ b/gdb/testsuite/gdb.base/completion.exp
@@ -777,6 +777,84 @@ gdb_test_multiple "" "$test" {
 }
 
 #
+# Tests for the location completer
+#
+
+# Turn off pending breakpoint support so that we don't get queried
+# all the time.
+gdb_test_no_output "set breakpoint pending off"
+
+set subsrc [string range $srcfile 0 [expr {[string length $srcfile] - 3}]]
+set test "tab complete break $subsrc"
+send_gdb "break $subsrc\t\t"
+gdb_test_multiple "" $test {
+    -re "break\.c.*break1\.c.*$gdb_prompt " {
+	send_gdb "1\t\n"
+	gdb_test_multiple "" $test {
+	    -re ".*Function \"$srcfile2\" not defined\..*$gdb_prompt " {
+		pass $test
+	    }
+	    -re "$gdb_prompt p$" {
+		fail $test
+	    }
+	}
+    }
+
+    -re "$gdb_prompt p$" {
+	fail $test
+    }
+}
+
+gdb_test "complete break $subsrc" "break\.c.*break1\.c"
+
+# gdb/17960
+set test "tab complete break $srcfile:ma"
+send_gdb "break $srcfile:ma\t"
+gdb_test_multiple "" $test {
+    -re "break $srcfile:main " {
+	send_gdb "\n"
+	gdb_test_multiple "" $test {
+	    -re ".*Breakpoint.*at .*/$srcfile, line .*$gdb_prompt " {
+		pass $test
+		gdb_test_no_output "delete breakpoint \$bpnum" \
+		    "delete breakpoint for $test"
+	    }
+	    -re "$gdb_prompt p$" {
+		fail $test
+	    }
+	}
+    }
+    -re "$gdb_prompt p$" {
+	fail $test
+    }
+}
+
+gdb_test "complete break $srcfile:ma" "break\.c:main"
+
+set test "tab complete break need"
+send_gdb "break need\t"
+gdb_test_multiple "" $test {
+    -re "break need_malloc " {
+	send_gdb "\n"
+	gdb_test_multiple "" $test {
+	    -re ".*Breakpoint.*at .*/$srcfile, line .*$gdb_prompt " {
+		pass $test
+		gdb_test_no_output "delete breakpoint \$bpnum" \
+		    "delete breakpoint for $test"
+	    }
+	    -re "$gdb_prompt p$" {
+		fail $test
+	    }
+	}
+    }
+    -re "$gdb_prompt p$" {
+	fail $test
+    }
+}
+
+gdb_test "complete break need" "need_malloc"
+
+#
 # Completion limiting.
 #
 
diff --git a/gdb/top.c b/gdb/top.c
index 8242e12..af5d923 100644
--- a/gdb/top.c
+++ b/gdb/top.c
@@ -1673,7 +1673,7 @@ set_verbose (char *args, int from_tty, struct cmd_list_element *c)
   const char *cmdname = "verbose";
   struct cmd_list_element *showcmd;
 
-  showcmd = lookup_cmd_1 (&cmdname, showlist, NULL, 1);
+  showcmd = lookup_cmd_1 (NULL, &cmdname, showlist, NULL, 1);
   gdb_assert (showcmd != NULL && showcmd != CMD_LIST_AMBIGUOUS);
 
   if (info_verbose)


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