+/* A completer function for explicit locations. This function
+ completes both options ("-source", "-line", etc) and values. If
+ completing a quoted string, then QUOTED_ARG_START and
+ QUOTED_ARG_END point to the quote characters. LANGUAGE is the
+ current language. */
+
+static void
+complete_explicit_location (completion_tracker &tracker,
+ struct event_location *location,
+ const char *text,
+ const language_defn *language,
+ const char *quoted_arg_start,
+ const char *quoted_arg_end)
+{
+ if (*text != '-')
+ return;
+
+ int keyword = skip_keyword (tracker, explicit_options, &text);
+
+ if (keyword == -1)
+ complete_on_enum (tracker, explicit_options, text, text);
+ else
+ {
+ /* Completing on value. */
+ enum explicit_location_match_type what
+ = (explicit_location_match_type) keyword;
+
+ if (quoted_arg_start != NULL && quoted_arg_end != NULL)
+ {
+ if (quoted_arg_end[1] == '\0')
+ {
+ /* If completing a quoted string with the cursor right
+ at the terminating quote char, complete the
+ completion word without interpretation, so that
+ readline advances the cursor one whitespace past the
+ quote, even if there's no match. This makes these
+ cases behave the same:
+
+ before: "b -function function()"
+ after: "b -function function() "
+
+ before: "b -function 'function()'"
+ after: "b -function 'function()' "
+
+ and trusts the user in this case:
+
+ 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));
+ }
+ else if (quoted_arg_end[1] == ' ')
+ {
+ /* We're maybe past the explicit location argument.
+ Skip the argument without interpretion, assuming the
+ user may want to create pending breakpoint. Offer
+ the keyword and explicit location options as possible
+ completions. */
+ tracker.advance_custom_word_point_by (strlen (text));
+ complete_on_enum (tracker, linespec_keywords, "", "");
+ complete_on_enum (tracker, explicit_options, "", "");
+ }
+ return;
+ }
+
+ /* Now gather matches */
+ collect_explicit_location_matches (tracker, location, what, text,
+ language);
+ }
+}
+
+/* A completer for locations. */
+
+void
+location_completer (struct cmd_list_element *ignore,
+ completion_tracker &tracker,
+ const char *text, const char * /* word */)
+{
+ int found_probe_option = -1;
+
+ /* If we have a probe modifier, skip it. This can only appear as
+ first argument. Until we have a specific completer for probes,
+ falling back to the linespec completer for the remainder of the
+ line is better than nothing. */
+ if (text[0] == '-' && text[1] == 'p')
+ found_probe_option = skip_keyword (tracker, probe_options, &text);
+
+ const char *option_text = text;
+ int saved_word_point = tracker.custom_word_point ();
+
+ const char *copy = text;
+
+ explicit_completion_info completion_info;
+ event_location_up location
+ = string_to_explicit_location (©, current_language,
+ &completion_info);
+ if (completion_info.quoted_arg_start != NULL
+ && completion_info.quoted_arg_end == NULL)
+ {
+ /* Found an unbalanced quote. */
+ tracker.set_quote_char (*completion_info.quoted_arg_start);
+ tracker.advance_custom_word_point_by (1);
+ }
+
+ if (location != NULL)
+ {
+ if (*copy != '\0')
+ {
+ tracker.advance_custom_word_point_by (copy - text);
+ text = copy;
+
+ /* We found a terminator at the tail end of the string,
+ which means we're past the explicit location options. We
+ may have a keyword to complete on. If we have a whole
+ keyword, then complete whatever comes after as an
+ expression. This is mainly for the "if" keyword. If the
+ "thread" and "task" keywords gain their own completers,
+ they should be used here. */
+ int keyword = skip_keyword (tracker, linespec_keywords, &text);
+
+ if (keyword == -1)
+ {
+ complete_on_enum (tracker, linespec_keywords, text, text);
+ }
+ else
+ {
+ const char *word
+ = advance_to_expression_complete_word_point (tracker, text);
+ complete_expression (tracker, text, word);
+ }
+ }
+ else
+ {
+ tracker.advance_custom_word_point_by (completion_info.last_option
+ - text);
+ text = completion_info.last_option;
+
+ complete_explicit_location (tracker, location.get (), text,
+ current_language,
+ completion_info.quoted_arg_start,
+ completion_info.quoted_arg_end);
+
+ }
+ }
+ else
+ {
+ /* This is an address or linespec location. */
+ complete_address_and_linespec_locations (tracker, text);
+ }
+
+ /* Add matches for option names, if either:
+
+ - Some completer above found some matches, but the word point did
+ not advance (e.g., "b <tab>" finds all functions, or "b -<tab>"
+ matches all objc selectors), or;
+
+ - Some completer above advanced the word point, but found no
+ matches.
+ */
+ if ((text[0] == '-' || text[0] == '\0')
+ && (!tracker.have_completions ()
+ || tracker.custom_word_point () == saved_word_point))
+ {
+ tracker.set_custom_word_point (saved_word_point);
+ text = option_text;
+
+ if (found_probe_option == -1)
+ complete_on_enum (tracker, probe_options, text, text);
+ complete_on_enum (tracker, explicit_options, text, text);
+ }
+}
+
+/* The corresponding completer_handle_brkchars
+ implementation. */
+
+static void
+location_completer_handle_brkchars (struct cmd_list_element *ignore,
+ completion_tracker &tracker,
+ const char *text,
+ const char *word_ignored)
+{
+ tracker.set_use_custom_word_point (true);
+
+ 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,
+ 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_no_tag (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;
+ char *fieldname;
+ enum type_code code = TYPE_CODE_UNDEF;
+
+ /* Perform a tentative parse of the expression, to see whether a
+ field completion is required. */
+ fieldname = NULL;
+ TRY
+ {
+ type = parse_expression_for_completion (text, &fieldname, &code);
+ }
+ CATCH (except, RETURN_MASK_ERROR)
+ {
+ return;
+ }
+ END_CATCH
+
+ if (fieldname && 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)
+ {
+ int flen = strlen (fieldname);
+ completion_list result;
+
+ add_struct_fields (type, result, fieldname, flen);
+ xfree (fieldname);
+ tracker.add_completions (std::move (result));
+ return;
+ }
+ }
+ else if (fieldname && code != TYPE_CODE_UNDEF)
+ {
+ VEC (char_ptr) *result;
+ struct cleanup *cleanup = make_cleanup (xfree, fieldname);
+
+ collect_symbol_completion_matches_type (tracker, fieldname, fieldname,
+ code);
+ do_cleanups (cleanup);
+ return;
+ }
+ xfree (fieldname);
+
+ 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. */
+
+void
+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
+symbol_completer (struct cmd_list_element *ignore,
+ completion_tracker &tracker,
+ const char *text, const char *word)
+{
+ collect_symbol_completion_matches (tracker, complete_symbol_mode::EXPRESSION,
+ text, word);
+}
+
+/* Here are some useful test cases for completion. FIXME: These
+ should be put in the test suite. They should be tested with both
+ M-? and TAB.
+
+ "show output-" "radix"
+ "show output" "-radix"
+ "p" ambiguous (commands starting with p--path, print, printf, etc.)
+ "p " ambiguous (all symbols)
+ "info t foo" no completions
+ "info t " no completions
+ "info t" ambiguous ("info target", "info terminal", etc.)
+ "info ajksdlfk" no completions
+ "info ajksdlfk " no completions
+ "info" " "
+ "info " ambiguous (all info commands)
+ "p \"a" no completions (string constant)
+ "p 'a" ambiguous (all symbols starting with a)
+ "p b-a" ambiguous (all symbols starting with a)
+ "p b-" ambiguous (all symbols)
+ "file Make" "file" (word break hard to screw up here)
+ "file ../gdb.stabs/we" "ird" (needs to not break word at slash)
+ */
+
+enum complete_line_internal_reason
+{
+ /* Preliminary phase, called by gdb_completion_word_break_characters
+ function, is used to either:
+
+ #1 - Determine the set of chars that are word delimiters
+ depending on the current command in line_buffer.
+
+ #2 - Manually advance RL_POINT to the "word break" point instead
+ of letting readline do it (based on too-simple character
+ matching).
+
+ Simpler completers that just pass a brkchars array to readline
+ (#1 above) must defer generating the completions to the main
+ phase (below). No completion list should be generated in this
+ phase.
+
+ OTOH, completers that manually advance the word point(#2 above)
+ must set "use_custom_word_point" in the tracker and generate
+ their completion in this phase. Note that this is the convenient
+ thing to do since they'll be parsing the input line anyway. */
+ handle_brkchars,
+
+ /* Main phase, called by complete_line function, is used to get the
+ list of possible completions. */
+ handle_completions,
+
+ /* 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 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)
+{
+ const char *p = cmd_args;
+
+ if (c->completer == filename_completer)
+ {
+ /* Many commands which want to complete on file names accept
+ several file names, as in "run foo bar >>baz". So we don't
+ want to complete the entire text after the command, just the
+ last word. To this end, we need to find the beginning of the
+ file name by starting at `word' and going backwards. */
+ for (p = word;
+ p > command
+ && strchr (gdb_completer_file_name_break_characters,
+ p[-1]) == NULL;
+ p--)
+ ;
+ }
+
+ if (reason == handle_brkchars)
+ {
+ completer_handle_brkchars_ftype *brkchars_fn;
+
+ if (c->completer_handle_brkchars != NULL)
+ brkchars_fn = c->completer_handle_brkchars;
+ else
+ {
+ brkchars_fn
+ = (completer_handle_brkchars_func_for_completer
+ (c->completer));
+ }
+
+ brkchars_fn (c, tracker, p, word);
+ }
+
+ if (reason != handle_brkchars && c->completer != NULL)
+ (*c->completer) (c, tracker, p, word);
+}
+
+/* Internal function used to handle completions.
+
+
+ TEXT is the caller's idea of the "word" we are looking at.
+
+ 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.
+
+ See complete_line_internal_reason for description of REASON. */
+
+static void
+complete_line_internal_1 (completion_tracker &tracker,
+ const char *text,
+ const char *line_buffer, int point,
+ complete_line_internal_reason reason)
+{
+ char *tmp_command;
+ const char *p;
+ int ignore_help_classes;
+ /* Pointer within tmp_command which corresponds to text. */
+ const char *word;
+ struct cmd_list_element *c, *result_list;
+
+ /* Choose the default set of word break characters to break
+ completions. If we later find out that we are doing completions
+ 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
+ commands. */
+ set_rl_completer_word_break_characters
+ (current_language->la_word_break_characters());
+
+ /* Decide whether to complete on a list of gdb commands or on
+ symbols. */
+ tmp_command = (char *) alloca (point + 1);
+ p = tmp_command;
+
+ /* The help command should complete help aliases. */
+ ignore_help_classes = reason != handle_help;
+
+ strncpy (tmp_command, line_buffer, point);
+ tmp_command[point] = '\0';
+ 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)
+ {
+ /* An empty line we want to consider ambiguous; that is, it
+ could be any command. */
+ c = CMD_LIST_AMBIGUOUS;
+ result_list = 0;
+ }
+ else
+ {
+ c = lookup_cmd_1 (&p, cmdlist, &result_list, ignore_help_classes);
+ }
+
+ /* Move p up to the next interesting thing. */
+ while (*p == ' ' || *p == '\t')
+ {
+ p++;
+ }
+
+ tracker.advance_custom_word_point_by (p - tmp_command);
+
+ if (!c)
+ {
+ /* It is an unrecognized command. So there are no
+ possible completions. */
+ }
+ else if (c == CMD_LIST_AMBIGUOUS)
+ {
+ const char *q;
+
+ /* 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 == '_'))
+ ++q;
+ if (q != tmp_command + point)
+ {
+ /* There is something beyond the ambiguous
+ command, so there are no possible completions. For
+ example, "info t " or "info t foo" does not complete
+ to anything, because "info t" can be "info target" or
+ "info terminal". */
+ }
+ else
+ {
+ /* We're trying to complete on the command which was ambiguous.
+ This we can deal with. */
+ if (result_list)
+ {
+ if (reason != handle_brkchars)
+ complete_on_cmdlist (*result_list->prefixlist, tracker, p,
+ word, ignore_help_classes);
+ }
+ else
+ {
+ if (reason != handle_brkchars)
+ complete_on_cmdlist (cmdlist, tracker, p, 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
+ {
+ /* We've recognized a full command. */
+
+ if (p == tmp_command + point)
+ {
+ /* There is no non-whitespace in the line beyond the
+ command. */
+
+ if (p[-1] == ' ' || p[-1] == '\t')
+ {
+ /* The command is followed by whitespace; we need to
+ complete on whatever comes after command. */
+ if (c->prefixlist)
+ {
+ /* 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,
+ 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 if (c->enums)
+ {
+ if (reason != handle_brkchars)
+ complete_on_enum (tracker, c->enums, p, word);
+ set_rl_completer_word_break_characters
+ (gdb_completer_command_word_break_characters);
+ }
+ else
+ {
+ /* It is a normal command; what comes after it is
+ completed by the command's completer function. */
+ complete_line_internal_normal_command (tracker,
+ tmp_command, word, p,
+ reason, c);
+ }
+ }
+ else
+ {
+ /* The command is not followed by whitespace; we need to
+ complete on the command itself, e.g. "p" which is a
+ command itself but also can complete to "print", "ptype"
+ etc. */
+ const char *q;
+
+ /* Find the command we are completing on. */
+ q = p;
+ while (q > tmp_command)
+ {
+ if (isalnum (q[-1]) || q[-1] == '-' || q[-1] == '_')
+ --q;
+ else
+ break;
+ }
+
+ 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->prefixlist && !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 (except, RETURN_MASK_ERROR)
+ {
+ if (except.error != MAX_COMPLETIONS_REACHED_ERROR)
+ throw_exception (except);
+ }
+}
+
+/* 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 ()
+{
+ m_entries_hash = htab_create_alloc (INITIAL_COMPLETION_HTAB_SIZE,
+ htab_hash_string, (htab_eq) streq,
+ NULL, xcalloc, xfree);
+}
+
+/* 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_entries_vec.clear ();
+
+ htab_delete (m_entries_hash);
+ m_entries_hash = htab_create_alloc (INITIAL_COMPLETION_HTAB_SIZE,
+ htab_hash_string, (htab_eq) streq,
+ NULL, xcalloc, xfree);
+}
+
+/* See completer.h. */
+
+completion_tracker::~completion_tracker ()
+{
+ xfree (m_lowest_common_denominator);
+ htab_delete (m_entries_hash);
+}
+
+/* See completer.h. */
+
+bool
+completion_tracker::maybe_add_completion (gdb::unique_xmalloc_ptr<char> name)
+{
+ void **slot;
+
+ if (max_completions == 0)
+ return false;
+
+ if (htab_elements (m_entries_hash) >= max_completions)
+ return false;
+
+ slot = htab_find_slot (m_entries_hash, name.get (), INSERT);
+ if (*slot == HTAB_EMPTY_ENTRY)
+ {
+ const char *match_for_lcd_str = name.get ();
+
+ recompute_lowest_common_denominator (match_for_lcd_str);
+
+ *slot = name.get ();
+ m_entries_vec.push_back (std::move (name));
+ }
+
+ return true;
+}
+
+/* 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
+completion_tracker::add_completions (completion_list &&list)
+{
+ for (auto &candidate : list)
+ add_completion (std::move (candidate));
+}
+
+/* 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.
+
+ 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. */
+
+void
+complete_line (completion_tracker &tracker,
+ const char *text, const char *line_buffer, int point)
+{
+ if (max_completions == 0)
+ return;
+ complete_line_internal (tracker, text, line_buffer, point,
+ handle_completions);
+}
+
+/* Complete on command names. Used by "help". */
+
+void
+command_completer (struct cmd_list_element *ignore,
+ completion_tracker &tracker,
+ const char *text, const char *word)
+{
+ 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
+ (gdb_completer_command_word_break_characters);
+}
+
+/* Complete on signals. */
+
+void
+signal_completer (struct cmd_list_element *ignore,
+ completion_tracker &tracker,
+ const char *text, const char *word)
+{
+ size_t len = strlen (word);
+ int signum;
+ const char *signame;
+
+ for (signum = GDB_SIGNAL_FIRST; signum != GDB_SIGNAL_LAST; ++signum)
+ {
+ /* Can't handle this, so skip it. */
+ if (signum == GDB_SIGNAL_0)
+ continue;
+
+ signame = gdb_signal_to_name ((enum gdb_signal) signum);
+
+ /* Ignore the unknown signal case. */
+ if (!signame || strcmp (signame, "?") == 0)
+ continue;
+
+ if (strncasecmp (signame, word, len) == 0)
+ {
+ gdb::unique_xmalloc_ptr<char> copy (xstrdup (signame));
+ tracker.add_completion (std::move (copy));
+ }
+ }
+}
+
+/* Bit-flags for selecting what the register and/or register-group
+ completer should complete on. */
+
+enum reg_completer_target
+ {
+ complete_register_names = 0x1,
+ complete_reggroup_names = 0x2
+ };
+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 void
+reg_or_group_completer_1 (completion_tracker &tracker,
+ const char *text, const char *word,
+ reg_completer_targets targets)
+{
+ size_t len = strlen (word);
+ struct gdbarch *gdbarch;
+ const char *name;
+
+ gdb_assert ((targets & (complete_register_names
+ | complete_reggroup_names)) != 0);
+ gdbarch = get_current_arch ();
+
+ if ((targets & complete_register_names) != 0)
+ {
+ int i;
+
+ for (i = 0;
+ (name = user_reg_map_regnum_to_name (gdbarch, i)) != NULL;
+ i++)
+ {
+ if (*name != '\0' && strncmp (word, name, len) == 0)
+ {
+ gdb::unique_xmalloc_ptr<char> copy (xstrdup (name));
+ tracker.add_completion (std::move (copy));
+ }
+ }
+ }
+
+ if ((targets & complete_reggroup_names) != 0)
+ {
+ struct reggroup *group;
+
+ for (group = reggroup_next (gdbarch, NULL);
+ group != NULL;
+ group = reggroup_next (gdbarch, group))
+ {
+ name = reggroup_name (group);
+ if (strncmp (word, name, len) == 0)
+ {
+ gdb::unique_xmalloc_ptr<char> copy (xstrdup (name));
+ tracker.add_completion (std::move (copy));
+ }
+ }
+ }
+}
+
+/* Perform completion on register and reggroup names. */
+
+void
+reg_or_group_completer (struct cmd_list_element *ignore,
+ completion_tracker &tracker,
+ const char *text, const char *word)
+{
+ reg_or_group_completer_1 (tracker, text, word,
+ (complete_register_names
+ | complete_reggroup_names));
+}
+
+/* Perform completion on reggroup names. */
+
+void
+reggroup_completer (struct cmd_list_element *ignore,
+ completion_tracker &tracker,
+ const char *text, const char *word)
+{
+ 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
+ (current_language->la_word_break_characters ());
+}
+
+/* See definition in completer.h. */
+
+completer_handle_brkchars_ftype *
+completer_handle_brkchars_func_for_completer (completer_ftype *fn)
+{
+ if (fn == filename_completer)
+ return filename_completer_handle_brkchars;
+
+ if (fn == location_completer)
+ return location_completer_handle_brkchars;
+
+ if (fn == command_completer)
+ return command_completer_handle_brkchars;
+
+ return default_completer_handle_brkchars;
+}
+
+/* Used as brkchars when we want to tell readline we have a custom
+ word point. We do that by making our rl_completion_word_break_hook
+ set RL_POINT to the desired word point, and return the character at
+ the word break point as the break char. This is two bytes in order
+ to fit one break character plus the terminating null. */
+static char gdb_custom_word_point_brkchars[2];
+
+/* Since rl_basic_quote_characters is not completer-specific, we save
+ its original value here, in order to be able to restore it in
+ gdb_rl_attempted_completion_function. */
+static const char *gdb_org_rl_basic_quote_characters = rl_basic_quote_characters;
+
+/* Get the list of chars that are considered as word breaks
+ for the current command. */
+
+static char *
+gdb_completion_word_break_characters_throw ()
+{
+ /* 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);
+
+ if (tracker.use_custom_word_point ())
+ {
+ gdb_assert (tracker.custom_word_point () > 0);
+ rl_point = tracker.custom_word_point () - 1;
+ 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;
+
+ /* Clear this too, so that if we're completing a quoted string,
+ readline doesn't consider the quote character a delimiter.
+ If we didn't do this, readline would auto-complete {b
+ 'fun<tab>} to {'b 'function()'}, i.e., add the terminating
+ \', but, it wouldn't append the separator space either, which
+ is not desirable. So instead we take care of appending the
+ quote character to the LCD ourselves, in
+ gdb_rl_attempted_completion_function. Since this global is
+ not just completer-specific, we'll restore it back to the
+ default in gdb_rl_attempted_completion_function. */
+ rl_basic_quote_characters = NULL;
+ }
+
+ return rl_completer_word_break_characters;
+}
+
+char *
+gdb_completion_word_break_characters ()
+{
+ /* New completion starting. */
+ current_completion.aborted = false;
+
+ 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
+
+ return NULL;
+}
+
+/* See completer.h. */
+
+const char *
+completion_find_completion_word (completion_tracker &tracker, const char *text,
+ int *quote_char)
+{
+ size_t point = strlen (text);
+
+ complete_line_internal (tracker, NULL, text, point, handle_brkchars);
+
+ if (tracker.use_custom_word_point ())
+ {
+ gdb_assert (tracker.custom_word_point () > 0);
+ *quote_char = tracker.quote_char ();
+ return text + tracker.custom_word_point ();
+ }
+
+ gdb_rl_completion_word_info info;
+
+ info.word_break_characters = rl_completer_word_break_characters;
+ info.quote_characters = gdb_completer_quote_characters;
+ info.basic_quote_characters = rl_basic_quote_characters;
+
+ return gdb_rl_find_completion_word (&info, quote_char, NULL, text);
+}
+
+/* See completer.h. */
+
+void
+completion_tracker::recompute_lowest_common_denominator (const char *new_match)
+{
+ 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
+ {
+ /* Find the common denominator between the currently-known
+ lowest common denominator and NEW_MATCH. That becomes the
+ new lowest common denominator. */
+ size_t i;
+
+ 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])
+ {
+ m_lowest_common_denominator[i] = '\0';
+ m_lowest_common_denominator_unique = false;
+ }
+ }
+}
+
+/* See completer.h. */
+
+void
+completion_tracker::advance_custom_word_point_by (size_t len)
+{
+ m_custom_word_point += len;
+}
+
+/* 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.:
+
+ "handle sig<>"
+
+ 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 (*p_orig == ' ')
+ {
+ while (p_orig < orig_end && *p_orig == ' ')
+ res += *p_orig++;
+ p_lcd = skip_spaces (p_lcd);
+ }
+ else
+ {
+ /* 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++;
+ }
+ }
+
+ 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. */
+
+ 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)
+ {
+ /* 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 () };
+
+ match_list[0] = reconcat (match_list[0], match_list[0],
+ buf, (char *) NULL);
+ match_list[1] = NULL;
+
+ /* If the tracker wants to, or we already have a space at the
+ end of the match, tell readline to skip appending
+ another. */
+ bool completion_suppress_append
+ = (suppress_append_ws ()
+ || 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;
+}
+
+/* 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 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 rl_point (i.e., END) is at column 0,
+ then readline skips the handle_brkchars phase, and so we create a
+ tracker now in that case too. */
+ if (end == 0 || !current_completion.tracker->use_custom_word_point ())
+ {
+ 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)
+{
+ /* Restore globals that might have been tweaked in
+ gdb_completion_word_break_characters. */
+ rl_basic_quote_characters = gdb_org_rl_basic_quote_characters;
+
+ /* 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
+ characters QUOTECHARS and the word break characters BREAKCHARS).
+ Returns pointer to the location after the "word". If either
+ QUOTECHARS or BREAKCHARS is NULL, use the same values used by the
+ completer. */
+
+const char *
+skip_quoted_chars (const char *str, const char *quotechars,
+ const char *breakchars)
+{
+ char quote_char = '\0';
+ const char *scan;
+
+ if (quotechars == NULL)
+ quotechars = gdb_completer_quote_characters;
+
+ if (breakchars == NULL)
+ breakchars = current_language->la_word_break_characters();
+
+ for (scan = str; *scan != '\0'; scan++)
+ {
+ if (quote_char != '\0')
+ {
+ /* Ignore everything until the matching close quote char. */
+ if (*scan == quote_char)
+ {
+ /* Found matching close quote. */
+ scan++;
+ break;
+ }
+ }
+ else if (strchr (quotechars, *scan))
+ {
+ /* Found start of a quoted string. */
+ quote_char = *scan;
+ }
+ else if (strchr (breakchars, *scan))
+ {
+ break;
+ }
+ }
+
+ return (scan);
+}
+
+/* Skip over the possibly quoted word STR (as defined by the quote
+ characters and word break characters used by the completer).
+ Returns pointer to the location after the "word". */
+
+const char *
+skip_quoted (const char *str)
+{
+ return skip_quoted_chars (str, NULL, NULL);
+}
+
+/* Return a message indicating that the maximum number of completions
+ has been reached and that there may be more. */
+
+const char *
+get_max_completions_reached_message (void)
+{
+ return _("*** List may be truncated, max-completions reached. ***");
+}
+\f
+/* GDB replacement for rl_display_match_list.
+ Readline doesn't provide a clean interface for TUI(curses).
+ A hack previously used was to send readline's rl_outstream through a pipe
+ and read it from the event loop. Bleah. IWBN if readline abstracted
+ away all the necessary bits, and this is what this code does. It
+ replicates the parts of readline we need and then adds an abstraction
+ layer, currently implemented as struct match_list_displayer, so that both
+ CLI and TUI can use it. We copy all this readline code to minimize
+ GDB-specific mods to readline. Once this code performs as desired then
+ we can submit it to the readline maintainers.
+
+ N.B. A lot of the code is the way it is in order to minimize differences
+ from readline's copy. */
+
+/* Not supported here. */
+#undef VISIBLE_STATS
+
+#if defined (HANDLE_MULTIBYTE)
+#define MB_INVALIDCH(x) ((x) == (size_t)-1 || (x) == (size_t)-2)
+#define MB_NULLWCH(x) ((x) == 0)
+#endif
+
+#define ELLIPSIS_LEN 3
+
+/* gdb version of readline/complete.c:get_y_or_n.
+ 'y' -> returns 1, and 'n' -> returns 0.
+ Also supported: space == 'y', RUBOUT == 'n', ctrl-g == start over.
+ If FOR_PAGER is non-zero, then also supported are:
+ NEWLINE or RETURN -> returns 2, and 'q' -> returns 0. */
+
+static int
+gdb_get_y_or_n (int for_pager, const struct match_list_displayer *displayer)
+{
+ int c;
+
+ for (;;)
+ {
+ RL_SETSTATE (RL_STATE_MOREINPUT);
+ c = displayer->read_key (displayer);
+ RL_UNSETSTATE (RL_STATE_MOREINPUT);
+
+ if (c == 'y' || c == 'Y' || c == ' ')
+ return 1;
+ if (c == 'n' || c == 'N' || c == RUBOUT)
+ return 0;
+ if (c == ABORT_CHAR || c < 0)
+ {
+ /* Readline doesn't erase_entire_line here, but without it the
+ --More-- prompt isn't erased and neither is the text entered
+ thus far redisplayed. */
+ displayer->erase_entire_line (displayer);
+ /* Note: The arguments to rl_abort are ignored. */
+ rl_abort (0, 0);
+ }
+ if (for_pager && (c == NEWLINE || c == RETURN))
+ return 2;
+ if (for_pager && (c == 'q' || c == 'Q'))
+ return 0;
+ displayer->beep (displayer);
+ }
+}
+
+/* Pager function for tab-completion.
+ This is based on readline/complete.c:_rl_internal_pager.
+ LINES is the number of lines of output displayed thus far.
+ Returns:
+ -1 -> user pressed 'n' or equivalent,
+ 0 -> user pressed 'y' or equivalent,
+ N -> user pressed NEWLINE or equivalent and N is LINES - 1. */
+
+static int
+gdb_display_match_list_pager (int lines,
+ const struct match_list_displayer *displayer)
+{
+ int i;
+
+ displayer->puts (displayer, "--More--");
+ displayer->flush (displayer);
+ i = gdb_get_y_or_n (1, displayer);
+ displayer->erase_entire_line (displayer);
+ if (i == 0)
+ return -1;
+ else if (i == 2)
+ return (lines - 1);
+ else
+ return 0;
+}
+
+/* Return non-zero if FILENAME is a directory.
+ Based on readline/complete.c:path_isdir. */
+
+static int
+gdb_path_isdir (const char *filename)
+{
+ struct stat finfo;
+
+ return (stat (filename, &finfo) == 0 && S_ISDIR (finfo.st_mode));
+}
+
+/* Return the portion of PATHNAME that should be output when listing
+ possible completions. If we are hacking filename completion, we
+ are only interested in the basename, the portion following the
+ final slash. Otherwise, we return what we were passed. Since
+ printing empty strings is not very informative, if we're doing
+ filename completion, and the basename is the empty string, we look
+ for the previous slash and return the portion following that. If
+ there's no previous slash, we just return what we were passed.
+
+ Based on readline/complete.c:printable_part. */
+
+static char *
+gdb_printable_part (char *pathname)
+{
+ char *temp, *x;
+
+ if (rl_filename_completion_desired == 0) /* don't need to do anything */
+ return (pathname);
+
+ temp = strrchr (pathname, '/');
+#if defined (__MSDOS__)
+ if (temp == 0 && ISALPHA ((unsigned char)pathname[0]) && pathname[1] == ':')
+ temp = pathname + 1;
+#endif
+
+ if (temp == 0 || *temp == '\0')
+ return (pathname);
+ /* If the basename is NULL, we might have a pathname like '/usr/src/'.
+ Look for a previous slash and, if one is found, return the portion
+ following that slash. If there's no previous slash, just return the
+ pathname we were passed. */
+ else if (temp[1] == '\0')
+ {
+ for (x = temp - 1; x > pathname; x--)
+ if (*x == '/')
+ break;
+ return ((*x == '/') ? x + 1 : pathname);
+ }
+ else
+ return ++temp;
+}
+
+/* Compute width of STRING when displayed on screen by print_filename.
+ Based on readline/complete.c:fnwidth. */
+
+static int
+gdb_fnwidth (const char *string)
+{
+ int width, pos;
+#if defined (HANDLE_MULTIBYTE)
+ mbstate_t ps;
+ int left, w;
+ size_t clen;
+ wchar_t wc;
+
+ left = strlen (string) + 1;
+ memset (&ps, 0, sizeof (mbstate_t));
+#endif
+
+ width = pos = 0;
+ while (string[pos])
+ {
+ if (CTRL_CHAR (string[pos]) || string[pos] == RUBOUT)
+ {
+ width += 2;
+ pos++;
+ }
+ else
+ {
+#if defined (HANDLE_MULTIBYTE)
+ clen = mbrtowc (&wc, string + pos, left - pos, &ps);
+ if (MB_INVALIDCH (clen))
+ {
+ width++;
+ pos++;
+ memset (&ps, 0, sizeof (mbstate_t));
+ }
+ else if (MB_NULLWCH (clen))
+ break;
+ else
+ {
+ pos += clen;
+ w = wcwidth (wc);
+ width += (w >= 0) ? w : 1;
+ }
+#else
+ width++;
+ pos++;
+#endif
+ }
+ }
+
+ return width;
+}
+
+/* Print TO_PRINT, one matching completion.
+ PREFIX_BYTES is number of common prefix bytes.
+ Based on readline/complete.c:fnprint. */
+
+static int
+gdb_fnprint (const char *to_print, int prefix_bytes,
+ const struct match_list_displayer *displayer)
+{
+ int printed_len, w;
+ const char *s;
+#if defined (HANDLE_MULTIBYTE)
+ mbstate_t ps;
+ const char *end;
+ size_t tlen;
+ int width;
+ wchar_t wc;
+
+ end = to_print + strlen (to_print) + 1;
+ memset (&ps, 0, sizeof (mbstate_t));
+#endif
+
+ printed_len = 0;
+
+ /* Don't print only the ellipsis if the common prefix is one of the
+ possible completions */
+ if (to_print[prefix_bytes] == '\0')
+ prefix_bytes = 0;
+
+ if (prefix_bytes)
+ {
+ char ellipsis;
+
+ ellipsis = (to_print[prefix_bytes] == '.') ? '_' : '.';
+ for (w = 0; w < ELLIPSIS_LEN; w++)
+ displayer->putch (displayer, ellipsis);
+ printed_len = ELLIPSIS_LEN;
+ }
+
+ s = to_print + prefix_bytes;
+ while (*s)
+ {
+ if (CTRL_CHAR (*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, '^');
+ displayer->putch (displayer, '?');
+ printed_len += 2;
+ s++;
+#if defined (HANDLE_MULTIBYTE)
+ memset (&ps, 0, sizeof (mbstate_t));
+#endif
+ }
+ else
+ {
+#if defined (HANDLE_MULTIBYTE)
+ tlen = mbrtowc (&wc, s, end - s, &ps);
+ if (MB_INVALIDCH (tlen))
+ {
+ tlen = 1;
+ width = 1;
+ memset (&ps, 0, sizeof (mbstate_t));
+ }
+ else if (MB_NULLWCH (tlen))
+ break;
+ else
+ {
+ w = wcwidth (wc);
+ width = (w >= 0) ? w : 1;
+ }
+ for (w = 0; w < tlen; ++w)
+ displayer->putch (displayer, s[w]);
+ s += tlen;
+ printed_len += width;
+#else
+ displayer->putch (displayer, *s);
+ s++;
+ printed_len++;
+#endif
+ }
+ }
+
+ return printed_len;
+}
+
+/* Output TO_PRINT to rl_outstream. If VISIBLE_STATS is defined and we
+ are using it, check for and output a single character for `special'
+ filenames. Return the number of characters we output.
+ Based on readline/complete.c:print_filename. */
+
+static int
+gdb_print_filename (char *to_print, char *full_pathname, int prefix_bytes,
+ const struct match_list_displayer *displayer)
+{
+ int printed_len, extension_char, slen, tlen;
+ char *s, c, *new_full_pathname;
+ const char *dn;
+ extern int _rl_complete_mark_directories;
+
+ extension_char = 0;
+ printed_len = gdb_fnprint (to_print, prefix_bytes, displayer);
+
+#if defined (VISIBLE_STATS)
+ if (rl_filename_completion_desired && (rl_visible_stats || _rl_complete_mark_directories))
+#else
+ if (rl_filename_completion_desired && _rl_complete_mark_directories)
+#endif
+ {
+ /* If to_print != full_pathname, to_print is the basename of the
+ path passed. In this case, we try to expand the directory
+ name before checking for the stat character. */
+ if (to_print != full_pathname)
+ {
+ /* Terminate the directory name. */
+ c = to_print[-1];
+ to_print[-1] = '\0';
+
+ /* If setting the last slash in full_pathname to a NUL results in
+ full_pathname being the empty string, we are trying to complete
+ files in the root directory. If we pass a null string to the
+ bash directory completion hook, for example, it will expand it
+ to the current directory. We just want the `/'. */
+ if (full_pathname == 0 || *full_pathname == 0)
+ dn = "/";
+ else if (full_pathname[0] != '/')
+ dn = full_pathname;
+ else if (full_pathname[1] == 0)
+ dn = "//"; /* restore trailing slash to `//' */
+ else if (full_pathname[1] == '/' && full_pathname[2] == 0)
+ dn = "/"; /* don't turn /// into // */
+ else
+ dn = full_pathname;
+ s = tilde_expand (dn);
+ if (rl_directory_completion_hook)
+ (*rl_directory_completion_hook) (&s);
+
+ slen = strlen (s);
+ tlen = strlen (to_print);
+ new_full_pathname = (char *)xmalloc (slen + tlen + 2);
+ strcpy (new_full_pathname, s);
+ if (s[slen - 1] == '/')
+ slen--;
+ else
+ new_full_pathname[slen] = '/';
+ new_full_pathname[slen] = '/';
+ strcpy (new_full_pathname + slen + 1, to_print);
+
+#if defined (VISIBLE_STATS)
+ if (rl_visible_stats)
+ extension_char = stat_char (new_full_pathname);
+ else
+#endif
+ if (gdb_path_isdir (new_full_pathname))
+ extension_char = '/';
+
+ xfree (new_full_pathname);
+ to_print[-1] = c;
+ }
+ else
+ {
+ s = tilde_expand (full_pathname);
+#if defined (VISIBLE_STATS)
+ if (rl_visible_stats)
+ extension_char = stat_char (s);
+ else
+#endif
+ if (gdb_path_isdir (s))
+ extension_char = '/';
+ }
+
+ xfree (s);
+ if (extension_char)
+ {
+ displayer->putch (displayer, extension_char);
+ printed_len++;
+ }
+ }
+
+ return printed_len;
+}
+
+/* GDB version of readline/complete.c:complete_get_screenwidth. */
+
+static int
+gdb_complete_get_screenwidth (const struct match_list_displayer *displayer)
+{
+ /* Readline has other stuff here which it's not clear we need. */
+ return displayer->width;
+}
+
+extern int _rl_completion_prefix_display_length;
+extern int _rl_print_completions_horizontally;
+
+EXTERN_C int _rl_qsort_string_compare (const void *, const void *);
+typedef int QSFUNC (const void *, const void *);
+
+/* GDB version of readline/complete.c:rl_display_match_list.
+ See gdb_display_match_list for a description of MATCHES, LEN, MAX.
+ Returns non-zero if all matches are displayed. */
+
+static int
+gdb_display_match_list_1 (char **matches, int len, int max,
+ const struct match_list_displayer *displayer)
+{
+ int count, limit, printed_len, lines, cols;
+ int i, j, k, l, common_length, sind;
+ char *temp, *t;
+ int page_completions = displayer->height != INT_MAX && pagination_enabled;
+
+ /* Find the length of the prefix common to all items: length as displayed
+ characters (common_length) and as a byte index into the matches (sind) */
+ common_length = sind = 0;
+ if (_rl_completion_prefix_display_length > 0)
+ {
+ t = gdb_printable_part (matches[0]);
+ temp = strrchr (t, '/');
+ common_length = temp ? gdb_fnwidth (temp) : gdb_fnwidth (t);
+ sind = temp ? strlen (temp) : strlen (t);
+
+ if (common_length > _rl_completion_prefix_display_length && common_length > ELLIPSIS_LEN)
+ max -= common_length - ELLIPSIS_LEN;
+ else
+ common_length = sind = 0;
+ }
+
+ /* How many items of MAX length can we fit in the screen window? */
+ cols = gdb_complete_get_screenwidth (displayer);
+ max += 2;
+ limit = cols / max;
+ if (limit != 1 && (limit * max == cols))
+ limit--;
+
+ /* If cols == 0, limit will end up -1 */
+ if (cols < displayer->width && limit < 0)
+ limit = 1;
+
+ /* Avoid a possible floating exception. If max > cols,
+ limit will be 0 and a divide-by-zero fault will result. */
+ if (limit == 0)
+ limit = 1;
+
+ /* How many iterations of the printing loop? */
+ count = (len + (limit - 1)) / limit;
+
+ /* Watch out for special case. If LEN is less than LIMIT, then
+ just do the inner printing loop.
+ 0 < len <= limit implies count = 1. */
+
+ /* Sort the items if they are not already sorted. */
+ if (rl_ignore_completion_duplicates == 0 && rl_sort_completion_matches)
+ qsort (matches + 1, len, sizeof (char *), (QSFUNC *)_rl_qsort_string_compare);
+
+ displayer->crlf (displayer);
+
+ lines = 0;
+ if (_rl_print_completions_horizontally == 0)
+ {
+ /* Print the sorted items, up-and-down alphabetically, like ls. */
+ for (i = 1; i <= count; i++)
+ {
+ for (j = 0, l = i; j < limit; j++)
+ {
+ if (l > len || matches[l] == 0)
+ break;
+ else
+ {
+ temp = gdb_printable_part (matches[l]);
+ printed_len = gdb_print_filename (temp, matches[l], sind,
+ displayer);
+
+ if (j + 1 < limit)
+ for (k = 0; k < max - printed_len; k++)
+ displayer->putch (displayer, ' ');
+ }
+ l += count;
+ }
+ displayer->crlf (displayer);
+ lines++;
+ if (page_completions && lines >= (displayer->height - 1) && i < count)
+ {
+ lines = gdb_display_match_list_pager (lines, displayer);
+ if (lines < 0)
+ return 0;
+ }
+ }
+ }
+ else
+ {
+ /* Print the sorted items, across alphabetically, like ls -x. */
+ for (i = 1; matches[i]; i++)
+ {
+ temp = gdb_printable_part (matches[i]);
+ printed_len = gdb_print_filename (temp, matches[i], sind, displayer);
+ /* Have we reached the end of this line? */
+ if (matches[i+1])
+ {
+ if (i && (limit > 1) && (i % limit) == 0)
+ {
+ displayer->crlf (displayer);
+ lines++;
+ if (page_completions && lines >= displayer->height - 1)
+ {
+ lines = gdb_display_match_list_pager (lines, displayer);
+ if (lines < 0)
+ return 0;
+ }
+ }
+ else
+ for (k = 0; k < max - printed_len; k++)
+ displayer->putch (displayer, ' ');
+ }
+ }
+ displayer->crlf (displayer);
+ }
+
+ return 1;
+}
+
+/* Utility for displaying completion list matches, used by both CLI and TUI.
+
+ MATCHES is the list of strings, in argv format, LEN is the number of
+ strings in MATCHES, and MAX is the length of the longest string in
+ MATCHES. */
+
+void
+gdb_display_match_list (char **matches, int len, int max,
+ const struct match_list_displayer *displayer)
+{
+ /* Readline will never call this if complete_line returned NULL. */
+ gdb_assert (max_completions != 0);
+
+ /* complete_line will never return more than this. */
+ if (max_completions > 0)
+ gdb_assert (len <= max_completions);
+
+ if (rl_completion_query_items > 0 && len >= rl_completion_query_items)
+ {
+ char msg[100];
+
+ /* We can't use *query here because they wait for <RET> which is
+ wrong here. This follows the readline version as closely as possible
+ for compatibility's sake. See readline/complete.c. */
+
+ displayer->crlf (displayer);
+
+ xsnprintf (msg, sizeof (msg),
+ "Display all %d possibilities? (y or n)", len);
+ displayer->puts (displayer, msg);
+ displayer->flush (displayer);
+
+ if (gdb_get_y_or_n (0, displayer) == 0)
+ {
+ displayer->crlf (displayer);
+ return;
+ }
+ }
+
+ if (gdb_display_match_list_1 (matches, len, max, displayer))
+ {
+ /* Note: MAX_COMPLETIONS may be -1 or zero, but LEN is always > 0. */
+ if (len == max_completions)
+ {
+ /* The maximum number of completions has been reached. Warn the user
+ that there may be more. */
+ const char *message = get_max_completions_reached_message ();
+
+ displayer->puts (displayer, message);
+ displayer->crlf (displayer);
+ }
+ }
+}
+
+void
+_initialize_completer (void)
+{
+ add_setshow_zuinteger_unlimited_cmd ("max-completions", no_class,
+ &max_completions, _("\
+Set maximum number of completion candidates."), _("\
+Show maximum number of completion candidates."), _("\
+Use this to limit the number of candidates considered\n\
+during completion. Specifying \"unlimited\" or -1\n\
+disables limiting. Note that setting either no limit or\n\
+a very large limit can make completion slow."),
+ NULL, NULL, &setlist, &showlist);
+}