opcodes/i386: remove trailing whitespace from insns with zero operands
[binutils-gdb.git] / gdb / completer.c
index 3e87ed454c624f4e961c67f2782c091be61f1bb0..fea3eb9329d8464c8d74fb219d53182f568b36f7 100644 (file)
@@ -1,5 +1,5 @@
 /* Line completion stuff for GDB, the GNU debugger.
-   Copyright (C) 2000-2018 Free Software Foundation, Inc.
+   Copyright (C) 2000-2022 Free Software Foundation, Inc.
 
    This file is part of GDB.
 
@@ -22,7 +22,7 @@
 #include "expression.h"
 #include "filenames.h"         /* For DOSish file names.  */
 #include "language.h"
-#include "gdb_signals.h"
+#include "gdbsupport/gdb_signals.h"
 #include "target.h"
 #include "reggroups.h"
 #include "user-regs.h"
@@ -36,7 +36,7 @@
    calling a hook instead so we eliminate the CLI dependency.  */
 #include "gdbcmd.h"
 
-/* Needed for rl_completer_word_break_characters() and for
+/* Needed for rl_completer_word_break_characters and for
    rl_filename_completion_function.  */
 #include "readline/readline.h"
 
 
 #include "completer.h"
 
+/* See completer.h.  */
+
+class completion_tracker::completion_hash_entry
+{
+public:
+  /* Constructor.  */
+  completion_hash_entry (gdb::unique_xmalloc_ptr<char> name,
+                        gdb::unique_xmalloc_ptr<char> lcd)
+    : m_name (std::move (name)),
+      m_lcd (std::move (lcd))
+  {
+    /* Nothing.  */
+  }
+
+  /* Returns a pointer to the lowest common denominator string.  This
+     string will only be valid while this hash entry is still valid as the
+     string continues to be owned by this hash entry and will be released
+     when this entry is deleted.  */
+  char *get_lcd () const
+  {
+    return m_lcd.get ();
+  }
+
+  /* Get, and release the name field from this hash entry.  This can only
+     be called once, after which the name field is no longer valid.  This
+     should be used to pass ownership of the name to someone else.  */
+  char *release_name ()
+  {
+    return m_name.release ();
+  }
+
+  /* Return true of the name in this hash entry is STR.  */
+  bool is_name_eq (const char *str) const
+  {
+    return strcmp (m_name.get (), str) == 0;
+  }
+
+  /* Return the hash value based on the name of the entry.  */
+  hashval_t hash_name () const
+  {
+    return htab_hash_string (m_name.get ());
+  }
+
+private:
+
+  /* The symbol name stored in this hash entry.  */
+  gdb::unique_xmalloc_ptr<char> m_name;
+
+  /* The lowest common denominator string computed for this hash entry.  */
+  gdb::unique_xmalloc_ptr<char> m_lcd;
+};
+
 /* Misc state that needs to be tracked across several different
    readline completer entry point calls, all related to a single
    completion invocation.  */
@@ -95,20 +147,20 @@ enum explicit_location_match_type
    but it does affect how much stuff M-? lists.
    (2) If one of the matches contains a word break character, readline
    will quote it.  That's why we switch between
-   current_language->la_word_break_characters() and
+   current_language->word_break_characters () and
    gdb_completer_command_word_break_characters.  I'm not sure when
    we need this behavior (perhaps for funky characters in C++ 
    symbols?).  */
 
 /* Variables which are necessary for fancy command line editing.  */
 
-/* When completing on command names, we remove '-' from the list of
+/* When completing on command names, we remove '-' and '.' from the list of
    word break characters, since we use it in command names.  If the
    readline library sees one in any of the current completion strings,
    it thinks that the string needs to be quoted and automatically
    supplies a leading quote.  */
 static const char gdb_completer_command_word_break_characters[] =
-" \t\n!@#$%^&*()+=|~`}{[]\"';:?/>.<,";
+" \t\n!@#$%^&*()+=|~`}{[]\"';:?/><,";
 
 /* When completing on file names, we remove from the list of word
    break characters any characters that are commonly used in file
@@ -168,7 +220,7 @@ filename_completer (struct cmd_list_element *ignore,
         will loop indefinitely.  */
       subsequent_name = 1;
       /* Like emacs, don't complete on old versions.  Especially
-         useful in the "source" command.  */
+        useful in the "source" command.  */
       const char *p = p_rl.get ();
       if (p[strlen (p) - 1] == '~')
        continue;
@@ -198,14 +250,6 @@ filename_completer_handle_brkchars (struct cmd_list_element *ignore,
     (gdb_completer_file_name_break_characters);
 }
 
-/* Possible values for the found_quote flags word used by the completion
-   functions.  It says what kind of (shell-like) quoting we found anywhere
-   in the line. */
-#define RL_QF_SINGLE_QUOTE      0x01
-#define RL_QF_DOUBLE_QUOTE      0x02
-#define RL_QF_BACKSLASH         0x04
-#define RL_QF_OTHER_QUOTE       0x08
-
 /* Find the bounds of the current word for completion purposes, and
    return a pointer to the end of the word.  This mimics (and is a
    modified version of) readline's _rl_find_completion_word internal
@@ -232,7 +276,7 @@ gdb_rl_find_completion_word (struct gdb_rl_completion_word_info *info,
                             int *qc, int *dp,
                             const char *line_buffer)
 {
-  int scan, end, found_quote, delimiter, pass_next, isbrk;
+  int scan, end, delimiter, pass_next, isbrk;
   char quote_char;
   const char *brkchars;
   int point = strlen (line_buffer);
@@ -249,7 +293,7 @@ gdb_rl_find_completion_word (struct gdb_rl_completion_word_info *info,
     }
 
   end = point;
-  found_quote = delimiter = 0;
+  delimiter = 0;
   quote_char = '\0';
 
   brkchars = info->word_break_characters;
@@ -259,8 +303,6 @@ gdb_rl_find_completion_word (struct gdb_rl_completion_word_info *info,
       /* We have a list of characters which can be used in pairs to
         quote substrings for the completer.  Try to find the start of
         an unclosed quoted substring.  */
-      /* FOUND_QUOTE is set so we know what kind of quotes we
-        found.  */
       for (scan = pass_next = 0;
           scan < end;
           scan++)
@@ -278,7 +320,6 @@ gdb_rl_find_completion_word (struct gdb_rl_completion_word_info *info,
          if (quote_char != '\'' && line_buffer[scan] == '\\')
            {
              pass_next = 1;
-             found_quote |= RL_QF_BACKSLASH;
              continue;
            }
 
@@ -299,13 +340,6 @@ gdb_rl_find_completion_word (struct gdb_rl_completion_word_info *info,
              /* Found start of a quoted substring.  */
              quote_char = line_buffer[scan];
              point = scan + 1;
-             /* Shell-like quoting conventions.  */
-             if (quote_char == '\'')
-               found_quote |= RL_QF_SINGLE_QUOTE;
-             else if (quote_char == '"')
-               found_quote |= RL_QF_DOUBLE_QUOTE;
-             else
-               found_quote |= RL_QF_OTHER_QUOTE;
            }
        }
     }
@@ -352,32 +386,62 @@ gdb_rl_find_completion_word (struct gdb_rl_completion_word_info *info,
   return line_buffer + point;
 }
 
-/* See completer.h.  */
+/* Find the completion word point for TEXT, emulating the algorithm
+   readline uses to find the word point, using WORD_BREAK_CHARACTERS
+   as word break characters.  */
 
-const char *
-advance_to_expression_complete_word_point (completion_tracker &tracker,
-                                          const char *text)
+static const char *
+advance_to_completion_word (completion_tracker &tracker,
+                           const char *word_break_characters,
+                           const char *text)
 {
   gdb_rl_completion_word_info info;
 
-  info.word_break_characters
-    = current_language->la_word_break_characters ();
+  info.word_break_characters = word_break_characters;
   info.quote_characters = gdb_completer_quote_characters;
   info.basic_quote_characters = rl_basic_quote_characters;
 
+  int delimiter;
   const char *start
-    = gdb_rl_find_completion_word (&info, NULL, NULL, text);
+    = gdb_rl_find_completion_word (&info, NULL, &delimiter, text);
 
   tracker.advance_custom_word_point_by (start - text);
 
+  if (delimiter)
+    {
+      tracker.set_quote_char (delimiter);
+      tracker.set_suppress_append_ws (true);
+    }
+
   return start;
 }
 
 /* See completer.h.  */
 
+const char *
+advance_to_expression_complete_word_point (completion_tracker &tracker,
+                                          const char *text)
+{
+  const char *brk_chars = current_language->word_break_characters ();
+  return advance_to_completion_word (tracker, brk_chars, text);
+}
+
+/* See completer.h.  */
+
+const char *
+advance_to_filename_complete_word_point (completion_tracker &tracker,
+                                        const char *text)
+{
+  const char *brk_chars = gdb_completer_file_name_break_characters;
+  return advance_to_completion_word (tracker, brk_chars, text);
+}
+
+/* See completer.h.  */
+
 bool
 completion_tracker::completes_to_completion_word (const char *word)
 {
+  recompute_lowest_common_denominator ();
   if (m_lowest_common_denominator_unique)
     {
       const char *lcd = m_lowest_common_denominator;
@@ -394,6 +458,39 @@ completion_tracker::completes_to_completion_word (const char *word)
   return false;
 }
 
+/* See completer.h.  */
+
+void
+complete_nested_command_line (completion_tracker &tracker, const char *text)
+{
+  /* Must be called from a custom-word-point completer.  */
+  gdb_assert (tracker.use_custom_word_point ());
+
+  /* Disable the custom word point temporarily, because we want to
+     probe whether the command we're completing itself uses a custom
+     word point.  */
+  tracker.set_use_custom_word_point (false);
+  size_t save_custom_word_point = tracker.custom_word_point ();
+
+  int quote_char = '\0';
+  const char *word = completion_find_completion_word (tracker, text,
+                                                     &quote_char);
+
+  if (tracker.use_custom_word_point ())
+    {
+      /* The command we're completing uses a custom word point, so the
+        tracker already contains the matches.  We're done.  */
+      return;
+    }
+
+  /* Restore the custom word point settings.  */
+  tracker.set_custom_word_point (save_custom_word_point);
+  tracker.set_use_custom_word_point (true);
+
+  /* Run the handle_completions completer phase.  */
+  complete_line (tracker, word, text, strlen (text));
+}
+
 /* Complete on linespecs, which might be of two possible forms:
 
        file:line
@@ -450,7 +547,7 @@ complete_files_symbols (completion_tracker &tracker,
          colon = p;
          symbol_start = p + 1;
        }
-      else if (strchr (current_language->la_word_break_characters(), *p))
+      else if (strchr (current_language->word_break_characters (), *p))
        symbol_start = p + 1;
     }
 
@@ -769,7 +866,11 @@ complete_explicit_location (completion_tracker &tracker,
   int keyword = skip_keyword (tracker, explicit_options, &text);
 
   if (keyword == -1)
-    complete_on_enum (tracker, explicit_options, text, text);
+    {
+      complete_on_enum (tracker, explicit_options, text, text);
+      /* There are keywords that start with "-".   Include them, too.  */
+      complete_on_enum (tracker, linespec_keywords, text, text);
+    }
   else
     {
       /* Completing on value.  */
@@ -798,14 +899,12 @@ complete_explicit_location (completion_tracker &tracker,
                   before: "b -function 'not_loaded_function_yet()'"
                   after:  "b -function 'not_loaded_function_yet()' "
              */
-             gdb::unique_xmalloc_ptr<char> text_copy
-               (xstrdup (text));
-             tracker.add_completion (std::move (text_copy));
+             tracker.add_completion (make_unique_xstrdup (text));
            }
          else if (quoted_arg_end[1] == ' ')
            {
              /* We're maybe past the explicit location argument.
-                Skip the argument without interpretion, assuming the
+                Skip the argument without interpretation, assuming the
                 user may want to create pending breakpoint.  Offer
                 the keyword and explicit location options as possible
                 completions.  */
@@ -956,108 +1055,31 @@ location_completer_handle_brkchars (struct cmd_list_element *ignore,
   location_completer (ignore, tracker, text, NULL);
 }
 
-/* Helper for expression_completer which recursively adds field and
-   method names from TYPE, a struct or union type, to the OUTPUT
-   list.  */
-
-static void
-add_struct_fields (struct type *type, completion_list &output,
-                  const char *fieldname, int namelen)
-{
-  int i;
-  int computed_type_name = 0;
-  const char *type_name = NULL;
-
-  type = check_typedef (type);
-  for (i = 0; i < TYPE_NFIELDS (type); ++i)
-    {
-      if (i < TYPE_N_BASECLASSES (type))
-       add_struct_fields (TYPE_BASECLASS (type, i),
-                          output, fieldname, namelen);
-      else if (TYPE_FIELD_NAME (type, i))
-       {
-         if (TYPE_FIELD_NAME (type, i)[0] != '\0')
-           {
-             if (! strncmp (TYPE_FIELD_NAME (type, i), 
-                            fieldname, namelen))
-               output.emplace_back (xstrdup (TYPE_FIELD_NAME (type, i)));
-           }
-         else if (TYPE_CODE (TYPE_FIELD_TYPE (type, i)) == TYPE_CODE_UNION)
-           {
-             /* Recurse into anonymous unions.  */
-             add_struct_fields (TYPE_FIELD_TYPE (type, i),
-                                output, fieldname, namelen);
-           }
-       }
-    }
-
-  for (i = TYPE_NFN_FIELDS (type) - 1; i >= 0; --i)
-    {
-      const char *name = TYPE_FN_FIELDLIST_NAME (type, i);
-
-      if (name && ! strncmp (name, fieldname, namelen))
-       {
-         if (!computed_type_name)
-           {
-             type_name = TYPE_NAME (type);
-             computed_type_name = 1;
-           }
-         /* Omit constructors from the completion list.  */
-         if (!type_name || strcmp (type_name, name))
-           output.emplace_back (xstrdup (name));
-       }
-    }
-}
-
 /* See completer.h.  */
 
 void
 complete_expression (completion_tracker &tracker,
                     const char *text, const char *word)
 {
-  struct type *type = NULL;
-  gdb::unique_xmalloc_ptr<char> fieldname;
-  enum type_code code = TYPE_CODE_UNDEF;
+  expression_up exp;
+  std::unique_ptr<expr_completion_base> expr_completer;
 
   /* Perform a tentative parse of the expression, to see whether a
      field completion is required.  */
-  TRY
+  try
     {
-      type = parse_expression_for_completion (text, &fieldname, &code);
+      exp = parse_expression_for_completion (text, &expr_completer);
     }
-  CATCH (except, RETURN_MASK_ERROR)
+  catch (const gdb_exception_error &except)
     {
       return;
     }
-  END_CATCH
-
-  if (fieldname != nullptr && type)
-    {
-      for (;;)
-       {
-         type = check_typedef (type);
-         if (TYPE_CODE (type) != TYPE_CODE_PTR && !TYPE_IS_REFERENCE (type))
-           break;
-         type = TYPE_TARGET_TYPE (type);
-       }
-
-      if (TYPE_CODE (type) == TYPE_CODE_UNION
-         || TYPE_CODE (type) == TYPE_CODE_STRUCT)
-       {
-         completion_list result;
 
-         add_struct_fields (type, result, fieldname.get (),
-                            strlen (fieldname.get ()));
-         tracker.add_completions (std::move (result));
-         return;
-       }
-    }
-  else if (fieldname != nullptr && code != TYPE_CODE_UNDEF)
-    {
-      collect_symbol_completion_matches_type (tracker, fieldname.get (),
-                                             fieldname.get (), code);
-      return;
-    }
+  /* Part of the parse_expression_for_completion contract.  */
+  gdb_assert ((exp == nullptr) == (expr_completer == nullptr));
+  if (expr_completer != nullptr
+      && expr_completer->complete (exp.get (), tracker))
+    return;
 
   complete_files_symbols (tracker, text, word);
 }
@@ -1082,23 +1104,6 @@ set_rl_completer_word_break_characters (const char *break_chars)
   rl_completer_word_break_characters = (char *) break_chars;
 }
 
-/* See definition in completer.h.  */
-
-void
-set_gdb_completion_word_break_characters (completer_ftype *fn)
-{
-  const char *break_chars;
-
-  /* So far we are only interested in differentiating filename
-     completers from everything else.  */
-  if (fn == filename_completer)
-    break_chars = gdb_completer_file_name_break_characters;
-  else
-    break_chars = gdb_completer_command_word_break_characters;
-
-  set_rl_completer_word_break_characters (break_chars);
-}
-
 /* Complete on symbols.  */
 
 void
@@ -1242,10 +1247,10 @@ complete_line_internal_1 (completion_tracker &tracker,
      on command strings (as opposed to strings supplied by the
      individual command completer functions, which can be any string)
      then we will switch to the special word break set for command
-     strings, which leaves out the '-' character used in some
+     strings, which leaves out the '-' and '.' character used in some
      commands.  */
   set_rl_completer_word_break_characters
-    (current_language->la_word_break_characters());
+    (current_language->word_break_characters ());
 
   /* Decide whether to complete on a list of gdb commands or on
      symbols.  */
@@ -1281,9 +1286,8 @@ complete_line_internal_1 (completion_tracker &tracker,
       result_list = 0;
     }
   else
-    {
-      c = lookup_cmd_1 (&p, cmdlist, &result_list, ignore_help_classes);
-    }
+    c = lookup_cmd_1 (&p, cmdlist, &result_list, NULL, ignore_help_classes,
+                     true);
 
   /* Move p up to the next interesting thing.  */
   while (*p == ' ' || *p == '\t')
@@ -1305,7 +1309,7 @@ complete_line_internal_1 (completion_tracker &tracker,
       /* lookup_cmd_1 advances p up to the first ambiguous thing, but
         doesn't advance over that thing itself.  Do so now.  */
       q = p;
-      while (*q && (isalnum (*q) || *q == '-' || *q == '_'))
+      while (valid_cmd_char_p (*q))
        ++q;
       if (q != tmp_command + point)
        {
@@ -1322,7 +1326,7 @@ complete_line_internal_1 (completion_tracker &tracker,
          if (result_list)
            {
              if (reason != handle_brkchars)
-               complete_on_cmdlist (*result_list->prefixlist, tracker, p,
+               complete_on_cmdlist (*result_list->subcommands, tracker, p,
                                     word, ignore_help_classes);
            }
          else
@@ -1350,12 +1354,12 @@ complete_line_internal_1 (completion_tracker &tracker,
            {
              /* The command is followed by whitespace; we need to
                 complete on whatever comes after command.  */
-             if (c->prefixlist)
+             if (c->is_prefix ())
                {
                  /* It is a prefix command; what comes after it is
                     a subcommand (e.g. "info ").  */
                  if (reason != handle_brkchars)
-                   complete_on_cmdlist (*c->prefixlist, tracker, p, word,
+                   complete_on_cmdlist (*c->subcommands, tracker, p, word,
                                         ignore_help_classes);
 
                  /* Ensure that readline does the right thing
@@ -1393,12 +1397,15 @@ complete_line_internal_1 (completion_tracker &tracker,
              q = p;
              while (q > tmp_command)
                {
-                 if (isalnum (q[-1]) || q[-1] == '-' || q[-1] == '_')
+                 if (valid_cmd_char_p (q[-1]))
                    --q;
                  else
                    break;
                }
 
+             /* Move the custom word point back too.  */
+             tracker.advance_custom_word_point_by (q - p);
+
              if (reason != handle_brkchars)
                complete_on_cmdlist (result_list, tracker, q, word,
                                     ignore_help_classes);
@@ -1415,7 +1422,7 @@ complete_line_internal_1 (completion_tracker &tracker,
        {
          /* There is non-whitespace beyond the command.  */
 
-         if (c->prefixlist && !c->allow_unknown)
+         if (c->is_prefix () && !c->allow_unknown)
            {
              /* It is an unrecognized subcommand of a prefix command,
                 e.g. "info adsfkdj".  */
@@ -1445,16 +1452,15 @@ complete_line_internal (completion_tracker &tracker,
                        const char *line_buffer, int point,
                        complete_line_internal_reason reason)
 {
-  TRY
+  try
     {
       complete_line_internal_1 (tracker, text, line_buffer, point, reason);
     }
-  CATCH (except, RETURN_MASK_ERROR)
+  catch (const gdb_exception_error &except)
     {
       if (except.error != MAX_COMPLETIONS_REACHED_ERROR)
-       throw_exception (except);
+       throw;
     }
-  END_CATCH
 }
 
 /* See completer.h.  */
@@ -1468,9 +1474,7 @@ int max_completions = 200;
 
 completion_tracker::completion_tracker ()
 {
-  m_entries_hash = htab_create_alloc (INITIAL_COMPLETION_HTAB_SIZE,
-                                     htab_hash_string, streq_hash,
-                                     NULL, xcalloc, xfree);
+  discard_completions ();
 }
 
 /* See completer.h.  */
@@ -1482,13 +1486,41 @@ completion_tracker::discard_completions ()
   m_lowest_common_denominator = NULL;
 
   m_lowest_common_denominator_unique = false;
+  m_lowest_common_denominator_valid = false;
 
-  m_entries_vec.clear ();
+  m_entries_hash.reset (nullptr);
 
-  htab_delete (m_entries_hash);
-  m_entries_hash = htab_create_alloc (INITIAL_COMPLETION_HTAB_SIZE,
-                                     htab_hash_string, streq_hash,
-                                     NULL, xcalloc, xfree);
+  /* A callback used by the hash table to compare new entries with existing
+     entries.  We can't use the standard htab_eq_string function here as the
+     key to our hash is just a single string, while the values we store in
+     the hash are a struct containing multiple strings.  */
+  static auto entry_eq_func
+    = [] (const void *first, const void *second) -> int
+      {
+       /* The FIRST argument is the entry already in the hash table, and
+          the SECOND argument is the new item being inserted.  */
+       const completion_hash_entry *entry
+         = (const completion_hash_entry *) first;
+       const char *name_str = (const char *) second;
+
+       return entry->is_name_eq (name_str);
+      };
+
+  /* Callback used by the hash table to compute the hash value for an
+     existing entry.  This is needed when expanding the hash table.  */
+  static auto entry_hash_func
+    = [] (const void *arg) -> hashval_t
+      {
+       const completion_hash_entry *entry
+         = (const completion_hash_entry *) arg;
+       return entry->hash_name ();
+      };
+
+  m_entries_hash.reset
+    (htab_create_alloc (INITIAL_COMPLETION_HTAB_SIZE,
+                       entry_hash_func, entry_eq_func,
+                       htab_delete_entry<completion_hash_entry>,
+                       xcalloc, xfree));
 }
 
 /* See completer.h.  */
@@ -1496,7 +1528,6 @@ completion_tracker::discard_completions ()
 completion_tracker::~completion_tracker ()
 {
   xfree (m_lowest_common_denominator);
-  htab_delete (m_entries_hash);
 }
 
 /* See completer.h.  */
@@ -1512,10 +1543,12 @@ completion_tracker::maybe_add_completion
   if (max_completions == 0)
     return false;
 
-  if (htab_elements (m_entries_hash) >= max_completions)
+  if (htab_elements (m_entries_hash.get ()) >= max_completions)
     return false;
 
-  slot = htab_find_slot (m_entries_hash, name.get (), INSERT);
+  hashval_t hash = htab_hash_string (name.get ());
+  slot = htab_find_slot_with_hash (m_entries_hash.get (), name.get (),
+                                  hash, INSERT);
   if (*slot == HTAB_EMPTY_ENTRY)
     {
       const char *match_for_lcd_str = NULL;
@@ -1529,10 +1562,12 @@ completion_tracker::maybe_add_completion
       gdb::unique_xmalloc_ptr<char> lcd
        = make_completion_match_str (match_for_lcd_str, text, word);
 
-      recompute_lowest_common_denominator (std::move (lcd));
+      size_t lcd_len = strlen (lcd.get ());
+      *slot = new completion_hash_entry (std::move (name), std::move (lcd));
 
-      *slot = name.get ();
-      m_entries_vec.push_back (std::move (name));
+      m_lowest_common_denominator_valid = false;
+      m_lowest_common_denominator_max_length
+       = std::max (m_lowest_common_denominator_max_length, lcd_len);
     }
 
   return true;
@@ -1558,6 +1593,20 @@ completion_tracker::add_completions (completion_list &&list)
     add_completion (std::move (candidate));
 }
 
+/* See completer.h.  */
+
+void
+completion_tracker::remove_completion (const char *name)
+{
+  hashval_t hash = htab_hash_string (name);
+  if (htab_find_slot_with_hash (m_entries_hash.get (), name, hash, NO_INSERT)
+      != NULL)
+    {
+      htab_remove_elt_with_hash (m_entries_hash.get (), name, hash);
+      m_lowest_common_denominator_valid = false;
+    }
+}
+
 /* Helper for the make_completion_match_str overloads.  Returns NULL
    as an indication that we want MATCH_NAME exactly.  It is up to the
    caller to xstrdup that string if desired.  */
@@ -1615,6 +1664,48 @@ make_completion_match_str (gdb::unique_xmalloc_ptr<char> &&match_name,
   return gdb::unique_xmalloc_ptr<char> (newobj);
 }
 
+/* See complete.h.  */
+
+completion_result
+complete (const char *line, char const **word, int *quote_char)
+{
+  completion_tracker tracker_handle_brkchars;
+  completion_tracker tracker_handle_completions;
+  completion_tracker *tracker;
+
+  /* The WORD should be set to the end of word to complete.  We initialize
+     to the completion point which is assumed to be at the end of LINE.
+     This leaves WORD to be initialized to a sensible value in cases
+     completion_find_completion_word() fails i.e., throws an exception.
+     See bug 24587. */
+  *word = line + strlen (line);
+
+  try
+    {
+      *word = completion_find_completion_word (tracker_handle_brkchars,
+                                             line, quote_char);
+
+      /* Completers that provide a custom word point in the
+        handle_brkchars phase also compute their completions then.
+        Completers that leave the completion word handling to readline
+        must be called twice.  */
+      if (tracker_handle_brkchars.use_custom_word_point ())
+       tracker = &tracker_handle_brkchars;
+      else
+       {
+         complete_line (tracker_handle_completions, *word, line, strlen (line));
+         tracker = &tracker_handle_completions;
+       }
+    }
+  catch (const gdb_exception &ex)
+    {
+      return {};
+    }
+
+  return tracker->build_completion_result (*word, *word - line, strlen (line));
+}
+
+
 /* 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.
@@ -1683,10 +1774,7 @@ signal_completer (struct cmd_list_element *ignore,
        continue;
 
       if (strncasecmp (signame, word, len) == 0)
-       {
-         gdb::unique_xmalloc_ptr<char> copy (xstrdup (signame));
-         tracker.add_completion (std::move (copy));
-       }
+       tracker.add_completion (make_unique_xstrdup (signame));
     }
 }
 
@@ -1725,27 +1813,17 @@ reg_or_group_completer_1 (completion_tracker &tracker,
           i++)
        {
          if (*name != '\0' && strncmp (word, name, len) == 0)
-           {
-             gdb::unique_xmalloc_ptr<char> copy (xstrdup (name));
-             tracker.add_completion (std::move (copy));
-           }
+           tracker.add_completion (make_unique_xstrdup (name));
        }
     }
 
   if ((targets & complete_reggroup_names) != 0)
     {
-      struct reggroup *group;
-
-      for (group = reggroup_next (gdbarch, NULL);
-          group != NULL;
-          group = reggroup_next (gdbarch, group))
+      for (const struct reggroup *group : gdbarch_reggroups (gdbarch))
        {
-         name = reggroup_name (group);
+         name = group->name ();
          if (strncmp (word, name, len) == 0)
-           {
-             gdb::unique_xmalloc_ptr<char> copy (xstrdup (name));
-             tracker.add_completion (std::move (copy));
-           }
+           tracker.add_completion (make_unique_xstrdup (name));
        }
     }
 }
@@ -1781,7 +1859,7 @@ default_completer_handle_brkchars (struct cmd_list_element *ignore,
                                   const char *text, const char *word)
 {
   set_rl_completer_word_break_characters
-    (current_language->la_word_break_characters ());
+    (current_language->word_break_characters ());
 }
 
 /* See definition in completer.h.  */
@@ -1833,6 +1911,9 @@ gdb_completion_word_break_characters_throw ()
     {
       gdb_assert (tracker.custom_word_point () > 0);
       rl_point = tracker.custom_word_point () - 1;
+
+      gdb_assert (rl_point >= 0 && rl_point < strlen (rl_line_buffer));
+
       gdb_custom_word_point_brkchars[0] = rl_line_buffer[rl_point];
       rl_completer_word_break_characters = gdb_custom_word_point_brkchars;
       rl_completer_quote_characters = NULL;
@@ -1850,7 +1931,7 @@ gdb_completion_word_break_characters_throw ()
       rl_basic_quote_characters = NULL;
     }
 
-  return rl_completer_word_break_characters;
+  return (char *) rl_completer_word_break_characters;
 }
 
 char *
@@ -1859,17 +1940,16 @@ gdb_completion_word_break_characters ()
   /* New completion starting.  */
   current_completion.aborted = false;
 
-  TRY
+  try
     {
       return gdb_completion_word_break_characters_throw ();
     }
-  CATCH (ex, RETURN_MASK_ALL)
+  catch (const gdb_exception &ex)
     {
       /* Set this to that gdb_rl_attempted_completion_function knows
         to abort early.  */
       current_completion.aborted = true;
     }
-  END_CATCH
 
   return NULL;
 }
@@ -1903,23 +1983,23 @@ completion_find_completion_word (completion_tracker &tracker, const char *text,
 /* See completer.h.  */
 
 void
-completion_tracker::recompute_lowest_common_denominator
-  (gdb::unique_xmalloc_ptr<char> &&new_match_up)
+completion_tracker::recompute_lcd_visitor (completion_hash_entry *entry)
 {
-  if (m_lowest_common_denominator == NULL)
+  if (!m_lowest_common_denominator_valid)
     {
-      /* We don't have a lowest common denominator yet, so simply take
-        the whole NEW_MATCH_UP as being it.  */
-      m_lowest_common_denominator = new_match_up.release ();
+      /* This is the first lowest common denominator that we are
+        considering, just copy it in.  */
+      strcpy (m_lowest_common_denominator, entry->get_lcd ());
       m_lowest_common_denominator_unique = true;
+      m_lowest_common_denominator_valid = true;
     }
   else
     {
-      /* Find the common denominator between the currently-known
-        lowest common denominator and NEW_MATCH_UP.  That becomes the
-        new lowest common denominator.  */
+      /* Find the common denominator between the currently-known lowest
+        common denominator and NEW_MATCH_UP.  That becomes the new lowest
+        common denominator.  */
       size_t i;
-      const char *new_match = new_match_up.get ();
+      const char *new_match = entry->get_lcd ();
 
       for (i = 0;
           (new_match[i] != '\0'
@@ -1937,7 +2017,36 @@ completion_tracker::recompute_lowest_common_denominator
 /* See completer.h.  */
 
 void
-completion_tracker::advance_custom_word_point_by (size_t len)
+completion_tracker::recompute_lowest_common_denominator ()
+{
+  /* We've already done this.  */
+  if (m_lowest_common_denominator_valid)
+    return;
+
+  /* Resize the storage to ensure we have enough space, the plus one gives
+     us space for the trailing null terminator we will include.  */
+  m_lowest_common_denominator
+    = (char *) xrealloc (m_lowest_common_denominator,
+                        m_lowest_common_denominator_max_length + 1);
+
+  /* Callback used to visit each entry in the m_entries_hash.  */
+  auto visitor_func
+    = [] (void **slot, void *info) -> int
+      {
+       completion_tracker *obj = (completion_tracker *) info;
+       completion_hash_entry *entry = (completion_hash_entry *) *slot;
+       obj->recompute_lcd_visitor (entry);
+       return 1;
+      };
+
+  htab_traverse (m_entries_hash.get (), visitor_func, this);
+  m_lowest_common_denominator_valid = true;
+}
+
+/* See completer.h.  */
+
+void
+completion_tracker::advance_custom_word_point_by (int len)
 {
   m_custom_word_point += len;
 }
@@ -2013,16 +2122,17 @@ completion_result
 completion_tracker::build_completion_result (const char *text,
                                             int start, int end)
 {
-  completion_list &list = m_entries_vec;       /* The completions.  */
+  size_t element_count = htab_elements (m_entries_hash.get ());
 
-  if (list.empty ())
+  if (element_count == 0)
     return {};
 
   /* +1 for the LCD, and +1 for NULL termination.  */
-  char **match_list = XNEWVEC (char *, 1 + list.size () + 1);
+  char **match_list = XNEWVEC (char *, 1 + element_count + 1);
 
   /* Build replacement word, based on the LCD.  */
 
+  recompute_lowest_common_denominator ();
   match_list[0]
     = expand_preserving_ws (text, end - start,
                            m_lowest_common_denominator);
@@ -2032,7 +2142,7 @@ completion_tracker::build_completion_result (const char *text,
       /* We don't rely on readline appending the quote char as
         delimiter as then readline wouldn't append the ' ' after the
         completion.  */
-      char buf[2] = { quote_char () };
+      char buf[2] = { (char) quote_char () };
 
       match_list[0] = reconcat (match_list[0], match_list[0],
                                buf, (char *) NULL);
@@ -2041,21 +2151,50 @@ completion_tracker::build_completion_result (const char *text,
       /* If the tracker wants to, or we already have a space at the
         end of the match, tell readline to skip appending
         another.  */
+      char *match = match_list[0];
       bool completion_suppress_append
        = (suppress_append_ws ()
-          || match_list[0][strlen (match_list[0]) - 1] == ' ');
+          || (match[0] != '\0'
+              && match[strlen (match) - 1] == ' '));
 
       return completion_result (match_list, 1, completion_suppress_append);
     }
   else
     {
-      int ix;
+      /* State object used while building the completion list.  */
+      struct list_builder
+      {
+       list_builder (char **ml)
+         : match_list (ml),
+           index (1)
+       { /* Nothing.  */ }
+
+       /* The list we are filling.  */
+       char **match_list;
+
+       /* The next index in the list to write to.  */
+       int index;
+      };
+      list_builder builder (match_list);
+
+      /* Visit each entry in m_entries_hash and add it to the completion
+        list, updating the builder state object.  */
+      auto func
+       = [] (void **slot, void *info) -> int
+         {
+           completion_hash_entry *entry = (completion_hash_entry *) *slot;
+           list_builder *state = (list_builder *) info;
 
-      for (ix = 0; ix < list.size (); ++ix)
-       match_list[ix + 1] = list[ix].release ();
-      match_list[ix + 1] = NULL;
+           state->match_list[state->index] = entry->release_name ();
+           state->index++;
+           return 1;
+         };
 
-      return completion_result (match_list, list.size (), false);
+      /* Build the completion list and add a null at the end.  */
+      htab_traverse_noresize (m_entries_hash.get (), func, &builder);
+      match_list[builder.index] = NULL;
+
+      return completion_result (match_list, builder.index - 1, false);
     }
 }
 
@@ -2085,15 +2224,11 @@ completion_result::~completion_result ()
 
 /* See completer.h  */
 
-completion_result::completion_result (completion_result &&rhs)
+completion_result::completion_result (completion_result &&rhs) noexcept
+  : match_list (rhs.match_list),
+    number_matches (rhs.number_matches)
 {
-  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;
 }
 
@@ -2207,14 +2342,13 @@ gdb_rl_attempted_completion_function (const char *text, int start, int end)
   if (current_completion.aborted)
     return NULL;
 
-  TRY
+  try
     {
       return gdb_rl_attempted_completion_function_throw (text, start, end);
     }
-  CATCH (ex, RETURN_MASK_ALL)
+  catch (const gdb_exception &ex)
     {
     }
-  END_CATCH
 
   return NULL;
 }
@@ -2236,7 +2370,7 @@ skip_quoted_chars (const char *str, const char *quotechars,
     quotechars = gdb_completer_quote_characters;
 
   if (breakchars == NULL)
-    breakchars = current_language->la_word_break_characters();
+    breakchars = current_language->word_break_characters ();
 
   for (scan = str; *scan != '\0'; scan++)
     {
@@ -2416,8 +2550,8 @@ gdb_printable_part (char *pathname)
   else if (temp[1] == '\0')
     {
       for (x = temp - 1; x > pathname; x--)
-        if (*x == '/')
-          break;
+       if (*x == '/')
+         break;
       return ((*x == '/') ? x + 1 : pathname);
     }
   else
@@ -2519,15 +2653,15 @@ gdb_fnprint (const char *to_print, int prefix_bytes,
   while (*s)
     {
       if (CTRL_CHAR (*s))
-        {
-          displayer->putch (displayer, '^');
-          displayer->putch (displayer, UNCTRL (*s));
-          printed_len += 2;
-          s++;
+       {
+         displayer->putch (displayer, '^');
+         displayer->putch (displayer, UNCTRL (*s));
+         printed_len += 2;
+         s++;
 #if defined (HANDLE_MULTIBYTE)
          memset (&ps, 0, sizeof (mbstate_t));
 #endif
-        }
+       }
       else if (*s == RUBOUT)
        {
          displayer->putch (displayer, '^');
@@ -2857,8 +2991,9 @@ gdb_display_match_list (char **matches, int len, int max,
     }
 }
 
+void _initialize_completer ();
 void
-_initialize_completer (void)
+_initialize_completer ()
 {
   add_setshow_zuinteger_unlimited_cmd ("max-completions", no_class,
                                       &max_completions, _("\