#include "target.h"
#include "reggroups.h"
#include "user-regs.h"
+#include "arch-utils.h"
+#include "location.h"
#include "cli/cli-decode.h"
#include "completer.h"
+/* An enumeration of the various things a user might
+ attempt to complete for a location. */
+
+enum explicit_location_match_type
+{
+ /* The filename of a source file. */
+ MATCH_SOURCE,
+
+ /* The name of a function or method. */
+ MATCH_FUNCTION,
+
+ /* The name of a label. */
+ MATCH_LABEL
+};
+
/* Prototypes for local functions. */
static
char *line_completion_function (const char *text, int matches,
else if (word > text)
{
/* Return some portion of p. */
- q = xmalloc (strlen (p) + 5);
+ q = (char *) xmalloc (strlen (p) + 5);
strcpy (q, p + (word - text));
xfree (p);
}
else
{
/* Return some of TEXT plus p. */
- q = xmalloc (strlen (p) + (text - word) + 5);
+ q = (char *) xmalloc (strlen (p) + (text - word) + 5);
strncpy (q, word, text - word);
q[text - word] = '\0';
strcat (q, p);
return return_val;
}
-/* Complete on locations, which might be of two possible forms:
+/* Complete on linespecs, which might be of two possible forms:
file:line
or
This is intended to be used in commands that set breakpoints
etc. */
-VEC (char_ptr) *
-location_completer (struct cmd_list_element *ignore,
- const char *text, const char *word)
+static VEC (char_ptr) *
+linespec_location_completer (struct cmd_list_element *ignore,
+ const char *text, const char *word)
{
int n_syms, n_files, ix;
VEC (char_ptr) *fn_list = NULL;
return list;
}
+/* A helper function to collect explicit location matches for the given
+ LOCATION, which is attempting to match on WORD. */
+
+static VEC (char_ptr) *
+collect_explicit_location_matches (struct event_location *location,
+ enum explicit_location_match_type what,
+ const char *word)
+{
+ VEC (char_ptr) *matches = NULL;
+ const struct explicit_location *explicit_loc
+ = get_explicit_location (location);
+
+ switch (what)
+ {
+ case MATCH_SOURCE:
+ {
+ const char *text = (explicit_loc->source_filename == NULL
+ ? "" : explicit_loc->source_filename);
+
+ matches = make_source_files_completion_list (text, word);
+ }
+ break;
+
+ case MATCH_FUNCTION:
+ {
+ const char *text = (explicit_loc->function_name == NULL
+ ? "" : explicit_loc->function_name);
+
+ if (explicit_loc->source_filename != NULL)
+ {
+ const char *filename = explicit_loc->source_filename;
+
+ matches = make_file_symbol_completion_list (text, word, filename);
+ }
+ else
+ matches = make_symbol_completion_list (text, word);
+ }
+ break;
+
+ case MATCH_LABEL:
+ /* Not supported. */
+ break;
+
+ default:
+ gdb_assert_not_reached ("unhandled explicit_location_match_type");
+ }
+
+ return matches;
+}
+
+/* A convenience macro to (safely) back up P to the previous word. */
+
+static const char *
+backup_text_ptr (const char *p, const char *text)
+{
+ while (p > text && isspace (*p))
+ --p;
+ for (; p > text && !isspace (p[-1]); --p)
+ ;
+
+ return p;
+}
+
+/* A completer function for explicit locations. This function
+ completes both options ("-source", "-line", etc) and values. */
+
+static VEC (char_ptr) *
+explicit_location_completer (struct cmd_list_element *ignore,
+ struct event_location *location,
+ const char *text, const char *word)
+{
+ const char *p;
+ VEC (char_ptr) *matches = NULL;
+
+ /* Find the beginning of the word. This is necessary because
+ we need to know if we are completing an option name or value. We
+ don't get the leading '-' from the completer. */
+ p = backup_text_ptr (word, text);
+
+ if (*p == '-')
+ {
+ /* Completing on option name. */
+ static const char *const keywords[] =
+ {
+ "source",
+ "function",
+ "line",
+ "label",
+ NULL
+ };
+
+ /* Skip over the '-'. */
+ ++p;
+
+ return complete_on_enum (keywords, p, p);
+ }
+ else
+ {
+ /* Completing on value (or unknown). Get the previous word to see what
+ the user is completing on. */
+ size_t len, offset;
+ const char *new_word, *end;
+ enum explicit_location_match_type what;
+ struct explicit_location *explicit_loc
+ = get_explicit_location (location);
+
+ /* Backup P to the previous word, which should be the option
+ the user is attempting to complete. */
+ offset = word - p;
+ end = --p;
+ p = backup_text_ptr (p, text);
+ len = end - p;
+
+ if (strncmp (p, "-source", len) == 0)
+ {
+ what = MATCH_SOURCE;
+ new_word = explicit_loc->source_filename + offset;
+ }
+ else if (strncmp (p, "-function", len) == 0)
+ {
+ what = MATCH_FUNCTION;
+ new_word = explicit_loc->function_name + offset;
+ }
+ else if (strncmp (p, "-label", len) == 0)
+ {
+ what = MATCH_LABEL;
+ new_word = explicit_loc->label_name + offset;
+ }
+ else
+ {
+ /* The user isn't completing on any valid option name,
+ e.g., "break -source foo.c [tab]". */
+ return NULL;
+ }
+
+ /* If the user hasn't entered a search expression, e.g.,
+ "break -function <TAB><TAB>", new_word will be NULL, but
+ search routines require non-NULL search words. */
+ if (new_word == NULL)
+ new_word = "";
+
+ /* Now gather matches */
+ matches = collect_explicit_location_matches (location, what, new_word);
+ }
+
+ return matches;
+}
+
+/* A completer for locations. */
+
+VEC (char_ptr) *
+location_completer (struct cmd_list_element *ignore,
+ const char *text, const char *word)
+{
+ VEC (char_ptr) *matches = NULL;
+ const char *copy = text;
+ struct event_location *location;
+
+ location = string_to_explicit_location (©, current_language, 1);
+ if (location != NULL)
+ {
+ struct cleanup *cleanup;
+
+ cleanup = make_cleanup_delete_event_location (location);
+ matches = explicit_location_completer (ignore, location, text, word);
+ do_cleanups (cleanup);
+ }
+ else
+ {
+ /* This is an address or linespec location.
+ Right now both of these are handled by the (old) linespec
+ completer. */
+ matches = linespec_location_completer (ignore, text, word);
+ }
+
+ return matches;
+}
+
/* Helper for expression_completer which recursively adds field and
method names from TYPE, a struct or union type, to the array
OUTPUT. */
int computed_type_name = 0;
const char *type_name = NULL;
- CHECK_TYPEDEF (type);
+ type = check_typedef (type);
for (i = 0; i < TYPE_NFIELDS (type); ++i)
{
if (i < TYPE_N_BASECLASSES (type))
struct type *type = NULL;
char *fieldname;
const char *p;
- volatile struct gdb_exception except;
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_CATCH (except, RETURN_MASK_ERROR)
+ TRY
{
type = parse_expression_for_completion (text, &fieldname, &code);
}
- if (except.reason < 0)
- return NULL;
+ CATCH (except, RETURN_MASK_ERROR)
+ {
+ return NULL;
+ }
+ END_CATCH
+
if (fieldname && type)
{
for (;;)
{
- CHECK_TYPEDEF (type);
+ type = check_typedef (type);
if (TYPE_CODE (type) != TYPE_CODE_PTR
&& TYPE_CODE (type) != TYPE_CODE_REF)
break;
rl_completer_word_break_characters =
gdb_completer_file_name_break_characters;
}
- else if (c->completer == location_completer)
- {
- /* Commands which complete on locations want to
- see the entire argument. */
- for (p = word;
- p > tmp_command
- && p[-1] != ' ' && p[-1] != '\t';
- p--)
- ;
- }
if (reason == handle_brkchars
&& c->completer_handle_brkchars != NULL)
(*c->completer_handle_brkchars) (c, p, word);
rl_completer_word_break_characters =
gdb_completer_file_name_break_characters;
}
- else if (c->completer == location_completer)
- {
- for (p = word;
- p > tmp_command
- && p[-1] != ' ' && p[-1] != '\t';
- p--)
- ;
- }
if (reason == handle_brkchars
&& c->completer_handle_brkchars != NULL)
(*c->completer_handle_brkchars) (c, p, word);
return list;
}
-/* Generate completions all at once. Returns a vector of strings.
- Each element is allocated with xmalloc. It can also return NULL if
- there are no completions.
+
+/* See completer.h. */
+
+int max_completions = 200;
+
+/* See completer.h. */
+
+completion_tracker_t
+new_completion_tracker (void)
+{
+ if (max_completions <= 0)
+ return NULL;
+
+ return htab_create_alloc (max_completions,
+ htab_hash_string, (htab_eq) streq,
+ NULL, xcalloc, xfree);
+}
+
+/* Cleanup routine to free a completion tracker and reset the pointer
+ to NULL. */
+
+static void
+free_completion_tracker (void *p)
+{
+ completion_tracker_t *tracker_ptr = (completion_tracker_t *) p;
+
+ htab_delete (*tracker_ptr);
+ *tracker_ptr = NULL;
+}
+
+/* See completer.h. */
+
+struct cleanup *
+make_cleanup_free_completion_tracker (completion_tracker_t *tracker_ptr)
+{
+ if (*tracker_ptr == NULL)
+ return make_cleanup (null_cleanup, NULL);
+
+ return make_cleanup (free_completion_tracker, tracker_ptr);
+}
+
+/* See completer.h. */
+
+enum maybe_add_completion_enum
+maybe_add_completion (completion_tracker_t tracker, char *name)
+{
+ void **slot;
+
+ if (max_completions < 0)
+ return MAYBE_ADD_COMPLETION_OK;
+ if (max_completions == 0)
+ return MAYBE_ADD_COMPLETION_MAX_REACHED;
+
+ gdb_assert (tracker != NULL);
+
+ if (htab_elements (tracker) >= max_completions)
+ return MAYBE_ADD_COMPLETION_MAX_REACHED;
+
+ slot = htab_find_slot (tracker, name, INSERT);
+
+ if (*slot != HTAB_EMPTY_ENTRY)
+ return MAYBE_ADD_COMPLETION_DUPLICATE;
+
+ *slot = name;
+
+ return (htab_elements (tracker) < max_completions
+ ? MAYBE_ADD_COMPLETION_OK
+ : MAYBE_ADD_COMPLETION_OK_MAX_REACHED);
+}
+
+void
+throw_max_completions_reached_error (void)
+{
+ throw_error (MAX_COMPLETIONS_REACHED_ERROR, _("Max completions reached."));
+}
+
+/* Generate completions all at once. Returns a vector of unique strings
+ allocated with xmalloc. Returns NULL if there are no completions
+ or if max_completions is 0. If max_completions is non-negative, this will
+ return at most max_completions strings.
TEXT is the caller's idea of the "word" we are looking at.
VEC (char_ptr) *
complete_line (const char *text, const char *line_buffer, int point)
{
- return complete_line_internal (text, line_buffer,
- point, handle_completions);
+ VEC (char_ptr) *list;
+ VEC (char_ptr) *result = NULL;
+ struct cleanup *cleanups;
+ completion_tracker_t tracker;
+ char *candidate;
+ int ix, max_reached;
+
+ if (max_completions == 0)
+ return NULL;
+ list = complete_line_internal (text, line_buffer, point,
+ handle_completions);
+ if (max_completions < 0)
+ return list;
+
+ tracker = new_completion_tracker ();
+ cleanups = make_cleanup_free_completion_tracker (&tracker);
+ make_cleanup_free_char_ptr_vec (list);
+
+ /* Do a final test for too many completions. Individual completers may
+ do some of this, but are not required to. Duplicates are also removed
+ here. Otherwise the user is left scratching his/her head: readline and
+ complete_command will remove duplicates, and if removal of duplicates
+ there brings the total under max_completions the user may think gdb quit
+ searching too early. */
+
+ for (ix = 0, max_reached = 0;
+ !max_reached && VEC_iterate (char_ptr, list, ix, candidate);
+ ++ix)
+ {
+ enum maybe_add_completion_enum add_status;
+
+ add_status = maybe_add_completion (tracker, candidate);
+
+ switch (add_status)
+ {
+ case MAYBE_ADD_COMPLETION_OK:
+ VEC_safe_push (char_ptr, result, xstrdup (candidate));
+ break;
+ case MAYBE_ADD_COMPLETION_OK_MAX_REACHED:
+ VEC_safe_push (char_ptr, result, xstrdup (candidate));
+ max_reached = 1;
+ break;
+ case MAYBE_ADD_COMPLETION_MAX_REACHED:
+ gdb_assert_not_reached ("more than max completions reached");
+ case MAYBE_ADD_COMPLETION_DUPLICATE:
+ break;
+ }
+ }
+
+ do_cleanups (cleanups);
+
+ return result;
}
/* Complete on command names. Used by "help". */
{
VEC (char_ptr) *return_val = NULL;
size_t len = strlen (word);
- enum gdb_signal signum;
+ int signum;
const char *signame;
for (signum = GDB_SIGNAL_FIRST; signum != GDB_SIGNAL_LAST; ++signum)
if (signum == GDB_SIGNAL_0)
continue;
- signame = gdb_signal_to_name (signum);
+ signame = gdb_signal_to_name ((enum gdb_signal) signum);
/* Ignore the unknown signal case. */
if (!signame || strcmp (signame, "?") == 0)
return return_val;
}
-/* Complete on a register or reggroup. */
+/* Bit-flags for selecting what the register and/or register-group
+ completer should complete on. */
-VEC (char_ptr) *
-reg_or_group_completer (struct cmd_list_element *ignore,
- const char *text, const char *word)
+enum reg_completer_targets
+ {
+ complete_register_names = 0x1,
+ complete_reggroup_names = 0x2
+ };
+
+/* Complete register names and/or reggroup names based on the value passed
+ in TARGETS. At least one bit in TARGETS must be set. */
+
+static VEC (char_ptr) *
+reg_or_group_completer_1 (struct cmd_list_element *ignore,
+ const char *text, const char *word,
+ enum reg_completer_targets targets)
{
VEC (char_ptr) *result = NULL;
size_t len = strlen (word);
struct gdbarch *gdbarch;
- struct reggroup *group;
const char *name;
- int i;
-
- if (!target_has_registers)
- return result;
- gdbarch = get_frame_arch (get_selected_frame (NULL));
+ gdb_assert ((targets & (complete_register_names
+ | complete_reggroup_names)) != 0);
+ gdbarch = get_current_arch ();
- for (i = 0;
- (name = user_reg_map_regnum_to_name (gdbarch, i)) != NULL;
- i++)
+ if ((targets & complete_register_names) != 0)
{
- if (*name != '\0' && strncmp (word, name, len) == 0)
- VEC_safe_push (char_ptr, result, xstrdup (name));
+ int i;
+
+ for (i = 0;
+ (name = user_reg_map_regnum_to_name (gdbarch, i)) != NULL;
+ i++)
+ {
+ if (*name != '\0' && strncmp (word, name, len) == 0)
+ VEC_safe_push (char_ptr, result, xstrdup (name));
+ }
}
- for (group = reggroup_next (gdbarch, NULL);
- group != NULL;
- group = reggroup_next (gdbarch, group))
+ if ((targets & complete_reggroup_names) != 0)
{
- name = reggroup_name (group);
- if (strncmp (word, name, len) == 0)
- VEC_safe_push (char_ptr, result, xstrdup (name));
+ 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)
+ VEC_safe_push (char_ptr, result, xstrdup (name));
+ }
}
return result;
}
+/* Perform completion on register and reggroup names. */
+
+VEC (char_ptr) *
+reg_or_group_completer (struct cmd_list_element *ignore,
+ const char *text, const char *word)
+{
+ return reg_or_group_completer_1 (ignore, text, word,
+ (complete_register_names
+ | complete_reggroup_names));
+}
+
+/* Perform completion on reggroup names. */
+
+VEC (char_ptr) *
+reggroup_completer (struct cmd_list_element *ignore,
+ const char *text, const char *word)
+{
+ return reg_or_group_completer_1 (ignore, text, word,
+ complete_reggroup_names);
+}
/* Get the list of chars that are considered as word breaks
for the current command. */
{
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).
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. */
+ See gdb_display_match_list for a description of MATCHES, LEN, MAX.
+ Returns non-zero if all matches are displayed. */
-static void
+static int
gdb_display_match_list_1 (char **matches, int len, int max,
const struct match_list_displayer *displayer)
{
int i, j, k, l, common_length, sind;
char *temp, *t;
int page_completions = displayer->height != INT_MAX && pagination_enabled;
- extern int _rl_completion_prefix_display_length;
- extern int _rl_qsort_string_compare (const void *, const void *);
- extern int _rl_print_completions_horizontally;
- typedef int QSFUNC (const void *, const void *);
/* 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) */
{
lines = gdb_display_match_list_pager (lines, displayer);
if (lines < 0)
- return;
+ return 0;
}
}
}
{
lines = gdb_display_match_list_pager (lines, displayer);
if (lines < 0)
- return;
+ return 0;
}
}
else
}
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.
-
- This function handles the LIST_MAYBE_TRUNCATED marker that we add to the
- completion list.
-
- Note: While LIST_MAYBE_TRUNCATED contributes to MAX, it's not long enough
- that we worry about it. */
+ 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];
}
}
- gdb_display_match_list_1 (matches, len, max, displayer);
+ 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);
+ }
+ }
+}
+\f
+extern initialize_file_ftype _initialize_completer; /* -Wmissing-prototypes */
+
+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);
}