From cf00cd6faf31c208bbfe107140c26895412214bb Mon Sep 17 00:00:00 2001 From: Philippe Waroquiers Date: Wed, 19 Jun 2019 12:49:55 +0200 Subject: default-args: allow to define default arguments for aliases Currently, a user can define an alias, but cannot have default arguments for this alias. This patch modifies the 'alias' command so that default args can be provided. (gdb) h alias Define a new command that is an alias of an existing command. Usage: alias [-a] [--] ALIAS = COMMAND [DEFAULT-ARGS...] ALIAS is the name of the alias command to create. COMMAND is the command being aliased to. Options: -a Specify that ALIAS is an abbreviation of COMMAND. Abbreviations are not used in command completion.. GDB will automatically prepend the provided DEFAULT-ARGS to the list of arguments explicitly provided when using ALIAS. Use "help aliases" to list all user defined aliases and their default args. Examples: Make "spe" an alias of "set print elements": alias spe set print elements Make "elms" an alias of "elements" in the "set print" command: alias -a set print elms set print elements Make "btf" an alias of "backtrace -full -past-entry -past-main" : alias btf = backtrace -full -past-entry -past-main Make "wLapPeu" an alias of 2 nested "with": alias wLapPeu = with language pascal -- with print elements unlimited -- (gdb) The way 'default-args' is implemented makes it trivial to set default args also for GDB commands (such as "backtrace") and for GDB pre-defined aliases (such as "bt"). It was however deemed better to not allow to define default arguments for pre-defined commands and aliases, to avoid users believing that e.g. default args for "backtrace" would apply to "bt". If needed, default-args could be allowed for GDB predefined commands and aliases by adding a command 'set default-args GDB_COMMAND_OR_PREDEFINED_ALIAS [DEFAULT-ARGS...]'. * 'alias' command now has a completer that helps to complete: - ALIAS (if the user defines an alias after a prefix), - the aliased COMMAND - the possible options for the aliased COMMAND. * Help and apropos commands show the definitions of the aliases that have default arguments, e.g. (gdb) help backtrace backtrace, btf, where, bt alias btf = backtrace -full -past-entry -past-main Print backtrace of all stack frames, or innermost COUNT frames. Usage: backtrace [OPTION]... [QUALIFIER]... [COUNT | -COUNT] Options: -entry-values no|only|preferred|if-needed|both|compact|default Set printing of function arguments at function entry. ... gdb/ChangeLog 2020-06-22 Philippe Waroquiers * cli/cli-cmds.c (lookup_cmd_for_default_args) (alias_command_completer) (make_alias_options_def_group): New functions. (alias_opts, alias_option_defs): New struct and array. (alias_usage_error): Update usage. (alias_command): Handles optional DEFAULT-ARGS... arguments. Use option framework. (_initialize_cli_cmds): Update alias command help. Update aliases command help. (show_user): Add NULL for new default_args lookup_cmd argument. (valid_command_p): Rename to validate_aliased_command. Add NULL for new default_args lookup_cmd argument. Verify that the aliased_command has no default args. * cli/cli-decode.c (help_cmd): Show aliases definitions. (lookup_cmd_1, lookup_cmd): New argument default_args. (add_alias_cmd): Add NULL for new default_args lookup_cmd argument. (print_help_for_command): Show default args under the layout alias some_alias = some_aliased_cmd some_alias_default_arg. * cli/cli-decode.h (struct cmd_list_element): New member default_args. xfree default_args in destructor. * cli/cli-script.c (process_next_line, do_define_command): Add NULL for new default_args lookup_cmd argument. * command.h: Declare new default_args argument in lookup_cmd and lookup_cmd_1. * completer.c (complete_line_internal_1): Add NULL for new default_args lookup_cmd or lookup_cmd_1 argument. * guile/scm-cmd.c (gdbscm_parse_command_name): Likewise. * guile/scm-param.c (add_setshow_generic, pascm_parameter_defined_p): Likewise. * infcmd.c (_initialize_infcmd): Likewise. * python/py-auto-load.c (gdbpy_initialize_auto_load): Likewise. * python/py-cmd.c (gdbpy_parse_command_name): Likewise. * python/py-param.c (add_setshow_generic): Likewise. * remote.c (_initialize_remote): Likewise. * top.c (execute_command): Prepend default_args if command has some. (set_verbose): Add NULL for new default_args lookup_cmd or lookup_cmd_1 argument. * tracepoint.c (validate_actionline, encode_actions_1): Add NULL for new default_args lookup_cmd or lookup_cmd_1 argument. --- gdb/ChangeLog | 44 +++++++++ gdb/cli/cli-cmds.c | 247 ++++++++++++++++++++++++++++++++++++---------- gdb/cli/cli-decode.c | 110 +++++++++++++++++---- gdb/cli/cli-decode.h | 4 + gdb/cli/cli-script.c | 12 +-- gdb/command.h | 2 + gdb/completer.c | 2 +- gdb/guile/scm-cmd.c | 2 +- gdb/guile/scm-param.c | 6 +- gdb/infcmd.c | 6 +- gdb/python/py-auto-load.c | 4 +- gdb/python/py-cmd.c | 2 +- gdb/python/py-param.c | 4 +- gdb/remote.c | 4 +- gdb/top.c | 21 +++- gdb/tracepoint.c | 6 +- 16 files changed, 380 insertions(+), 96 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 022b797d47b..675a9e4ce82 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,47 @@ +2020-06-22 Philippe Waroquiers + + * cli/cli-cmds.c (lookup_cmd_for_default_args) + (alias_command_completer) + (make_alias_options_def_group): New functions. + (alias_opts, alias_option_defs): New struct and array. + (alias_usage_error): Update usage. + (alias_command): Handles optional DEFAULT-ARGS... arguments. + Use option framework. + (_initialize_cli_cmds): Update alias command help. + Update aliases command help. + (show_user): + Add NULL for new default_args lookup_cmd argument. + (valid_command_p): Rename to validate_aliased_command. + Add NULL for new default_args lookup_cmd argument. Verify that the + aliased_command has no default args. + * cli/cli-decode.c (help_cmd): Show aliases definitions. + (lookup_cmd_1, lookup_cmd): New argument default_args. + (add_alias_cmd): + Add NULL for new default_args lookup_cmd argument. + (print_help_for_command): Show default args under the layout + alias some_alias = some_aliased_cmd some_alias_default_arg. + * cli/cli-decode.h (struct cmd_list_element): New member default_args. + xfree default_args in destructor. + * cli/cli-script.c (process_next_line, do_define_command): + Add NULL for new default_args lookup_cmd argument. + * command.h: Declare new default_args argument in lookup_cmd + and lookup_cmd_1. + * completer.c (complete_line_internal_1): + Add NULL for new default_args lookup_cmd or lookup_cmd_1 argument. + * guile/scm-cmd.c (gdbscm_parse_command_name): Likewise. + * guile/scm-param.c (add_setshow_generic, pascm_parameter_defined_p): + Likewise. + * infcmd.c (_initialize_infcmd): Likewise. + * python/py-auto-load.c (gdbpy_initialize_auto_load): Likewise. + * python/py-cmd.c (gdbpy_parse_command_name): Likewise. + * python/py-param.c (add_setshow_generic): Likewise. + * remote.c (_initialize_remote): Likewise. + * top.c (execute_command): Prepend default_args if command has some. + (set_verbose): + Add NULL for new default_args lookup_cmd or lookup_cmd_1 argument. + * tracepoint.c (validate_actionline, encode_actions_1): + Add NULL for new default_args lookup_cmd or lookup_cmd_1 argument. + 2020-06-22 Tankut Baris Aktemur * jit.c (jit_read_descriptor): Use bool as the return type. diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c index cd6f7856599..2ff515ace7d 100644 --- a/gdb/cli/cli-cmds.c +++ b/gdb/cli/cli-cmds.c @@ -50,6 +50,7 @@ #include "cli/cli-cmds.h" #include "cli/cli-style.h" #include "cli/cli-utils.h" +#include "cli/cli-style.h" #include "extension.h" #include "gdbsupport/pathstuff.h" @@ -221,6 +222,7 @@ with_command_1 (const char *set_cmd_prefix, nested_cmd = repeat_previous (); cmd_list_element *set_cmd = lookup_cmd (&args, setlist, set_cmd_prefix, + nullptr, /*allow_unknown=*/ 0, /*ignore_help_classes=*/ 1); gdb_assert (set_cmd != nullptr); @@ -315,7 +317,54 @@ with_command_completer (struct cmd_list_element *ignore, with_command_completer_1 ("set ", tracker, text); } - +/* Look up the contents of TEXT as a command usable with default args. + Throws an error if no such command is found. + Return the found command and advances TEXT past the found command. + If the found command is a postfix command, set *PREFIX_CMD to its + prefix command. */ + +static struct cmd_list_element * +lookup_cmd_for_default_args (const char **text, + struct cmd_list_element **prefix_cmd) +{ + const char *orig_text = *text; + struct cmd_list_element *lcmd; + + if (*text == nullptr || skip_spaces (*text) == nullptr) + error (_("ALIAS missing.")); + + /* We first use lookup_cmd to verify TEXT unambiguously identifies + a command. */ + lcmd = lookup_cmd (text, cmdlist, "", NULL, + /*allow_unknown=*/ 0, + /*ignore_help_classes=*/ 1); + + /* Note that we accept default args for prefix commands, + as a prefix command can also be a valid usable + command accepting some arguments. + For example, "thread apply" applies a command to a + list of thread ids, and is also the prefix command for + thread apply all. */ + + /* We have an unambiguous command for which default args + can be specified. What remains after having found LCMD + is either spaces, or the default args character. */ + + /* We then use lookup_cmd_composition to detect if the user + has specified an alias, and find the possible prefix_cmd + of cmd. */ + struct cmd_list_element *alias, *cmd; + lookup_cmd_composition + (std::string (orig_text, *text - orig_text).c_str (), + &alias, prefix_cmd, &cmd); + gdb_assert (cmd != nullptr); + gdb_assert (cmd == lcmd); + if (alias != nullptr) + cmd = alias; + + return cmd; +} + /* Provide documentation on command or list given by COMMAND. FROM_TTY is ignored. */ @@ -1541,7 +1590,7 @@ show_user (const char *args, int from_tty) { const char *comname = args; - c = lookup_cmd (&comname, cmdlist, "", 0, 1); + c = lookup_cmd (&comname, cmdlist, "", NULL, 0, 1); if (!cli_user_command_p (c)) error (_("Not a user command.")); show_user_1 (c, "", args, gdb_stdout); @@ -1573,6 +1622,71 @@ apropos_command (const char *arg, int from_tty) apropos_cmd (gdb_stdout, cmdlist, verbose, pattern, ""); } +/* The options for the "alias" command. */ + +struct alias_opts +{ + /* For "-a". */ + bool abbrev_flag = false; +}; + +static const gdb::option::option_def alias_option_defs[] = { + + gdb::option::flag_option_def { + "a", + [] (alias_opts *opts) { return &opts->abbrev_flag; }, + N_("Specify that ALIAS is an abbreviation of COMMAND.\n\ +Abbreviations are not used in command completion."), + }, + +}; + +/* Create an option_def_group for the "alias" options, with + A_OPTS as context. */ + +static gdb::option::option_def_group +make_alias_options_def_group (alias_opts *a_opts) +{ + return {{alias_option_defs}, a_opts}; +} + +/* Completer for the "alias_command". */ + +static void +alias_command_completer (struct cmd_list_element *ignore, + completion_tracker &tracker, + const char *text, const char *word) +{ + const auto grp = make_alias_options_def_group (nullptr); + + tracker.set_use_custom_word_point (true); + + if (gdb::option::complete_options + (tracker, &text, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_ERROR, grp)) + return; + + const char *delim = strchr (text, '='); + + /* If we're past the "=" delimiter, complete the + "alias ALIAS = COMMAND [DEFAULT-ARGS...]" as if the user is + typing COMMAND DEFAULT-ARGS... */ + if (delim != text + && delim != nullptr + && isspace (delim[-1]) + && (isspace (delim[1]) || delim[1] == '\0')) + { + std::string new_text = std::string (delim + 1); + + tracker.advance_custom_word_point_by (delim + 1 - text); + complete_nested_command_line (tracker, new_text.c_str ()); + return; + } + + /* We're not yet past the "=" delimiter. Complete a command, as + the user might type an alias following a prefix command. */ + complete_nested_command_line (tracker, text); +} + /* Subroutine of alias_command to simplify it. Return the first N elements of ARGV flattened back to a string with a space separating each element. @@ -1600,24 +1714,29 @@ argv_to_string (char **argv, int n) } /* Subroutine of alias_command to simplify it. - Return true if COMMAND exists, unambiguously. Otherwise false. */ + Verifies that COMMAND can have an alias: + COMMAND must exist. + COMMAND must not have default args. + This last condition is to avoid the following: + alias aaa = backtrace -full + alias bbb = aaa -past-main + as (at least currently), alias default args are not cumulative + and the user would expect bbb to execute 'backtrace -full -past-main' + while it will execute 'backtrace -past-main'. */ -static bool -valid_command_p (const char *command) +static void +validate_aliased_command (const char *command) { struct cmd_list_element *c; + std::string default_args; - c = lookup_cmd_1 (& command, cmdlist, NULL, 1); + c = lookup_cmd_1 (& command, cmdlist, NULL, &default_args, 1); if (c == NULL || c == (struct cmd_list_element *) -1) - return false; - - /* This is the slightly tricky part. - lookup_cmd_1 will return a pointer to the last part of COMMAND - to match, leaving COMMAND pointing at the remainder. */ - while (*command == ' ' || *command == '\t') - ++command; - return *command == '\0'; + error (_("Invalid command to alias to: %s"), command); + + if (!default_args.empty ()) + error (_("Cannot define an alias of an alias that has default args")); } /* Called when "alias" was incorrectly used. */ @@ -1625,7 +1744,7 @@ valid_command_p (const char *command) static void alias_usage_error (void) { - error (_("Usage: alias [-a] [--] ALIAS = COMMAND")); + error (_("Usage: alias [-a] [--] ALIAS = COMMAND [DEFAULT-ARGS...]")); } /* Make an alias of an existing command. */ @@ -1633,8 +1752,13 @@ alias_usage_error (void) static void alias_command (const char *args, int from_tty) { + alias_opts a_opts; + + auto grp = make_alias_options_def_group (&a_opts); + gdb::option::process_options + (&args, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_ERROR, grp); + int i, alias_argc, command_argc; - int abbrev_flag = 0; const char *equals; const char *alias, *command; @@ -1645,24 +1769,18 @@ alias_command (const char *args, int from_tty) std::string args2 (args, equals - args); gdb_argv built_alias_argv (args2.c_str ()); - gdb_argv command_argv (equals + 1); + + const char *default_args = equals + 1; + struct cmd_list_element *c_command_prefix; + + lookup_cmd_for_default_args (&default_args, &c_command_prefix); + std::string command_argv_str (equals + 1, + default_args == nullptr + ? strlen (equals + 1) + : default_args - equals - 1); + gdb_argv command_argv (command_argv_str.c_str ()); char **alias_argv = built_alias_argv.get (); - while (alias_argv[0] != NULL) - { - if (strcmp (alias_argv[0], "-a") == 0) - { - ++alias_argv; - abbrev_flag = 1; - } - else if (strcmp (alias_argv[0], "--") == 0) - { - ++alias_argv; - break; - } - else - break; - } if (alias_argv[0] == NULL || command_argv[0] == NULL || *alias_argv[0] == '\0' || *command_argv[0] == '\0') @@ -1682,14 +1800,13 @@ alias_command (const char *args, int from_tty) alias_argc = countargv (alias_argv); command_argc = command_argv.count (); - /* COMMAND must exist. + /* COMMAND must exist, and cannot have default args. Reconstruct the command to remove any extraneous spaces, for better error messages. */ std::string command_string (argv_to_string (command_argv.get (), command_argc)); command = command_string.c_str (); - if (! valid_command_p (command)) - error (_("Invalid command to alias to: %s"), command); + validate_aliased_command (command); /* ALIAS must not exist. */ std::string alias_string (argv_to_string (alias_argv, alias_argc)); @@ -1718,6 +1835,8 @@ alias_command (const char *args, int from_tty) } + struct cmd_list_element *alias_cmd; + /* If ALIAS is one word, it is an alias for the entire COMMAND. Example: alias spe = set print elements @@ -1730,8 +1849,8 @@ alias_command (const char *args, int from_tty) if (alias_argc == 1) { /* add_cmd requires *we* allocate space for name, hence the xstrdup. */ - add_com_alias (xstrdup (alias_argv[0]), command, class_alias, - abbrev_flag); + alias_cmd = add_com_alias (xstrdup (alias_argv[0]), command, class_alias, + a_opts.abbrev_flag); } else { @@ -1751,19 +1870,29 @@ alias_command (const char *args, int from_tty) alias_prefix = alias_prefix_string.c_str (); command_prefix = command_prefix_string.c_str (); - c_command = lookup_cmd_1 (& command_prefix, cmdlist, NULL, 1); + c_command = lookup_cmd_1 (& command_prefix, cmdlist, NULL, NULL, 1); /* We've already tried to look up COMMAND. */ gdb_assert (c_command != NULL && c_command != (struct cmd_list_element *) -1); gdb_assert (c_command->prefixlist != NULL); - c_alias = lookup_cmd_1 (& alias_prefix, cmdlist, NULL, 1); + c_alias = lookup_cmd_1 (& alias_prefix, cmdlist, NULL, NULL, 1); if (c_alias != c_command) error (_("ALIAS and COMMAND prefixes do not match.")); /* add_cmd requires *we* allocate space for name, hence the xstrdup. */ - add_alias_cmd (xstrdup (alias_argv[alias_argc - 1]), - command_argv[command_argc - 1], - class_alias, abbrev_flag, c_command->prefixlist); + alias_cmd = add_alias_cmd (xstrdup (alias_argv[alias_argc - 1]), + command_argv[command_argc - 1], + class_alias, a_opts.abbrev_flag, + c_command->prefixlist); + } + + gdb_assert (alias_cmd != nullptr); + gdb_assert (alias_cmd->default_args.empty ()); + if (default_args != nullptr) + { + default_args = skip_spaces (default_args); + + alias_cmd->default_args = default_args; } } @@ -1938,7 +2067,7 @@ setting_cmd (const char *fnname, struct cmd_list_element *showlist, error (_("First argument of %s must be a string."), fnname); const char *a0 = (const char *) value_contents (argv[0]); - cmd_list_element *cmd = lookup_cmd (&a0, showlist, "", -1, 0); + cmd_list_element *cmd = lookup_cmd (&a0, showlist, "", NULL, -1, 0); if (cmd == nullptr || cmd_type (cmd) != show_cmd) error (_("First argument of %s must be a " @@ -2128,7 +2257,7 @@ well documented as user commands."), &cmdlist); add_cmd ("obscure", class_obscure, _("Obscure features."), &cmdlist); add_cmd ("aliases", class_alias, - _("Aliases of other commands."), &cmdlist); + _("User-defined aliases of other commands."), &cmdlist); add_cmd ("user-defined", class_user, _("\ User-defined commands.\n\ The commands in this class are those defined by the user.\n\ @@ -2454,19 +2583,37 @@ When 'on', each command is displayed as it is executed."), NULL, &setlist, &showlist); - c = add_com ("alias", class_support, alias_command, _("\ + const auto alias_opts = make_alias_options_def_group (nullptr); + + static std::string alias_help + = gdb::option::build_help (_("\ Define a new command that is an alias of an existing command.\n\ -Usage: alias [-a] [--] ALIAS = COMMAND\n\ +Usage: alias [-a] [--] ALIAS = COMMAND [DEFAULT-ARGS...]\n\ ALIAS is the name of the alias command to create.\n\ COMMAND is the command being aliased to.\n\ -If \"-a\" is specified, the command is an abbreviation,\n\ -and will not be used in command completion.\n\ +\n\ +Options:\n\ +%OPTIONS%\n\ +\n\ +GDB will automatically prepend the provided DEFAULT-ARGS to the list\n\ +of arguments explicitly provided when using ALIAS.\n\ +Use \"help aliases\" to list all user defined aliases and their default args.\n\ \n\ Examples:\n\ Make \"spe\" an alias of \"set print elements\":\n\ - alias spe = set print elements\n\ + alias spe set print elements\n\ Make \"elms\" an alias of \"elements\" in the \"set print\" command:\n\ - alias -a set print elms = set print elements")); + alias -a set print elms set print elements\n\ +Make \"btf\" an alias of \"backtrace -full -past-entry -past-main\" :\n\ + alias btf = backtrace -full -past-entry -past-main\n\ +Make \"wLapPeu\" an alias of 2 nested \"with\":\n\ + alias wLapPeu = with language pascal -- with print elements unlimited --"), + alias_opts); + + c = add_com ("alias", class_support, alias_command, + alias_help.c_str ()); + + set_cmd_completer_handle_brkchars (c, alias_command_completer); const char *source_help_text = xstrprintf (_("\ Read commands from a file named FILE.\n\ diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c index a4ef93c62b0..85f50aa8e48 100644 --- a/gdb/cli/cli-decode.c +++ b/gdb/cli/cli-decode.c @@ -337,7 +337,7 @@ add_alias_cmd (const char *name, const char *oldname, struct cmd_list_element *old; tmp = oldname; - old = lookup_cmd (&tmp, *list, "", 1, 1); + old = lookup_cmd (&tmp, *list, "", NULL, 1, 1); return add_alias_cmd (name, old, theclass, abbrev_flag, list); } @@ -1040,6 +1040,40 @@ fput_command_name_styled (struct cmd_list_element *c, struct ui_file *stream) fprintf_styled (stream, title_style.style (), "%s%s", prefixname, c->name); } +/* Print the definition of alias C using title style for alias + and aliased command. */ + +static void +fput_alias_definition_styled (struct cmd_list_element *c, + struct ui_file *stream) +{ + gdb_assert (c->cmd_pointer != nullptr); + fputs_filtered (" alias ", stream); + fput_command_name_styled (c, stream); + fprintf_filtered (stream, " = "); + fput_command_name_styled (c->cmd_pointer, stream); + fprintf_filtered (stream, " %s\n", c->default_args.c_str ()); +} + +/* Print the definition of the aliases of CMD that have default args. */ + +static void +fput_aliases_definition_styled (struct cmd_list_element *cmd, + struct ui_file *stream) +{ + if (cmd->aliases != nullptr) + { + for (cmd_list_element *iter = cmd->aliases; + iter; + iter = iter->alias_chain) + { + if (!iter->default_args.empty ()) + fput_alias_definition_styled (iter, stream); + } + } +} + + /* If C has one or more aliases, style print the name of C and the name of its aliases, separated by commas. If ALWAYS_FPUT_C_NAME, print the name of C even if it has no aliases. @@ -1081,12 +1115,21 @@ print_doc_of_command (struct cmd_list_element *c, const char *prefix, if (verbose) fputs_filtered ("\n", stream); - fput_command_names_styled (c, true, " -- ", stream); + fput_command_names_styled (c, true, + verbose ? "" : " -- ", stream); if (verbose) - fputs_highlighted (c->doc, highlight, stream); + { + fputs_filtered ("\n", stream); + fput_aliases_definition_styled (c, stream); + fputs_highlighted (c->doc, highlight, stream); + fputs_filtered ("\n", stream); + } else - print_doc_line (stream, c->doc, false); - fputs_filtered ("\n", stream); + { + print_doc_line (stream, c->doc, false); + fputs_filtered ("\n", stream); + fput_aliases_definition_styled (c, stream); + } } /* Recursively walk the commandlist structures, and print out the @@ -1183,7 +1226,7 @@ help_cmd (const char *command, struct ui_file *stream) } const char *orig_command = command; - c = lookup_cmd (&command, cmdlist, "", 0, 0); + c = lookup_cmd (&command, cmdlist, "", NULL, 0, 0); if (c == 0) return; @@ -1205,6 +1248,7 @@ help_cmd (const char *command, struct ui_file *stream) /* If the user asked 'help somecommand' and there is no alias, the false indicates to not output the (single) command name. */ fput_command_names_styled (c, false, "\n", stream); + fput_aliases_definition_styled (c, stream); fputs_filtered (c->doc, stream); fputs_filtered ("\n", stream); @@ -1341,7 +1385,7 @@ help_all (struct ui_file *stream) fprintf_filtered (stream, "\nUnclassified commands\n\n"); seen_unclassified = 1; } - print_help_for_command (c, 1, stream); + print_help_for_command (c, true, stream); } } @@ -1399,6 +1443,9 @@ print_help_for_command (struct cmd_list_element *c, fput_command_names_styled (c, true, " -- ", stream); print_doc_line (stream, c->doc, false); fputs_filtered ("\n", stream); + if (!c->default_args.empty ()) + fput_alias_definition_styled (c, stream); + fput_aliases_definition_styled (c, stream); if (recurse && c->prefixlist != 0 @@ -1582,8 +1629,12 @@ valid_user_defined_cmd_name_p (const char *name) the list in which there are ambiguous choices (and *TEXT will be set to the ambiguous text string). + if DEFAULT_ARGS is not null, *DEFAULT_ARGS is set to the found command + default args (possibly empty). + If the located command was an abbreviation, this routine returns the base - command of the abbreviation. + command of the abbreviation. Note that *DEFAULT_ARGS will contain the + default args defined for the alias. It does no error reporting whatsoever; control will always return to the superior routine. @@ -1610,11 +1661,13 @@ valid_user_defined_cmd_name_p (const char *name) struct cmd_list_element * lookup_cmd_1 (const char **text, struct cmd_list_element *clist, - struct cmd_list_element **result_list, int ignore_help_classes) + struct cmd_list_element **result_list, std::string *default_args, + int ignore_help_classes) { char *command; int len, nfound; struct cmd_list_element *found, *c; + bool found_alias = false; const char *line = *text; while (**text == ' ' || **text == '\t') @@ -1646,10 +1699,12 @@ lookup_cmd_1 (const char **text, struct cmd_list_element *clist, if (nfound > 1) { - if (result_list != NULL) + if (result_list != nullptr) /* Will be modified in calling routine if we know what the prefix command is. */ *result_list = 0; + if (default_args != nullptr) + *default_args = std::string (); return CMD_LIST_AMBIGUOUS; /* Ambiguous. */ } @@ -1665,22 +1720,30 @@ lookup_cmd_1 (const char **text, struct cmd_list_element *clist, are warning about the alias, we may also warn about the command itself and we will adjust the appropriate DEPRECATED_WARN_USER flags. */ - + if (found->deprecated_warn_user) deprecated_cmd_warning (line); + + /* Return the default_args of the alias, not the default_args + of the command it is pointing to. */ + if (default_args != nullptr) + *default_args = found->default_args; found = found->cmd_pointer; + found_alias = true; } /* If we found a prefix command, keep looking. */ if (found->prefixlist) { c = lookup_cmd_1 (text, *found->prefixlist, result_list, - ignore_help_classes); + default_args, ignore_help_classes); if (!c) { /* Didn't find anything; this is as far as we got. */ - if (result_list != NULL) + if (result_list != nullptr) *result_list = clist; + if (!found_alias && default_args != nullptr) + *default_args = found->default_args; return found; } else if (c == CMD_LIST_AMBIGUOUS) @@ -1688,13 +1751,16 @@ lookup_cmd_1 (const char **text, struct cmd_list_element *clist, /* We've gotten this far properly, but the next step is ambiguous. We need to set the result list to the best we've found (if an inferior hasn't already set it). */ - if (result_list != NULL) + if (result_list != nullptr) if (!*result_list) /* This used to say *result_list = *found->prefixlist. If that was correct, need to modify the documentation at the top of this function to clarify what is supposed to be going on. */ *result_list = found; + /* For ambiguous commands, do not return any default_args args. */ + if (default_args != nullptr) + *default_args = std::string (); return c; } else @@ -1705,8 +1771,10 @@ lookup_cmd_1 (const char **text, struct cmd_list_element *clist, } else { - if (result_list != NULL) + if (result_list != nullptr) *result_list = clist; + if (!found_alias && default_args != nullptr) + *default_args = found->default_args; return found; } } @@ -1726,8 +1794,13 @@ undef_cmd_error (const char *cmdtype, const char *q) /* Look up the contents of *LINE as a command in the command list LIST. LIST is a chain of struct cmd_list_element's. - If it is found, return the struct cmd_list_element for that command - and update *LINE to point after the command name, at the first argument. + If it is found, return the struct cmd_list_element for that command, + update *LINE to point after the command name, at the first argument + and update *DEFAULT_ARGS (if DEFAULT_ARGS is not null) to the default + args to prepend to the user provided args when running the command. + Note that if the found cmd_list_element is found via an alias, + the default args of the alias are returned. + If not found, call error if ALLOW_UNKNOWN is zero otherwise (or if error returns) return zero. Call error if specified command is ambiguous, @@ -1741,6 +1814,7 @@ undef_cmd_error (const char *cmdtype, const char *q) struct cmd_list_element * lookup_cmd (const char **line, struct cmd_list_element *list, const char *cmdtype, + std::string *default_args, int allow_unknown, int ignore_help_classes) { struct cmd_list_element *last_list = 0; @@ -1752,7 +1826,7 @@ lookup_cmd (const char **line, struct cmd_list_element *list, if (!*line) error (_("Lack of needed %scommand"), cmdtype); - c = lookup_cmd_1 (line, list, &last_list, ignore_help_classes); + c = lookup_cmd_1 (line, list, &last_list, default_args, ignore_help_classes); if (!c) { diff --git a/gdb/cli/cli-decode.h b/gdb/cli/cli-decode.h index f4719bfac47..f855ee5724f 100644 --- a/gdb/cli/cli-decode.h +++ b/gdb/cli/cli-decode.h @@ -183,6 +183,10 @@ struct cmd_list_element /* Hook for another command to be executed after this command. */ struct cmd_list_element *hook_post = nullptr; + /* Default arguments to automatically prepend to the user + provided arguments when running this command or alias. */ + std::string default_args; + /* Nonzero identifies a prefix command. For them, the address of the variable containing the list of subcommands. */ struct cmd_list_element **prefixlist = nullptr; diff --git a/gdb/cli/cli-script.c b/gdb/cli/cli-script.c index 90ee67a5f3d..da4a41023ad 100644 --- a/gdb/cli/cli-script.c +++ b/gdb/cli/cli-script.c @@ -974,7 +974,7 @@ process_next_line (const char *p, struct command_line **command, /* Resolve command abbreviations (e.g. 'ws' for 'while-stepping'). */ const char *cmd_name = p; struct cmd_list_element *cmd - = lookup_cmd_1 (&cmd_name, cmdlist, NULL, 1); + = lookup_cmd_1 (&cmd_name, cmdlist, NULL, NULL, 1); cmd_name = skip_spaces (cmd_name); bool inline_cmd = *cmd_name != '\0'; @@ -1331,7 +1331,7 @@ validate_comname (const char **comname) std::string prefix (*comname, last_word - 1); const char *tem = prefix.c_str (); - c = lookup_cmd (&tem, cmdlist, "", 0, 1); + c = lookup_cmd (&tem, cmdlist, "", NULL, 0, 1); if (c->prefixlist == NULL) error (_("\"%s\" is not a prefix command."), prefix.c_str ()); @@ -1387,7 +1387,7 @@ do_define_command (const char *comname, int from_tty, /* Look it up, and verify that we got an exact match. */ tem = comname; - c = lookup_cmd (&tem, *list, "", -1, 1); + c = lookup_cmd (&tem, *list, "", NULL, -1, 1); if (c && strcmp (comname, c->name) != 0) c = 0; @@ -1432,7 +1432,7 @@ do_define_command (const char *comname, int from_tty, { /* Look up cmd it hooks, and verify that we got an exact match. */ tem = comname + hook_name_size; - hookc = lookup_cmd (&tem, *list, "", -1, 0); + hookc = lookup_cmd (&tem, *list, "", NULL, -1, 0); if (hookc && strcmp (comname + hook_name_size, hookc->name) != 0) hookc = 0; if (!hookc && commands == nullptr) @@ -1518,7 +1518,7 @@ document_command (const char *comname, int from_tty) list = validate_comname (&comname); tem = comname; - c = lookup_cmd (&tem, *list, "", 0, 1); + c = lookup_cmd (&tem, *list, "", NULL, 0, 1); if (c->theclass != class_user) error (_("Command \"%s\" is built-in."), comfull); @@ -1566,7 +1566,7 @@ define_prefix_command (const char *comname, int from_tty) /* Look it up, and verify that we got an exact match. */ tem = comname; - c = lookup_cmd (&tem, *list, "", -1, 1); + c = lookup_cmd (&tem, *list, "", NULL, -1, 1); if (c != nullptr && strcmp (comname, c->name) != 0) c = nullptr; diff --git a/gdb/command.h b/gdb/command.h index 32b5b35b0c5..2cac5c8ced0 100644 --- a/gdb/command.h +++ b/gdb/command.h @@ -291,11 +291,13 @@ extern enum cmd_types cmd_type (struct cmd_list_element *cmd); extern struct cmd_list_element *lookup_cmd (const char **, struct cmd_list_element *, const char *, + std::string *, int, int); extern struct cmd_list_element *lookup_cmd_1 (const char **, struct cmd_list_element *, struct cmd_list_element **, + std::string *, int); extern struct cmd_list_element *deprecate_cmd (struct cmd_list_element *, diff --git a/gdb/completer.c b/gdb/completer.c index ea07096210f..7d26774e851 100644 --- a/gdb/completer.c +++ b/gdb/completer.c @@ -1385,7 +1385,7 @@ complete_line_internal_1 (completion_tracker &tracker, } else { - c = lookup_cmd_1 (&p, cmdlist, &result_list, ignore_help_classes); + c = lookup_cmd_1 (&p, cmdlist, &result_list, NULL, ignore_help_classes); } /* Move p up to the next interesting thing. */ diff --git a/gdb/guile/scm-cmd.c b/gdb/guile/scm-cmd.c index ec1adffa250..8fd2772df48 100644 --- a/gdb/guile/scm-cmd.c +++ b/gdb/guile/scm-cmd.c @@ -512,7 +512,7 @@ gdbscm_parse_command_name (const char *name, prefix_text[i + 1] = '\0'; prefix_text2 = prefix_text; - elt = lookup_cmd_1 (&prefix_text2, *start_list, NULL, 1); + elt = lookup_cmd_1 (&prefix_text2, *start_list, NULL, NULL, 1); if (elt == NULL || elt == CMD_LIST_AMBIGUOUS) { msg = xstrprintf (_("could not find command prefix '%s'"), prefix_text); diff --git a/gdb/guile/scm-param.c b/gdb/guile/scm-param.c index b72766523aa..62e21087403 100644 --- a/gdb/guile/scm-param.c +++ b/gdb/guile/scm-param.c @@ -466,13 +466,13 @@ add_setshow_generic (enum var_types param_type, enum command_class cmd_class, /* Lookup created parameter, and register Scheme object against the parameter context. Perform this task against both lists. */ tmp_name = cmd_name; - param = lookup_cmd (&tmp_name, *show_list, "", 0, 1); + param = lookup_cmd (&tmp_name, *show_list, "", NULL, 0, 1); gdb_assert (param != NULL); set_cmd_context (param, self); *set_cmd = param; tmp_name = cmd_name; - param = lookup_cmd (&tmp_name, *set_list, "", 0, 1); + param = lookup_cmd (&tmp_name, *set_list, "", NULL, 0, 1); gdb_assert (param != NULL); set_cmd_context (param, self); *show_cmd = param; @@ -969,7 +969,7 @@ pascm_parameter_defined_p (const char *name, struct cmd_list_element *list) { struct cmd_list_element *c; - c = lookup_cmd_1 (&name, list, NULL, 1); + c = lookup_cmd_1 (&name, list, NULL, NULL, 1); /* If the name is ambiguous that's ok, it's a new parameter still. */ return c != NULL && c != CMD_LIST_AMBIGUOUS; diff --git a/gdb/infcmd.c b/gdb/infcmd.c index 891da91c806..42b050d3c4e 100644 --- a/gdb/infcmd.c +++ b/gdb/infcmd.c @@ -3052,7 +3052,7 @@ is restored."), show_inferior_tty_command, &setlist, &showlist); cmd_name = "inferior-tty"; - c = lookup_cmd (&cmd_name, setlist, "", -1, 1); + c = lookup_cmd (&cmd_name, setlist, "", NULL, -1, 1); gdb_assert (c != NULL); add_alias_cmd ("tty", c, class_run, 0, &cmdlist); @@ -3065,7 +3065,7 @@ Follow this command with any number of args, to be passed to the program."), set_args_command, show_args_command, &setlist, &showlist); - c = lookup_cmd (&cmd_name, setlist, "", -1, 1); + c = lookup_cmd (&cmd_name, setlist, "", NULL, -1, 1); gdb_assert (c != NULL); set_cmd_completer (c, filename_completer); @@ -3084,7 +3084,7 @@ working directory."), set_cwd_command, show_cwd_command, &setlist, &showlist); - c = lookup_cmd (&cmd_name, setlist, "", -1, 1); + c = lookup_cmd (&cmd_name, setlist, "", NULL, -1, 1); gdb_assert (c != NULL); set_cmd_completer (c, filename_completer); diff --git a/gdb/python/py-auto-load.c b/gdb/python/py-auto-load.c index 2084fc43ffa..56db946463a 100644 --- a/gdb/python/py-auto-load.c +++ b/gdb/python/py-auto-load.c @@ -84,12 +84,12 @@ Show the debugger's behaviour regarding auto-loaded Python scripts, " NULL, NULL, show_auto_load_python_scripts, &setlist, &showlist); cmd_name = "auto-load-scripts"; - cmd = lookup_cmd (&cmd_name, setlist, "", -1, 1); + cmd = lookup_cmd (&cmd_name, setlist, "", NULL, -1, 1); deprecate_cmd (cmd, "set auto-load python-scripts"); /* It is needed because lookup_cmd updates the CMD_NAME pointer. */ cmd_name = "auto-load-scripts"; - cmd = lookup_cmd (&cmd_name, showlist, "", -1, 1); + cmd = lookup_cmd (&cmd_name, showlist, "", NULL, -1, 1); deprecate_cmd (cmd, "show auto-load python-scripts"); add_cmd ("python-scripts", class_info, info_auto_load_python_scripts, diff --git a/gdb/python/py-cmd.c b/gdb/python/py-cmd.c index 3c1c566b0a1..760208f52b9 100644 --- a/gdb/python/py-cmd.c +++ b/gdb/python/py-cmd.c @@ -390,7 +390,7 @@ gdbpy_parse_command_name (const char *name, std::string prefix_text (name, i + 1); prefix_text2 = prefix_text.c_str (); - elt = lookup_cmd_1 (&prefix_text2, *start_list, NULL, 1); + elt = lookup_cmd_1 (&prefix_text2, *start_list, NULL, NULL, 1); if (elt == NULL || elt == CMD_LIST_AMBIGUOUS) { PyErr_Format (PyExc_RuntimeError, _("Could not find command prefix %s."), diff --git a/gdb/python/py-param.c b/gdb/python/py-param.c index 7b183cfa553..fb39187b184 100644 --- a/gdb/python/py-param.c +++ b/gdb/python/py-param.c @@ -569,12 +569,12 @@ add_setshow_generic (int parmclass, enum command_class cmdclass, /* Lookup created parameter, and register Python object against the parameter context. Perform this task against both lists. */ tmp_name = cmd_name; - param = lookup_cmd (&tmp_name, *show_list, "", 0, 1); + param = lookup_cmd (&tmp_name, *show_list, "", NULL, 0, 1); if (param) set_cmd_context (param, self); tmp_name = cmd_name; - param = lookup_cmd (&tmp_name, *set_list, "", 0, 1); + param = lookup_cmd (&tmp_name, *set_list, "", NULL, 0, 1); if (param) set_cmd_context (param, self); } diff --git a/gdb/remote.c b/gdb/remote.c index fd89f2c0840..d560c69eca4 100644 --- a/gdb/remote.c +++ b/gdb/remote.c @@ -14424,10 +14424,10 @@ If set, a break, instead of a cntrl-c, is sent to the remote target."), set_remotebreak, show_remotebreak, &setlist, &showlist); cmd_name = "remotebreak"; - cmd = lookup_cmd (&cmd_name, setlist, "", -1, 1); + cmd = lookup_cmd (&cmd_name, setlist, "", NULL, -1, 1); deprecate_cmd (cmd, "set remote interrupt-sequence"); cmd_name = "remotebreak"; /* needed because lookup_cmd updates the pointer */ - cmd = lookup_cmd (&cmd_name, showlist, "", -1, 1); + cmd = lookup_cmd (&cmd_name, showlist, "", NULL, -1, 1); deprecate_cmd (cmd, "show remote interrupt-sequence"); add_setshow_enum_cmd ("interrupt-sequence", class_support, diff --git a/gdb/top.c b/gdb/top.c index c62eb57695c..da9b805b479 100644 --- a/gdb/top.c +++ b/gdb/top.c @@ -579,6 +579,8 @@ execute_command (const char *p, int from_tty) { const char *cmd = p; const char *arg; + std::string default_args; + std::string default_args_and_arg; int was_sync = current_ui->prompt_state == PROMPT_BLOCKED; line = p; @@ -586,15 +588,26 @@ execute_command (const char *p, int from_tty) /* If trace-commands is set then this will print this command. */ print_command_trace ("%s", p); - c = lookup_cmd (&cmd, cmdlist, "", 0, 1); + c = lookup_cmd (&cmd, cmdlist, "", &default_args, 0, 1); p = cmd; scoped_restore save_repeat_args = make_scoped_restore (&repeat_arguments, nullptr); const char *args_pointer = p; - /* Pass null arg rather than an empty one. */ - arg = *p ? p : 0; + if (!default_args.empty ()) + { + if (*p != '\0') + default_args_and_arg = default_args + ' ' + p; + else + default_args_and_arg = default_args; + arg = default_args_and_arg.c_str (); + } + else + { + /* Pass null arg rather than an empty one. */ + arg = *p == '\0' ? nullptr : p; + } /* FIXME: cagney/2002-02-02: The c->type test is pretty dodgy while the is_complete_command(cfunc) test is just plain @@ -1957,7 +1970,7 @@ set_verbose (const char *args, int from_tty, struct cmd_list_element *c) const char *cmdname = "verbose"; struct cmd_list_element *showcmd; - showcmd = lookup_cmd_1 (&cmdname, showlist, NULL, 1); + showcmd = lookup_cmd_1 (&cmdname, showlist, NULL, NULL, 1); gdb_assert (showcmd != NULL && showcmd != CMD_LIST_AMBIGUOUS); if (c->doc && c->doc_allocated) diff --git a/gdb/tracepoint.c b/gdb/tracepoint.c index f4a208f6169..00b7059be58 100644 --- a/gdb/tracepoint.c +++ b/gdb/tracepoint.c @@ -651,7 +651,7 @@ validate_actionline (const char *line, struct breakpoint *b) if (*p == '#') /* comment line */ return; - c = lookup_cmd (&p, cmdlist, "", -1, 1); + c = lookup_cmd (&p, cmdlist, "", NULL, -1, 1); if (c == 0) error (_("`%s' is not a tracepoint action, or is ambiguous."), p); @@ -1303,7 +1303,7 @@ encode_actions_1 (struct command_line *action, action_exp = action->line; action_exp = skip_spaces (action_exp); - cmd = lookup_cmd (&action_exp, cmdlist, "", -1, 1); + cmd = lookup_cmd (&action_exp, cmdlist, "", NULL, -1, 1); if (cmd == 0) error (_("Bad action list item: %s"), action_exp); @@ -2673,7 +2673,7 @@ trace_dump_actions (struct command_line *action, if (*action_exp == '#') /* comment line */ continue; - cmd = lookup_cmd (&action_exp, cmdlist, "", -1, 1); + cmd = lookup_cmd (&action_exp, cmdlist, "", NULL, -1, 1); if (cmd == 0) error (_("Bad action list item: %s"), action_exp); -- cgit v1.2.1