Introduce class completion_tracker & rewrite completion<->readline interaction
[deliverable/binutils-gdb.git] / gdb / completer.c
index 625ff4f561bbb287c9ca846ef8acec68227c9254..85e6d88b7423acecadff89d085fb3b54e5418495 100644 (file)
@@ -28,7 +28,7 @@
 #include "user-regs.h"
 #include "arch-utils.h"
 #include "location.h"
-
+#include <algorithm>
 #include "cli/cli-decode.h"
 
 /* FIXME: This is needed because of lookup_cmd_1 ().  We should be
 
 #include "completer.h"
 
+/* Misc state that needs to be tracked across several different
+   readline completer entry point calls, all related to a single
+   completion invocation.  */
+
+struct gdb_completer_state
+{
+  /* The current completion's completion tracker.  This is a global
+     because a tracker can be shared between the handle_brkchars and
+     handle_completion phases, which involves different readline
+     callbacks.  */
+  completion_tracker *tracker = NULL;
+
+  /* Whether the current completion was aborted.  */
+  bool aborted = false;
+};
+
+/* The current completion state.  */
+static gdb_completer_state current_completion;
+
 /* An enumeration of the various things a user might
    attempt to complete for a location.  */
 
@@ -60,10 +79,6 @@ enum explicit_location_match_type
 };
 
 /* Prototypes for local functions.  */
-static
-char *line_completion_function (const char *text, int matches, 
-                               char *line_buffer,
-                               int point);
 
 /* readline uses the word breaks for two things:
    (1) In figuring out where to point the TEXT parameter to the
@@ -113,28 +128,21 @@ get_gdb_completer_quote_characters (void)
   return gdb_completer_quote_characters;
 }
 
-/* Line completion interface function for readline.  */
-
-char *
-readline_line_completion_function (const char *text, int matches)
-{
-  return line_completion_function (text, matches, 
-                                  rl_line_buffer, rl_point);
-}
-
 /* 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) *
+
+void
 noop_completer (struct cmd_list_element *ignore, 
+               completion_tracker &tracker,
                const char *text, const char *prefix)
 {
-  return NULL;
 }
 
 /* Complete on filenames.  */
 
-VEC (char_ptr) *
-filename_completer (struct cmd_list_element *ignore, 
+void
+filename_completer (struct cmd_list_element *ignore,
+                   completion_tracker &tracker,
                    const char *text, const char *word)
 {
   int subsequent_name;
@@ -180,7 +188,7 @@ filename_completer (struct cmd_list_element *ignore,
          strcat (q, p);
          xfree (p);
        }
-      VEC_safe_push (char_ptr, return_val, q);
+      tracker.add_completion (gdb::unique_xmalloc_ptr<char> (q));
     }
 #if 0
   /* There is no way to do this just long enough to affect quote
@@ -190,7 +198,6 @@ filename_completer (struct cmd_list_element *ignore,
      with respect to inserting quotes.  */
   rl_completer_word_break_characters = "";
 #endif
-  return return_val;
 }
 
 /* The corresponding completer_handle_brkchars
@@ -198,6 +205,7 @@ filename_completer (struct cmd_list_element *ignore,
 
 static void
 filename_completer_handle_brkchars (struct cmd_list_element *ignore,
+                                   completion_tracker &tracker,
                                    const char *text, const char *word)
 {
   set_rl_completer_word_break_characters
@@ -213,13 +221,12 @@ filename_completer_handle_brkchars (struct cmd_list_element *ignore,
    This is intended to be used in commands that set breakpoints
    etc.  */
 
-static VEC (char_ptr) *
-linespec_location_completer (struct cmd_list_element *ignore,
-                            const char *text, const char *word)
+static void
+complete_files_symbols (completion_tracker &tracker,
+                       const char *text, const char *word)
 {
-  int n_syms, n_files, ix;
-  VEC (char_ptr) *fn_list = NULL;
-  VEC (char_ptr) *list = NULL;
+  int ix;
+  completion_list fn_list;
   const char *p;
   int quote_found = 0;
   int quoted = *text == '\'' || *text == '"';
@@ -228,7 +235,6 @@ linespec_location_completer (struct cmd_list_element *ignore,
   char *file_to_match = NULL;
   const char *symbol_start = text;
   const char *orig_text = text;
-  size_t text_len;
 
   /* Do we have an unquoted colon, as in "break foo.c:bar"?  */
   for (p = text; *p != '\0'; ++p)
@@ -269,7 +275,6 @@ linespec_location_completer (struct cmd_list_element *ignore,
 
   if (quoted)
     text++;
-  text_len = strlen (text);
 
   /* Where is the file name?  */
   if (colon)
@@ -291,44 +296,23 @@ linespec_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);
+      collect_file_symbol_completion_matches (tracker, symbol_start, word,
+                                             file_to_match);
       xfree (file_to_match);
     }
   else
     {
-      list = make_symbol_completion_list (symbol_start, word);
+      size_t text_len = strlen (text);
+
+      collect_symbol_completion_matches (tracker, symbol_start, word);
       /* If text includes characters which cannot appear in a file
         name, they cannot be asking for completion on files.  */
-      if (strcspn (text, 
+      if (strcspn (text,
                   gdb_completer_file_name_break_characters) == text_len)
        fn_list = make_source_files_completion_list (text, text);
     }
 
-  n_syms = VEC_length (char_ptr, list);
-  n_files = VEC_length (char_ptr, fn_list);
-
-  /* Catenate fn_list[] onto the end of list[].  */
-  if (!n_syms)
-    {
-      VEC_free (char_ptr, list); /* Paranoia.  */
-      list = fn_list;
-      fn_list = NULL;
-    }
-  else
-    {
-      char *fn;
-
-      for (ix = 0; VEC_iterate (char_ptr, fn_list, ix, fn); ++ix)
-       VEC_safe_push (char_ptr, list, fn);
-      VEC_free (char_ptr, fn_list);
-    }
-
-  if (n_syms && n_files)
-    {
-      /* Nothing.  */
-    }
-  else if (n_files)
+  if (!fn_list.empty () && !tracker.have_completions ())
     {
       char *fn;
 
@@ -347,31 +331,41 @@ linespec_location_completer (struct cmd_list_element *ignore,
         completion, because rl_complete will prepend "/foo/" to each
         candidate completion.  The loop below removes that leading
         part.  */
-      for (ix = 0; VEC_iterate (char_ptr, list, ix, fn); ++ix)
+      for (const auto &fn_up: fn_list)
        {
-         memmove (fn, fn + (word - text),
-                  strlen (fn) + 1 - (word - text));
+         char *fn = fn_up.get ();
+         memmove (fn, fn + (word - text), strlen (fn) + 1 - (word - text));
        }
     }
-  else if (!n_syms)
+
+  tracker.add_completions (std::move (fn_list));
+
+  if (!tracker.have_completions ())
     {
       /* 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);
+      collect_symbol_completion_matches (tracker,
+                                        orig_text, word);
     }
+}
 
-  return list;
+/* Returns STRING if not NULL, the empty string otherwise.  */
+
+static const char *
+string_or_empty (const char *string)
+{
+  return string != NULL ? string : "";
 }
 
 /* A helper function to collect explicit location matches for the given
    LOCATION, which is attempting to match on WORD.  */
 
-static VEC (char_ptr) *
-collect_explicit_location_matches (struct event_location *location,
+static void
+collect_explicit_location_matches (completion_tracker &tracker,
+                                  struct event_location *location,
                                   enum explicit_location_match_type what,
                                   const char *word)
 {
-  VEC (char_ptr) *matches = NULL;
   const struct explicit_location *explicit_loc
     = get_explicit_location (location);
 
@@ -379,26 +373,25 @@ collect_explicit_location_matches (struct event_location *location,
     {
     case MATCH_SOURCE:
       {
-       const char *text = (explicit_loc->source_filename == NULL
-                           ? "" : explicit_loc->source_filename);
-
-       matches = make_source_files_completion_list (text, word);
+       const char *source = string_or_empty (explicit_loc->source_filename);
+       completion_list matches
+         = make_source_files_completion_list (source, word);
+       tracker.add_completions (std::move (matches));
       }
       break;
 
     case MATCH_FUNCTION:
       {
-       const char *text = (explicit_loc->function_name == NULL
-                           ? "" : explicit_loc->function_name);
-
+       const char *function = string_or_empty (explicit_loc->function_name);
        if (explicit_loc->source_filename != NULL)
          {
            const char *filename = explicit_loc->source_filename;
 
-           matches = make_file_symbol_completion_list (text, word, filename);
+           collect_file_symbol_completion_matches (tracker,
+                                                   function, word, filename);
          }
-       else
-         matches = make_symbol_completion_list (text, word);
+       else
+        collect_symbol_completion_matches (tracker, function, word);
       }
       break;
 
@@ -409,8 +402,6 @@ collect_explicit_location_matches (struct event_location *location,
     default:
       gdb_assert_not_reached ("unhandled explicit_location_match_type");
     }
-
-  return matches;
 }
 
 /* A convenience macro to (safely) back up P to the previous word.  */
@@ -429,13 +420,12 @@ backup_text_ptr (const char *p, const char *text)
 /* A completer function for explicit locations.  This function
    completes both options ("-source", "-line", etc) and values.  */
 
-static VEC (char_ptr) *
-explicit_location_completer (struct cmd_list_element *ignore,
-                            struct event_location *location,
-                            const char *text, const char *word)
+static void
+complete_explicit_location (completion_tracker &tracker,
+                           struct event_location *location,
+                           const char *text, const char *word)
 {
   const char *p;
-  VEC (char_ptr) *matches = NULL;
 
   /* Find the beginning of the word.  This is necessary because
      we need to know if we are completing an option name or value.  We
@@ -457,7 +447,8 @@ explicit_location_completer (struct cmd_list_element *ignore,
       /* Skip over the '-'.  */
       ++p;
 
-      return complete_on_enum (keywords, p, p);
+      complete_on_enum (tracker, keywords, p, p);
+      return;
     }
   else
     {
@@ -495,7 +486,7 @@ explicit_location_completer (struct cmd_list_element *ignore,
        {
          /* The user isn't completing on any valid option name,
             e.g., "break -source foo.c [tab]".  */
-         return NULL;
+         return;
        }
 
       /* If the user hasn't entered a search expression, e.g.,
@@ -505,43 +496,40 @@ explicit_location_completer (struct cmd_list_element *ignore,
        new_word = "";
 
       /* Now gather matches  */
-      matches = collect_explicit_location_matches (location, what, new_word);
+      collect_explicit_location_matches (tracker, location, what, new_word);
     }
-
-  return matches;
 }
 
 /* A completer for locations.  */
 
-VEC (char_ptr) *
+void
 location_completer (struct cmd_list_element *ignore,
+                   completion_tracker &tracker,
                    const char *text, const char *word)
 {
-  VEC (char_ptr) *matches = NULL;
   const char *copy = text;
 
   event_location_up location = string_to_explicit_location (&copy,
                                                            current_language,
                                                            1);
   if (location != NULL)
-    matches = explicit_location_completer (ignore, location.get (),
-                                          text, word);
+    complete_explicit_location (tracker, location.get (),
+                               text, word);
   else
     {
       /* This is an address or linespec location.
         Right now both of these are handled by the (old) linespec
         completer.  */
-      matches = linespec_location_completer (ignore, text, word);
+      complete_files_symbols (tracker, text, word);
     }
-
-  return matches;
 }
 
 /* Helper for expression_completer which recursively adds field and
-   method names from TYPE, a struct or union type, to the array
-   OUTPUT.  */
+   method names from TYPE, a struct or union type, to the OUTPUT
+   list.  */
+
 static void
-add_struct_fields (struct type *type, VEC (char_ptr) **output,
+add_struct_fields (struct type *type, completion_list &output,
                   char *fieldname, int namelen)
 {
   int i;
@@ -560,8 +548,7 @@ add_struct_fields (struct type *type, VEC (char_ptr) **output,
            {
              if (! strncmp (TYPE_FIELD_NAME (type, i), 
                             fieldname, namelen))
-               VEC_safe_push (char_ptr, *output,
-                              xstrdup (TYPE_FIELD_NAME (type, i)));
+               output.emplace_back (xstrdup (TYPE_FIELD_NAME (type, i)));
            }
          else if (TYPE_CODE (TYPE_FIELD_TYPE (type, i)) == TYPE_CODE_UNION)
            {
@@ -585,7 +572,7 @@ add_struct_fields (struct type *type, VEC (char_ptr) **output,
            }
          /* Omit constructors from the completion list.  */
          if (!type_name || strcmp (type_name, name))
-           VEC_safe_push (char_ptr, *output, xstrdup (name));
+           output.emplace_back (xstrdup (name));
        }
     }
 }
@@ -593,9 +580,10 @@ add_struct_fields (struct type *type, VEC (char_ptr) **output,
 /* Complete on expressions.  Often this means completing on symbol
    names, but some language parsers also have support for completing
    field names.  */
-VEC (char_ptr) *
-expression_completer (struct cmd_list_element *ignore, 
-                     const char *text, const char *word)
+
+static void
+complete_expression (completion_tracker &tracker,
+                    const char *text, const char *word)
 {
   struct type *type = NULL;
   char *fieldname;
@@ -610,7 +598,7 @@ expression_completer (struct cmd_list_element *ignore,
     }
   CATCH (except, RETURN_MASK_ERROR)
     {
-      return NULL;
+      return;
     }
   END_CATCH
 
@@ -628,11 +616,12 @@ expression_completer (struct cmd_list_element *ignore,
          || TYPE_CODE (type) == TYPE_CODE_STRUCT)
        {
          int flen = strlen (fieldname);
-         VEC (char_ptr) *result = NULL;
+         completion_list result;
 
-         add_struct_fields (type, &result, fieldname, flen);
+         add_struct_fields (type, result, fieldname, flen);
          xfree (fieldname);
-         return result;
+         tracker.add_completions (std::move (result));
+         return;
        }
     }
   else if (fieldname && code != TYPE_CODE_UNDEF)
@@ -640,14 +629,26 @@ 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);
+      collect_symbol_completion_matches_type (tracker, fieldname, fieldname,
+                                             code);
       do_cleanups (cleanup);
-      return result;
+      return;
     }
   xfree (fieldname);
 
-  /* Not ideal but it is what we used to do before...  */
-  return linespec_location_completer (ignore, text, word);
+  complete_files_symbols (tracker, text, word);
+}
+
+/* Complete on expressions.  Often this means completing on symbol
+   names, but some language parsers also have support for completing
+   field names.  */
+
+void
+expression_completer (struct cmd_list_element *ignore,
+                     completion_tracker &tracker,
+                     const char *text, const char *word)
+{
+  complete_expression (tracker, text, word);
 }
 
 /* See definition in completer.h.  */
@@ -677,11 +678,12 @@ set_gdb_completion_word_break_characters (completer_ftype *fn)
 
 /* Complete on symbols.  */
 
-VEC (char_ptr) *
+void
 symbol_completer (struct cmd_list_element *ignore,
+                 completion_tracker &tracker,
                  const char *text, const char *word)
 {
-  return make_symbol_completion_list (text, word);
+  collect_symbol_completion_matches (tracker, text, word);
 }
 
 /* Here are some useful test cases for completion.  FIXME: These
@@ -707,18 +709,30 @@ symbol_completer (struct cmd_list_element *ignore,
    "file ../gdb.stabs/we" "ird" (needs to not break word at slash)
  */
 
-typedef enum
+enum complete_line_internal_reason
 {
+  /* Preliminary phase, called by gdb_completion_word_break_characters
+     function, is used to determine the correct set of chars that are
+     word delimiters depending on the current command in line_buffer.
+     No completion list should be generated; the return value should
+     be NULL.  This is checked by an assertion.  */
   handle_brkchars,
+
+  /* Main phase, called by complete_line function, is used to get the
+     list of possible completions.  */
   handle_completions,
-  handle_help
-}
-complete_line_internal_reason;
+
+  /* Special case when completing a 'help' command.  In this case,
+     once sub-command completions are exhausted, we simply return
+     NULL.  */
+  handle_help,
+};
 
 /* Helper for complete_line_internal to simplify it.  */
 
-static VEC (char_ptr) *
-complete_line_internal_normal_command (const char *command, const char *word,
+static void
+complete_line_internal_normal_command (completion_tracker &tracker,
+                                      const char *command, const char *word,
                                       const char *cmd_args,
                                       complete_line_internal_reason reason,
                                       struct cmd_list_element *c)
@@ -753,12 +767,11 @@ complete_line_internal_normal_command (const char *command, const char *word,
               (c->completer));
        }
 
-      brkchars_fn (c, p, word);
+      brkchars_fn (c, tracker, p, word);
     }
 
   if (reason != handle_brkchars && c->completer != NULL)
-    return (*c->completer) (c, p, word);
-  return NULL;
+    (*c->completer) (c, tracker, p, word);
 }
 
 /* Internal function used to handle completions.
@@ -770,35 +783,19 @@ complete_line_internal_normal_command (const char *command, const char *word,
    text of the line.  POINT is the offset in that line of the cursor.
    You should pretend that the line ends at POINT.
 
-   REASON is of type complete_line_internal_reason.
-
-   If REASON is handle_brkchars:
-   Preliminary phase, called by gdb_completion_word_break_characters
-   function, is used to determine the correct set of chars that are
-   word delimiters depending on the current command in line_buffer.
-   No completion list should be generated; the return value should be
-   NULL.  This is checked by an assertion in that function.
-
-   If REASON is handle_completions:
-   Main phase, called by complete_line function, is used to get the list
-   of posible completions.
+   See complete_line_internal_reason for description of REASON.  */
 
-   If REASON is handle_help:
-   Special case when completing a 'help' command.  In this case,
-   once sub-command completions are exhausted, we simply return NULL.
- */
-
-static VEC (char_ptr) *
-complete_line_internal (const char *text, 
-                       const char *line_buffer, int point,
-                       complete_line_internal_reason reason)
+static void
+complete_line_internal_1 (completion_tracker &tracker,
+                         const char *text,
+                         const char *line_buffer, int point,
+                         complete_line_internal_reason reason)
 {
-  VEC (char_ptr) *list = NULL;
   char *tmp_command;
   const char *p;
   int ignore_help_classes;
   /* Pointer within tmp_command which corresponds to text.  */
-  char *word;
+  const char *word;
   struct cmd_list_element *c, *result_list;
 
   /* Choose the default set of word break characters to break
@@ -821,10 +818,18 @@ complete_line_internal (const char *text,
 
   strncpy (tmp_command, line_buffer, point);
   tmp_command[point] = '\0';
-  /* Since text always contains some number of characters leading up
-     to point, we can find the equivalent position in tmp_command
-     by subtracting that many characters from the end of tmp_command.  */
-  word = tmp_command + point - strlen (text);
+  if (reason == handle_brkchars)
+    {
+      gdb_assert (text == NULL);
+      word = NULL;
+    }
+  else
+    {
+      /* Since text always contains some number of characters leading up
+        to point, we can find the equivalent position in tmp_command
+        by subtracting that many characters from the end of tmp_command.  */
+      word = tmp_command + point - strlen (text);
+    }
 
   if (point == 0)
     {
@@ -848,7 +853,6 @@ complete_line_internal (const char *text,
     {
       /* It is an unrecognized command.  So there are no
         possible completions.  */
-      list = NULL;
     }
   else if (c == CMD_LIST_AMBIGUOUS)
     {
@@ -866,7 +870,6 @@ complete_line_internal (const char *text,
             example, "info t " or "info t foo" does not complete
             to anything, because "info t" can be "info target" or
             "info terminal".  */
-         list = NULL;
        }
       else
        {
@@ -875,14 +878,14 @@ complete_line_internal (const char *text,
          if (result_list)
            {
              if (reason != handle_brkchars)
-               list = complete_on_cmdlist (*result_list->prefixlist, p,
-                                           word, ignore_help_classes);
+               complete_on_cmdlist (*result_list->prefixlist, tracker, p,
+                                    word, ignore_help_classes);
            }
          else
            {
              if (reason != handle_brkchars)
-               list = complete_on_cmdlist (cmdlist, p, word,
-                                           ignore_help_classes);
+               complete_on_cmdlist (cmdlist, tracker, p, word,
+                                    ignore_help_classes);
            }
          /* Ensure that readline does the right thing with respect to
             inserting quotes.  */
@@ -908,8 +911,8 @@ complete_line_internal (const char *text,
                  /* It is a prefix command; what comes after it is
                     a subcommand (e.g. "info ").  */
                  if (reason != handle_brkchars)
-                   list = complete_on_cmdlist (*c->prefixlist, p, word,
-                                               ignore_help_classes);
+                   complete_on_cmdlist (*c->prefixlist, tracker, p, word,
+                                        ignore_help_classes);
 
                  /* Ensure that readline does the right thing
                     with respect to inserting quotes.  */
@@ -917,11 +920,11 @@ complete_line_internal (const char *text,
                    (gdb_completer_command_word_break_characters);
                }
              else if (reason == handle_help)
-               list = NULL;
+               ;
              else if (c->enums)
                {
                  if (reason != handle_brkchars)
-                   list = complete_on_enum (c->enums, p, word);
+                   complete_on_enum (tracker, c->enums, p, word);
                  set_rl_completer_word_break_characters
                    (gdb_completer_command_word_break_characters);
                }
@@ -929,9 +932,9 @@ complete_line_internal (const char *text,
                {
                  /* It is a normal command; what comes after it is
                     completed by the command's completer function.  */
-                 list = complete_line_internal_normal_command (tmp_command,
-                                                               word, p,
-                                                               reason, c);
+                 complete_line_internal_normal_command (tracker,
+                                                        tmp_command, word, p,
+                                                        reason, c);
                }
            }
          else
@@ -953,8 +956,8 @@ complete_line_internal (const char *text,
                }
 
              if (reason != handle_brkchars)
-               list = complete_on_cmdlist (result_list, q, word,
-                                           ignore_help_classes);
+               complete_on_cmdlist (result_list, tracker, q, word,
+                                    ignore_help_classes);
 
              /* Ensure that readline does the right thing
                 with respect to inserting quotes.  */
@@ -963,7 +966,7 @@ complete_line_internal (const char *text,
            }
        }
       else if (reason == handle_help)
-       list = NULL;
+       ;
       else
        {
          /* There is non-whitespace beyond the command.  */
@@ -972,105 +975,115 @@ complete_line_internal (const char *text,
            {
              /* It is an unrecognized subcommand of a prefix command,
                 e.g. "info adsfkdj".  */
-             list = NULL;
            }
          else if (c->enums)
            {
              if (reason != handle_brkchars)
-               list = complete_on_enum (c->enums, p, word);
+               complete_on_enum (tracker, c->enums, p, word);
            }
          else
            {
              /* It is a normal command.  */
-             list = complete_line_internal_normal_command (tmp_command,
-                                                           word, p,
-                                                           reason, c);
+             complete_line_internal_normal_command (tracker,
+                                                    tmp_command, word, p,
+                                                    reason, c);
            }
        }
     }
-
-  return list;
 }
 
-/* See completer.h.  */
+/* Wrapper around complete_line_internal_1 to handle
+   MAX_COMPLETIONS_REACHED_ERROR.  */
 
-int max_completions = 200;
+static void
+complete_line_internal (completion_tracker &tracker,
+                       const char *text,
+                       const char *line_buffer, int point,
+                       complete_line_internal_reason reason)
+{
+  TRY
+    {
+      complete_line_internal_1 (tracker, text, line_buffer, point, reason);
+    }
+  CATCH (except, RETURN_MASK_ERROR)
+    {
+      if (except.error != MAX_COMPLETIONS_REACHED_ERROR)
+       throw_exception (except);
+    }
+}
 
 /* See completer.h.  */
 
-completion_tracker_t
-new_completion_tracker (void)
-{
-  if (max_completions <= 0)
-    return NULL;
+int max_completions = 200;
 
-  return htab_create_alloc (max_completions,
-                           htab_hash_string, (htab_eq) streq,
-                           NULL, xcalloc, xfree);
-}
+/* Initial size of the table.  It automagically grows from here.  */
+#define INITIAL_COMPLETION_HTAB_SIZE 200
 
-/* Cleanup routine to free a completion tracker and reset the pointer
-   to NULL.  */
+/* See completer.h.  */
 
-static void
-free_completion_tracker (void *p)
+completion_tracker::completion_tracker ()
 {
-  completion_tracker_t *tracker_ptr = (completion_tracker_t *) p;
-
-  htab_delete (*tracker_ptr);
-  *tracker_ptr = NULL;
+  m_entries_hash = htab_create_alloc (INITIAL_COMPLETION_HTAB_SIZE,
+                                     htab_hash_string, (htab_eq) streq,
+                                     NULL, xcalloc, xfree);
 }
 
 /* See completer.h.  */
 
-struct cleanup *
-make_cleanup_free_completion_tracker (completion_tracker_t *tracker_ptr)
+completion_tracker::~completion_tracker ()
 {
-  if (*tracker_ptr == NULL)
-    return make_cleanup (null_cleanup, NULL);
-
-  return make_cleanup (free_completion_tracker, tracker_ptr);
+  xfree (m_lowest_common_denominator);
+  htab_delete (m_entries_hash);
 }
 
 /* See completer.h.  */
 
-enum maybe_add_completion_enum
-maybe_add_completion (completion_tracker_t tracker, char *name)
+bool
+completion_tracker::maybe_add_completion (gdb::unique_xmalloc_ptr<char> name)
 {
   void **slot;
 
-  if (max_completions < 0)
-    return MAYBE_ADD_COMPLETION_OK;
   if (max_completions == 0)
-    return MAYBE_ADD_COMPLETION_MAX_REACHED;
+    return false;
 
-  gdb_assert (tracker != NULL);
+  if (htab_elements (m_entries_hash) >= max_completions)
+    return false;
 
-  if (htab_elements (tracker) >= max_completions)
-    return MAYBE_ADD_COMPLETION_MAX_REACHED;
+  slot = htab_find_slot (m_entries_hash, name.get (), INSERT);
+  if (*slot == HTAB_EMPTY_ENTRY)
+    {
+      const char *match_for_lcd_str = name.get ();
 
-  slot = htab_find_slot (tracker, name, INSERT);
+      recompute_lowest_common_denominator (match_for_lcd_str);
 
-  if (*slot != HTAB_EMPTY_ENTRY)
-    return MAYBE_ADD_COMPLETION_DUPLICATE;
+      *slot = name.get ();
+      m_entries_vec.push_back (std::move (name));
+    }
 
-  *slot = name;
+  return true;
+}
 
-  return (htab_elements (tracker) < max_completions
-         ? MAYBE_ADD_COMPLETION_OK
-         : MAYBE_ADD_COMPLETION_OK_MAX_REACHED);
+/* See completer.h.  */
+
+void
+completion_tracker::add_completion (gdb::unique_xmalloc_ptr<char> name)
+{
+  if (!maybe_add_completion (std::move (name)))
+    throw_error (MAX_COMPLETIONS_REACHED_ERROR, _("Max completions reached."));
 }
 
+/* See completer.h.  */
+
 void
-throw_max_completions_reached_error (void)
+completion_tracker::add_completions (completion_list &&list)
 {
-  throw_error (MAX_COMPLETIONS_REACHED_ERROR, _("Max completions reached."));
+  for (auto &candidate : list)
+    add_completion (std::move (candidate));
 }
 
-/* Generate completions all at once.  Returns a vector of unique strings
-   allocated with xmalloc.  Returns NULL if there are no completions
-   or if max_completions is 0.  If max_completions is non-negative, this will
-   return at most max_completions strings.
+/* Generate completions all at once.  Does nothing if max_completions
+   is 0.  If max_completions is non-negative, this will collect at
+   most max_completions strings.
 
    TEXT is the caller's idea of the "word" we are looking at.
 
@@ -1080,77 +1093,32 @@ throw_max_completions_reached_error (void)
    POINT is the offset in that line of the cursor.  You
    should pretend that the line ends at POINT.  */
 
-VEC (char_ptr) *
-complete_line (const char *text, const char *line_buffer, int point)
+void
+complete_line (completion_tracker &tracker,
+              const char *text, const char *line_buffer, int point)
 {
-  VEC (char_ptr) *list;
-  VEC (char_ptr) *result = NULL;
-  struct 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.  */
-
-  for (ix = 0, max_reached = 0;
-       !max_reached && VEC_iterate (char_ptr, list, ix, candidate);
-       ++ix)
-    {
-      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;
-       }
-    }
-
-  do_cleanups (cleanups);
-
-  return result;
+    return;
+  complete_line_internal (tracker, text, line_buffer, point,
+                         handle_completions);
 }
 
 /* Complete on command names.  Used by "help".  */
 
-VEC (char_ptr) *
+void
 command_completer (struct cmd_list_element *ignore, 
+                  completion_tracker &tracker,
                   const char *text, const char *word)
 {
-  return complete_line_internal (word, text, 
-                                strlen (text), handle_help);
+  complete_line_internal (tracker, word, text,
+                         strlen (text), handle_help);
 }
 
 /* The corresponding completer_handle_brkchars implementation.  */
 
 static void
 command_completer_handle_brkchars (struct cmd_list_element *ignore,
+                                  completion_tracker &tracker,
                                   const char *text, const char *word)
 {
   set_rl_completer_word_break_characters
@@ -1159,11 +1127,11 @@ command_completer_handle_brkchars (struct cmd_list_element *ignore,
 
 /* Complete on signals.  */
 
-VEC (char_ptr) *
+void
 signal_completer (struct cmd_list_element *ignore,
+                 completion_tracker &tracker,
                  const char *text, const char *word)
 {
-  VEC (char_ptr) *return_val = NULL;
   size_t len = strlen (word);
   int signum;
   const char *signame;
@@ -1181,10 +1149,11 @@ signal_completer (struct cmd_list_element *ignore,
        continue;
 
       if (strncasecmp (signame, word, len) == 0)
-       VEC_safe_push (char_ptr, return_val, xstrdup (signame));
+       {
+         gdb::unique_xmalloc_ptr<char> copy (xstrdup (signame));
+         tracker.add_completion (std::move (copy));
+       }
     }
-
-  return return_val;
 }
 
 /* Bit-flags for selecting what the register and/or register-group
@@ -1200,12 +1169,11 @@ DEF_ENUM_FLAGS_TYPE (enum reg_completer_target, reg_completer_targets);
 /* Complete register names and/or reggroup names based on the value passed
    in TARGETS.  At least one bit in TARGETS must be set.  */
 
-static VEC (char_ptr) *
-reg_or_group_completer_1 (struct cmd_list_element *ignore,
+static void
+reg_or_group_completer_1 (completion_tracker &tracker,
                          const char *text, const char *word,
                          reg_completer_targets targets)
 {
-  VEC (char_ptr) *result = NULL;
   size_t len = strlen (word);
   struct gdbarch *gdbarch;
   const char *name;
@@ -1223,7 +1191,10 @@ reg_or_group_completer_1 (struct cmd_list_element *ignore,
           i++)
        {
          if (*name != '\0' && strncmp (word, name, len) == 0)
-           VEC_safe_push (char_ptr, result, xstrdup (name));
+           {
+             gdb::unique_xmalloc_ptr<char> copy (xstrdup (name));
+             tracker.add_completion (std::move (copy));
+           }
        }
     }
 
@@ -1237,38 +1208,42 @@ reg_or_group_completer_1 (struct cmd_list_element *ignore,
        {
          name = reggroup_name (group);
          if (strncmp (word, name, len) == 0)
-           VEC_safe_push (char_ptr, result, xstrdup (name));
+           {
+             gdb::unique_xmalloc_ptr<char> copy (xstrdup (name));
+             tracker.add_completion (std::move (copy));
+           }
        }
     }
-
-  return result;
 }
 
 /* Perform completion on register and reggroup names.  */
 
-VEC (char_ptr) *
+void
 reg_or_group_completer (struct cmd_list_element *ignore,
+                       completion_tracker &tracker,
                        const char *text, const char *word)
 {
-  return reg_or_group_completer_1 (ignore, text, word,
-                                  (complete_register_names
-                                   | complete_reggroup_names));
+  reg_or_group_completer_1 (tracker, text, word,
+                           (complete_register_names
+                            | complete_reggroup_names));
 }
 
 /* Perform completion on reggroup names.  */
 
-VEC (char_ptr) *
+void
 reggroup_completer (struct cmd_list_element *ignore,
+                   completion_tracker &tracker,
                    const char *text, const char *word)
 {
-  return reg_or_group_completer_1 (ignore, text, word,
-                                  complete_reggroup_names);
+  reg_or_group_completer_1 (tracker, text, word,
+                           complete_reggroup_names);
 }
 
 /* The default completer_handle_brkchars implementation.  */
 
 static void
 default_completer_handle_brkchars (struct cmd_list_element *ignore,
+                                  completion_tracker &tracker,
                                   const char *text, const char *word)
 {
   set_rl_completer_word_break_characters
@@ -1292,89 +1267,339 @@ completer_handle_brkchars_func_for_completer (completer_ftype *fn)
 /* Get the list of chars that are considered as word breaks
    for the current command.  */
 
-char *
-gdb_completion_word_break_characters (void)
+static char *
+gdb_completion_word_break_characters_throw ()
 {
-  VEC (char_ptr) *list;
+  /* New completion starting.  Get rid of the previous tracker and
+     start afresh.  */
+  delete current_completion.tracker;
+  current_completion.tracker = new completion_tracker ();
+
+  completion_tracker &tracker = *current_completion.tracker;
+
+  complete_line_internal (tracker, NULL, rl_line_buffer,
+                         rl_point, handle_brkchars);
 
-  list = complete_line_internal (rl_line_buffer, rl_line_buffer, rl_point,
-                                handle_brkchars);
-  gdb_assert (list == NULL);
   return rl_completer_word_break_characters;
 }
 
-/* Generate completions one by one for the completer.  Each time we
-   are called return another potential completion to the caller.
-   line_completion just completes on commands or passes the buck to
-   the command's completer function, the stuff specific to symbol
-   completion is in make_symbol_completion_list.
-
-   TEXT is the caller's idea of the "word" we are looking at.
+char *
+gdb_completion_word_break_characters ()
+{
+  /* New completion starting.  */
+  current_completion.aborted = false;
 
-   MATCHES is the number of matches that have currently been collected
-   from calling this completion function.  When zero, then we need to
-   initialize, otherwise the initialization has already taken place
-   and we can just return the next potential completion string.
+  TRY
+    {
+      return gdb_completion_word_break_characters_throw ();
+    }
+  CATCH (ex, RETURN_MASK_ALL)
+    {
+      /* Set this to that gdb_rl_attempted_completion_function knows
+        to abort early.  */
+      current_completion.aborted = true;
+    }
+  END_CATCH
 
-   LINE_BUFFER is available to be looked at; it contains the entire
-   text of the line.  POINT is the offset in that line of the cursor.
-   You should pretend that the line ends at POINT.
+  return NULL;
+}
 
-   Returns NULL if there are no more completions, else a pointer to a
-   string which is a possible completion, it is the caller's
-   responsibility to free the string.  */
+/* See completer.h.  */
 
-static char *
-line_completion_function (const char *text, int matches, 
-                         char *line_buffer, int point)
+void
+completion_tracker::recompute_lowest_common_denominator (const char *new_match)
 {
-  static VEC (char_ptr) *list = NULL;  /* Cache of completions.  */
-  static int index;                    /* Next cached completion.  */
-  char *output = NULL;
-
-  if (matches == 0)
+  if (m_lowest_common_denominator == NULL)
+    {
+      /* We don't have a lowest common denominator yet, so simply take
+        the whole NEW_MATCH as being it.  */
+      m_lowest_common_denominator = xstrdup (new_match);
+      m_lowest_common_denominator_unique = true;
+    }
+  else
     {
-      /* The caller is beginning to accumulate a new set of
-         completions, so we need to find all of them now, and cache
-         them for returning one at a time on future calls.  */
+      /* Find the common denominator between the currently-known
+        lowest common denominator and NEW_MATCH.  That becomes the
+        new lowest common denominator.  */
+      size_t i;
 
-      if (list)
+      for (i = 0;
+          (new_match[i] != '\0'
+           && new_match[i] == m_lowest_common_denominator[i]);
+          i++)
+       ;
+      if (m_lowest_common_denominator[i] != new_match[i])
        {
-         /* Free the storage used by LIST, but not by the strings
-            inside.  This is because rl_complete_internal () frees
-            the strings.  As complete_line may abort by calling
-            `error' clear LIST now.  */
-         VEC_free (char_ptr, list);
+         m_lowest_common_denominator[i] = '\0';
+         m_lowest_common_denominator_unique = false;
        }
-      index = 0;
-      list = complete_line (text, line_buffer, point);
     }
+}
+
+/* Build a new C string that is a copy of LCD with the whitespace of
+   ORIG/ORIG_LEN preserved.
+
+   Say the user is completing a symbol name, with spaces, like:
+
+     "foo ( i"
+
+   and the resulting completion match is:
+
+     "foo(int)"
+
+   we want to end up with an input line like:
+
+     "foo ( int)"
+      ^^^^^^^      => text from LCD [1], whitespace from ORIG preserved.
+            ^^    => new text from LCD
+
+   [1] - We must take characters from the LCD instead of the original
+   text, since some completions want to change upper/lowercase.  E.g.:
 
-  /* If we found a list of potential completions during initialization
-     then dole them out one at a time.  After returning the last one,
-     return NULL (and continue to do so) each time we are called after
-     that, until a new list is available.  */
+     "handle sig<>"
 
-  if (list)
+   completes to:
+
+     "handle SIG[QUIT|etc.]"
+*/
+
+static char *
+expand_preserving_ws (const char *orig, size_t orig_len,
+                     const char *lcd)
+{
+  const char *p_orig = orig;
+  const char *orig_end = orig + orig_len;
+  const char *p_lcd = lcd;
+  std::string res;
+
+  while (p_orig < orig_end)
     {
-      if (index < VEC_length (char_ptr, list))
+      if (*p_orig == ' ')
+       {
+         while (p_orig < orig_end && *p_orig == ' ')
+           res += *p_orig++;
+         p_lcd = skip_spaces_const (p_lcd);
+       }
+      else
        {
-         output = VEC_index (char_ptr, list, index);
-         index++;
+         /* Take characters from the LCD instead of the original
+            text, since some completions change upper/lowercase.
+            E.g.:
+              "handle sig<>"
+            completes to:
+              "handle SIG[QUIT|etc.]"
+         */
+         res += *p_lcd;
+         p_orig++;
+         p_lcd++;
        }
     }
 
-#if 0
-  /* Can't do this because readline hasn't yet checked the word breaks
-     for figuring out whether to insert a quote.  */
-  if (output == NULL)
-    /* Make sure the word break characters are set back to normal for
-       the next time that readline tries to complete something.  */
-    rl_completer_word_break_characters =
-      current_language->la_word_break_characters();
-#endif
+  while (*p_lcd != '\0')
+    res += *p_lcd++;
+
+  return xstrdup (res.c_str ());
+}
+
+/* See completer.h.  */
+
+completion_result
+completion_tracker::build_completion_result (const char *text,
+                                            int start, int end)
+{
+  completion_list &list = m_entries_vec;       /* The completions.  */
 
-  return (output);
+  if (list.empty ())
+    return {};
+
+  /* +1 for the LCD, and +1 for NULL termination.  */
+  char **match_list = XNEWVEC (char *, 1 + list.size () + 1);
+
+  /* Build replacement word, based on the LCD.  */
+
+  match_list[0]
+    = expand_preserving_ws (text, end - start,
+                           m_lowest_common_denominator);
+
+  if (m_lowest_common_denominator_unique)
+    {
+      match_list[1] = NULL;
+
+      /* If we already have a space at the end of the match, tell
+        readline to skip appending another.  */
+      bool completion_suppress_append
+       = (match_list[0][strlen (match_list[0]) - 1] == ' ');
+
+      return completion_result (match_list, 1, completion_suppress_append);
+    }
+  else
+    {
+      int ix;
+
+      for (ix = 0; ix < list.size (); ++ix)
+       match_list[ix + 1] = list[ix].release ();
+      match_list[ix + 1] = NULL;
+
+      return completion_result (match_list, list.size (), false);
+    }
+}
+
+/* See completer.h  */
+
+completion_result::completion_result ()
+  : match_list (NULL), number_matches (0),
+    completion_suppress_append (false)
+{}
+
+/* See completer.h  */
+
+completion_result::completion_result (char **match_list_,
+                                     size_t number_matches_,
+                                     bool completion_suppress_append_)
+  : match_list (match_list_),
+    number_matches (number_matches_),
+    completion_suppress_append (completion_suppress_append_)
+{}
+
+/* See completer.h  */
+
+completion_result::~completion_result ()
+{
+  reset_match_list ();
+}
+
+/* See completer.h  */
+
+completion_result::completion_result (completion_result &&rhs)
+{
+  if (this == &rhs)
+    return;
+
+  reset_match_list ();
+  match_list = rhs.match_list;
+  rhs.match_list = NULL;
+  number_matches = rhs.number_matches;
+  rhs.number_matches = 0;
+}
+
+/* See completer.h  */
+
+char **
+completion_result::release_match_list ()
+{
+  char **ret = match_list;
+  match_list = NULL;
+  return ret;
+}
+
+/* Compare C strings for std::sort.  */
+
+static bool
+compare_cstrings (const char *str1, const char *str2)
+{
+  return strcmp (str1, str2) < 0;
+}
+
+/* See completer.h  */
+
+void
+completion_result::sort_match_list ()
+{
+  if (number_matches > 1)
+    {
+      /* Element 0 is special (it's the common prefix), leave it
+        be.  */
+      std::sort (&match_list[1],
+                &match_list[number_matches + 1],
+                compare_cstrings);
+    }
+}
+
+/* See completer.h  */
+
+void
+completion_result::reset_match_list ()
+{
+  if (match_list != NULL)
+    {
+      for (char **p = match_list; *p != NULL; p++)
+       xfree (*p);
+      xfree (match_list);
+      match_list = NULL;
+    }
+}
+
+/* Helper for gdb_rl_attempted_completion_function, which does most of
+   the work.  This is called by readline to build the match list array
+   and to determine the lowest common denominator.  The real matches
+   list starts at match[1], while match[0] is the slot holding
+   readline's idea of the lowest common denominator of all matches,
+   which is what readline replaces the completion "word" with.
+
+   TEXT is the caller's idea of the "word" we are looking at, as
+   computed in the handle_brkchars phase.
+
+   START is the offset from RL_LINE_BUFFER where TEXT starts.  END is
+   the offset from RL_LINE_BUFFER where TEXT ends (i.e., where
+   rl_point is).
+
+   You should thus pretend that the line ends at END (relative to
+   RL_LINE_BUFFER).
+
+   RL_LINE_BUFFER contains the entire text of the line.  RL_POINT is
+   the offset in that line of the cursor.  You should pretend that the
+   line ends at POINT.
+
+   Returns NULL if there are no completions.  */
+
+static char **
+gdb_rl_attempted_completion_function_throw (const char *text, int start, int end)
+{
+  /* Completers must be called twice.  If rl_point (i.e., END) is at
+     column 0, then readline skips the the handle_brkchars phase, and
+     so we create a tracker now in that case too.  */
+  delete current_completion.tracker;
+  current_completion.tracker = new completion_tracker ();
+
+  complete_line (*current_completion.tracker, text,
+                rl_line_buffer, rl_point);
+
+  completion_tracker &tracker = *current_completion.tracker;
+
+  completion_result result
+    = tracker.build_completion_result (text, start, end);
+
+  rl_completion_suppress_append = result.completion_suppress_append;
+  return result.release_match_list ();
+}
+
+/* Function installed as "rl_attempted_completion_function" readline
+   hook.  Wrapper around gdb_rl_attempted_completion_function_throw
+   that catches C++ exceptions, which can't cross readline.  */
+
+char **
+gdb_rl_attempted_completion_function (const char *text, int start, int end)
+{
+  /* If we end up returning NULL, either on error, or simple because
+     there are no matches, inhibit readline's default filename
+     completer.  */
+  rl_attempted_completion_over = 1;
+
+  /* If the handle_brkchars phase was aborted, don't try
+     completing.  */
+  if (current_completion.aborted)
+    return NULL;
+
+  TRY
+    {
+      return gdb_rl_attempted_completion_function_throw (text, start, end);
+    }
+  CATCH (ex, RETURN_MASK_ALL)
+    {
+    }
+  END_CATCH
+
+  return NULL;
 }
 
 /* Skip over the possibly quoted word STR (as defined by the quote
This page took 0.04121 seconds and 4 git commands to generate.