+ /* Find the command we are completing on. */
+ q = p;
+ while (q > tmp_command)
+ {
+ 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);
+
+ /* Ensure that readline does the right thing
+ with respect to inserting quotes. */
+ set_rl_completer_word_break_characters
+ (gdb_completer_command_word_break_characters);
+ }
+ }
+ else if (reason == handle_help)
+ ;
+ else
+ {
+ /* There is non-whitespace beyond the command. */
+
+ if (c->is_prefix () && !c->allow_unknown)
+ {
+ /* It is an unrecognized subcommand of a prefix command,
+ e.g. "info adsfkdj". */
+ }
+ else if (c->enums)
+ {
+ if (reason != handle_brkchars)
+ complete_on_enum (tracker, c->enums, p, word);
+ }
+ else
+ {
+ /* It is a normal command. */
+ complete_line_internal_normal_command (tracker,
+ tmp_command, word, p,
+ reason, c);
+ }
+ }
+ }
+}
+
+/* Wrapper around complete_line_internal_1 to handle
+ MAX_COMPLETIONS_REACHED_ERROR. */
+
+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 (const gdb_exception_error &except)
+ {
+ if (except.error != MAX_COMPLETIONS_REACHED_ERROR)
+ throw;
+ }
+}
+
+/* See completer.h. */
+
+int max_completions = 200;
+
+/* Initial size of the table. It automagically grows from here. */
+#define INITIAL_COMPLETION_HTAB_SIZE 200
+
+/* See completer.h. */
+
+completion_tracker::completion_tracker ()
+{
+ discard_completions ();
+}
+
+/* See completer.h. */
+
+void
+completion_tracker::discard_completions ()
+{
+ xfree (m_lowest_common_denominator);
+ m_lowest_common_denominator = NULL;
+
+ m_lowest_common_denominator_unique = false;
+ m_lowest_common_denominator_valid = false;
+
+ m_entries_hash.reset (nullptr);
+
+ /* 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. */
+
+completion_tracker::~completion_tracker ()
+{
+ xfree (m_lowest_common_denominator);
+}
+
+/* See completer.h. */
+
+bool
+completion_tracker::maybe_add_completion
+ (gdb::unique_xmalloc_ptr<char> name,
+ completion_match_for_lcd *match_for_lcd,
+ const char *text, const char *word)
+{
+ void **slot;
+
+ if (max_completions == 0)
+ return false;
+
+ if (htab_elements (m_entries_hash.get ()) >= max_completions)
+ return false;
+
+ 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;
+
+ if (match_for_lcd != NULL)
+ match_for_lcd_str = match_for_lcd->finish ();
+
+ if (match_for_lcd_str == NULL)
+ match_for_lcd_str = name.get ();
+
+ gdb::unique_xmalloc_ptr<char> lcd
+ = make_completion_match_str (match_for_lcd_str, text, word);
+
+ size_t lcd_len = strlen (lcd.get ());
+ *slot = new completion_hash_entry (std::move (name), std::move (lcd));
+
+ m_lowest_common_denominator_valid = false;
+ m_lowest_common_denominator_max_length
+ = std::max (m_lowest_common_denominator_max_length, lcd_len);
+ }
+
+ return true;
+}
+
+/* See completer.h. */
+
+void
+completion_tracker::add_completion (gdb::unique_xmalloc_ptr<char> name,
+ completion_match_for_lcd *match_for_lcd,
+ const char *text, const char *word)
+{
+ if (!maybe_add_completion (std::move (name), match_for_lcd, text, word))
+ throw_error (MAX_COMPLETIONS_REACHED_ERROR, _("Max completions reached."));
+}
+
+/* See completer.h. */
+
+void
+completion_tracker::add_completions (completion_list &&list)
+{
+ for (auto &candidate : 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. */
+
+static char *
+make_completion_match_str_1 (const char *match_name,
+ const char *text, const char *word)
+{
+ char *newobj;
+
+ if (word == text)
+ {
+ /* Return NULL as an indication that we want MATCH_NAME
+ exactly. */
+ return NULL;
+ }
+ else if (word > text)
+ {
+ /* Return some portion of MATCH_NAME. */
+ newobj = xstrdup (match_name + (word - text));
+ }
+ else
+ {
+ /* Return some of WORD plus MATCH_NAME. */
+ size_t len = strlen (match_name);
+ newobj = (char *) xmalloc (text - word + len + 1);
+ memcpy (newobj, word, text - word);
+ memcpy (newobj + (text - word), match_name, len + 1);
+ }
+
+ return newobj;
+}
+
+/* See completer.h. */
+
+gdb::unique_xmalloc_ptr<char>
+make_completion_match_str (const char *match_name,
+ const char *text, const char *word)
+{
+ char *newobj = make_completion_match_str_1 (match_name, text, word);
+ if (newobj == NULL)
+ newobj = xstrdup (match_name);
+ return gdb::unique_xmalloc_ptr<char> (newobj);
+}
+
+/* See completer.h. */