diff options
author | Doug Evans <dje@google.com> | 2011-10-09 22:21:43 +0000 |
---|---|---|
committer | Doug Evans <dje@google.com> | 2011-10-09 22:21:43 +0000 |
commit | 5a56e9c5e9a817598264d329c0f7936982683cf3 (patch) | |
tree | b098197d8cf4b75931db58241388806c7ed8be9e | |
parent | 509f0fd9410d9394d0a6e2fa4ef80e08de5598b5 (diff) | |
download | gdb-5a56e9c5e9a817598264d329c0f7936982683cf3.zip gdb-5a56e9c5e9a817598264d329c0f7936982683cf3.tar.gz gdb-5a56e9c5e9a817598264d329c0f7936982683cf3.tar.bz2 |
Add new "alias" command.
* NEWS: Mention new command.
* command.h (valid_user_defined_cmd_name_p): Declare.
* defs.h (make_cleanup_dyn_string_delete): Declare.
* utils.c: #include "dyn-string.h".
(do_dyn_string_delete, make_cleanup_dyn_string_delete): New functions.
* cli/cli-cmds.c: #include "dyn-string.h".
(argv_to_dyn_string, valid_command_p, alias_command): New functions.
(init_cli_cmds): Add new command.
* cli/cli-decode.c (valid_user_defined_cmd_name_p): New function.
doc/
* gdb.texinfo (Extending GDB): Document alias command.
testsuite/
* gdb.base/alias.exp: Add tests for alias command.
-rw-r--r-- | gdb/ChangeLog | 13 | ||||
-rw-r--r-- | gdb/NEWS | 3 | ||||
-rw-r--r-- | gdb/cli/cli-cmds.c | 189 | ||||
-rw-r--r-- | gdb/cli/cli-decode.c | 29 | ||||
-rw-r--r-- | gdb/command.h | 2 | ||||
-rw-r--r-- | gdb/defs.h | 3 | ||||
-rw-r--r-- | gdb/doc/ChangeLog | 4 | ||||
-rw-r--r-- | gdb/doc/gdb.texinfo | 99 | ||||
-rw-r--r-- | gdb/testsuite/ChangeLog | 4 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/alias.exp | 68 | ||||
-rw-r--r-- | gdb/utils.c | 13 |
11 files changed, 421 insertions, 6 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog index eb5ba79..47c815e 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,16 @@ +2011-10-09 Doug Evans <dje@google.com> + + Add new "alias" command. + * NEWS: Mention new command. + * command.h (valid_user_defined_cmd_name_p): Declare. + * defs.h (make_cleanup_dyn_string_delete): Declare. + * utils.c: #include "dyn-string.h". + (do_dyn_string_delete, make_cleanup_dyn_string_delete): New functions. + * cli/cli-cmds.c: #include "dyn-string.h". + (argv_to_dyn_string, valid_command_p, alias_command): New functions. + (init_cli_cmds): Add new command. + * cli/cli-decode.c (valid_user_defined_cmd_name_p): New function. + 2011-10-09 Jan Kratochvil <jan.kratochvil@redhat.com> Fix compatibility with older GCCs. @@ -81,7 +81,8 @@ the first connection is made. The listening port used by GDBserver will become available after that. -* New commands "info macros", and "info definitions" have been added. +* New commands "info macros", "info definitions", + and "alias" have been added. * New function parameters suffix @entry specifies value of function parameter at the time the function got called. Entry values are available only since diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c index ccf6ea6..34a530a 100644 --- a/gdb/cli/cli-cmds.c +++ b/gdb/cli/cli-cmds.c @@ -21,6 +21,7 @@ #include "defs.h" #include "exceptions.h" #include "arch-utils.h" +#include "dyn-string.h" #include "readline/readline.h" #include "readline/tilde.h" #include "completer.h" @@ -1272,6 +1273,180 @@ apropos_command (char *searchstr, int from_tty) error (_("Error in regular expression: %s"), err); } } + +/* 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. + ARGV may not be NULL. + This does not take care of quoting elements in case they contain spaces + on purpose. */ + +static dyn_string_t +argv_to_dyn_string (char **argv, int n) +{ + int i; + dyn_string_t result = dyn_string_new (10); + + gdb_assert (argv != NULL); + gdb_assert (n >= 0 && n <= countargv (argv)); + + for (i = 0; i < n; ++i) + { + if (i > 0) + dyn_string_append_char (result, ' '); + dyn_string_append_cstr (result, argv[i]); + } + + return result; +} + +/* Subroutine of alias_command to simplify it. + Return TRUE if COMMAND exists, unambiguously. Otherwise FALSE. */ + +static int +valid_command_p (char *command) +{ + struct cmd_list_element *c; + + c = lookup_cmd_1 (& command, cmdlist, NULL, 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'; +} + +/* Make an alias of an existing command. */ + +static void +alias_command (char *args, int from_tty) +{ + int i, alias_argc, command_argc; + int abbrev_flag = 0; + char *args2, *equals, *alias, *command; + char **alias_argv, **command_argv; + dyn_string_t alias_dyn_string, command_dyn_string; + struct cmd_list_element *c; + static const char usage[] = N_("Usage: alias [-a] [--] ALIAS = COMMAND"); + + if (args == NULL || strchr (args, '=') == NULL) + error (_(usage)); + + args2 = xstrdup (args); + make_cleanup (xfree, args2); + equals = strchr (args2, '='); + *equals = '\0'; + alias_argv = gdb_buildargv (args2); + make_cleanup_freeargv (alias_argv); + command_argv = gdb_buildargv (equals + 1); + make_cleanup_freeargv (command_argv); + + for (i = 0; alias_argv[i] != NULL; ) + { + if (strcmp (alias_argv[i], "-a") == 0) + { + ++alias_argv; + abbrev_flag = 1; + } + else if (strcmp (alias_argv[i], "--") == 0) + { + ++alias_argv; + break; + } + else + break; + } + + if (alias_argv[0] == NULL || command_argv[0] == NULL + || *alias_argv[0] == '\0' || *command_argv[0] == '\0') + error (_(usage)); + + for (i = 0; alias_argv[i] != NULL; ++i) + { + if (! valid_user_defined_cmd_name_p (alias_argv[i])) + { + if (i == 0) + error (_("Invalid command name: %s"), alias_argv[i]); + else + error (_("Invalid command element name: %s"), alias_argv[i]); + } + } + + alias_argc = countargv (alias_argv); + command_argc = countargv (command_argv); + + /* COMMAND must exist. + Reconstruct the command to remove any extraneous spaces, + for better error messages. */ + command_dyn_string = argv_to_dyn_string (command_argv, command_argc); + make_cleanup_dyn_string_delete (command_dyn_string); + command = dyn_string_buf (command_dyn_string); + if (! valid_command_p (command)) + error (_("Invalid command to alias to: %s"), command); + + /* ALIAS must not exist. */ + alias_dyn_string = argv_to_dyn_string (alias_argv, alias_argc); + make_cleanup_dyn_string_delete (alias_dyn_string); + alias = dyn_string_buf (alias_dyn_string); + if (valid_command_p (alias)) + error (_("Alias already exists: %s"), alias); + + /* If ALIAS is one word, it is an alias for the entire COMMAND. + Example: alias spe = set print elements + + Otherwise ALIAS and COMMAND must have the same number of words, + and every word except the last must match; and the last word of + ALIAS is made an alias of the last word of COMMAND. + Example: alias set print elms = set pr elem + Note that unambiguous abbreviations are allowed. */ + + 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); + } + else + { + int i; + dyn_string_t alias_prefix_dyn_string, command_prefix_dyn_string; + char *alias_prefix, *command_prefix; + struct cmd_list_element *c_alias, *c_command; + + if (alias_argc != command_argc) + error (_("Mismatched command length between ALIAS and COMMAND.")); + + /* Create copies of ALIAS and COMMAND without the last word, + and use that to verify the leading elements match. */ + alias_prefix_dyn_string = + argv_to_dyn_string (alias_argv, alias_argc - 1); + make_cleanup_dyn_string_delete (alias_prefix_dyn_string); + command_prefix_dyn_string = + argv_to_dyn_string (alias_argv, command_argc - 1); + make_cleanup_dyn_string_delete (command_prefix_dyn_string); + alias_prefix = dyn_string_buf (alias_prefix_dyn_string); + command_prefix = dyn_string_buf (command_prefix_dyn_string); + + c_command = lookup_cmd_1 (& command_prefix, cmdlist, 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); + 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); + } +} /* Print a list of files and line numbers which a user may choose from in order to list a function which was specified ambiguously (as @@ -1674,4 +1849,18 @@ When 'on', each command is displayed as it is executed."), NULL, NULL, &setlist, &showlist); + + c = add_com ("alias", class_support, alias_command, _("\ +Define a new command that is an alias of an existing command.\n\ +Usage: alias [-a] [--] ALIAS = COMMAND\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 appear in help command list output.\n\ +\n\ +Examples:\n\ +Make \"spe\" an alias of \"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")); } diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c index 95996e8..0870782 100644 --- a/gdb/cli/cli-decode.c +++ b/gdb/cli/cli-decode.c @@ -126,7 +126,6 @@ set_cmd_completer (struct cmd_list_element *cmd, cmd->completer = completer; /* Ok. */ } - /* Add element named NAME. Space for NAME and DOC must be allocated by the caller. CLASS is the top level category into which commands are broken down @@ -1138,6 +1137,34 @@ find_command_name_length (const char *text) return p - text; } +/* Return TRUE if NAME is a valid user-defined command name. + This is a stricter subset of all gdb commands, + see find_command_name_length. */ + +int +valid_user_defined_cmd_name_p (const char *name) +{ + const char *p; + + if (*name == '\0') + return FALSE; + + /* Alas "42" is a legitimate user-defined command. + In the interests of not breaking anything we preserve that. */ + + for (p = name; *p != '\0'; ++p) + { + if (isalnum (*p) + || *p == '-' + || *p == '_') + ; /* Ok. */ + else + return FALSE; + } + + return TRUE; +} + /* This routine takes a line of TEXT and a CLIST in which to start the lookup. When it returns it will have incremented the text pointer past the section of text it matched, set *RESULT_LIST to point to the list in diff --git a/gdb/command.h b/gdb/command.h index 0e2547e..04cb751 100644 --- a/gdb/command.h +++ b/gdb/command.h @@ -106,6 +106,8 @@ struct cmd_list_element; /* Forward-declarations of the entry-points of cli/cli-decode.c. */ +extern int valid_user_defined_cmd_name_p (const char *name); + extern struct cmd_list_element *add_cmd (char *, enum command_class, void (*fun) (char *, int), char *, struct cmd_list_element **); @@ -340,6 +340,9 @@ extern struct cleanup *make_cleanup_dtor (make_cleanup_ftype *, void *, extern struct cleanup *make_cleanup_freeargv (char **); +struct dyn_string; +extern struct cleanup *make_cleanup_dyn_string_delete (struct dyn_string *); + struct ui_file; extern struct cleanup *make_cleanup_ui_file_delete (struct ui_file *); diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog index 808cf21..8ebc4f2 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -1,3 +1,7 @@ +2011-10-09 Doug Evans <dje@google.com> + + * gdb.texinfo (Extending GDB): Document alias command. + 2011-10-09 Jan Kratochvil <jan.kratochvil@redhat.com> Support @entry in input expressions. diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 1ea2ba3..5a78db1 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -20609,11 +20609,12 @@ Displays whether the debugger is operating in interactive mode or not. @chapter Extending @value{GDBN} @cindex extending GDB -@value{GDBN} provides two mechanisms for extension. The first is based -on composition of @value{GDBN} commands, and the second is based on the -Python scripting language. +@value{GDBN} provides three mechanisms for extension. The first is based +on composition of @value{GDBN} commands, the second is based on the +Python scripting language, and the third is for defining new aliases of +existing commands. -To facilitate the use of these extensions, @value{GDBN} is capable +To facilitate the use of the first two extensions, @value{GDBN} is capable of evaluating the contents of a file. When doing so, @value{GDBN} can recognize which scripting language is being used by looking at the filename extension. Files with an unrecognized filename extension @@ -20648,6 +20649,7 @@ Display the current value of the @code{script-extension} option. @menu * Sequences:: Canned Sequences of Commands * Python:: Scripting @value{GDBN} using Python +* Aliases:: Creating new spellings of existing commands @end menu @node Sequences @@ -24386,6 +24388,95 @@ substitute_prompt (``frame: \f, @end smallexample @end table +@node Aliases +@section Creating new spellings of existing commands +@cindex aliases for commands + +It is often useful to define alternate spellings of existing commands. +For example, if a new @value{GDBN} command defined in Python has +a long name to type, it is handy to have an abbreviated version of it +that involves less typing. + +@value{GDBN} itself uses aliases. For example @samp{s} is an alias +of the @samp{step} command even though it is otherwise an ambiguous +abbreviation of other commands like @samp{set} and @samp{show}. + +Aliases are also used to provide shortened or more common versions +of multi-word commands. For example, @value{GDBN} provides the +@samp{tty} alias of the @samp{set inferior-tty} command. + +You can define a new alias with the @samp{alias} command. + +@table @code + +@kindex alias +@item alias [-a] [--] @var{ALIAS} = @var{COMMAND} + +@end table + +@var{ALIAS} specifies the name of the new alias. +Each word of @var{ALIAS} must consist of letters, numbers, dashes and +underscores. + +@var{COMMAND} specifies the name of an existing command +that is being aliased. + +The @samp{-a} option specifies that the new alias is an abbreviation +of the command. Abbreviations are not shown in command +lists displayed by the @samp{help} command. + +The @samp{--} option specifies the end of options, +and is useful when @var{ALIAS} begins with a dash. + +Here is a simple example showing how to make an abbreviation +of a command so that there is less to type. +Suppose you were tired of typing @samp{disas}, the current +shortest unambiguous abbreviation of the @samp{disassemble} command +and you wanted an even shorter version named @samp{di}. +The following will accomplish this. + +@smallexample +(gdb) alias -a di = disas +@end smallexample + +Note that aliases are different from user-defined commands. +With a user-defined command, you also need to write documentation +for it with the @samp{document} command. +An alias automatically picks up the documentation of the existing command. + +Here is an example where we make @samp{elms} an abbreviation of +@samp{elements} in the @samp{set print elements} command. +This is to show that you can make an abbreviation of any part +of a command. + +@smallexample +(gdb) alias -a set print elms = set print elements +(gdb) alias -a show print elms = show print elements +(gdb) set p elms 20 +(gdb) show p elms +Limit on string chars or array elements to print is 200. +@end smallexample + +Note that if you are defining an alias of a @samp{set} command, +and you want to have an alias for the corresponding @samp{show} +command, then you need to define the latter separately. + +Unambiguously abbreviated commands are allowed in @var{COMMAND} and +@var{ALIAS}, just as they are normally. + +@smallexample +(gdb) alias -a set pr elms = set p ele +@end smallexample + +Finally, here is an example showing the creation of a one word +alias for a more complex command. +This creates alias @samp{spe} of the command @samp{set print elements}. + +@smallexample +(gdb) alias spe = set print elements +(gdb) spe 20 +@end smallexample + @node Interpreters @chapter Command Interpreters @cindex command interpreters diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 8c66a8e..4fc974e 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,7 @@ +2011-10-09 Doug Evans <dje@google.com> + + * gdb.base/alias.exp: Add tests for alias command. + 2011-10-09 Jan Kratochvil <jan.kratochvil@redhat.com> * gdb.arch/amd64-entry-value.s: New file. diff --git a/gdb/testsuite/gdb.base/alias.exp b/gdb/testsuite/gdb.base/alias.exp new file mode 100644 index 0000000..66384c0 --- /dev/null +++ b/gdb/testsuite/gdb.base/alias.exp @@ -0,0 +1,68 @@ +# Test the alias command. +# Copyright 2011 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +gdb_exit +gdb_start +gdb_reinitialize_dir $srcdir/$subdir + +# Helper to test the -a option to alias. +# Aliases that are abbreviations of commands (e.g. r -> run) +# do not appear in help output. + +proc test_abbrev_not_present { alias_name } { + global gdb_prompt + set alias_present 0 + set test_name "abbrev $alias_name not present in help command list" + gdb_test_multiple "help aliases" $test_name { + -re "\[\r\n\]$alias_name \[^\r\n\]*" { + set alias_present 1 + exp_continue + } + -re ".*$gdb_prompt $" { + if { !$alias_present } then { + pass $test_name + } else { + fail $test_name + } + } + } +} + +proc test_abbrev_alias { name gdb_command test_value } { + gdb_test_no_output $gdb_command + gdb_test_no_output "$name print elements $test_value" + gdb_test "show print elements" "Limit .* is $test_value\[.\]" "verify $name" + test_abbrev_not_present $name +} + +test_abbrev_alias set2 "alias -a set2=set" 42 +test_abbrev_alias set3 "alias -a set3= set" 43 +test_abbrev_alias set4 "alias -a set4 =set" 44 +test_abbrev_alias set5 "alias -a set5 = set" 45 +test_abbrev_alias set6 "alias -a -- set6 = set" 46 +test_abbrev_alias -a "alias -a -- -a = set" 47 + +gdb_test "alias set2=set" "already exists: set2" +gdb_test "alias foo=bar" "Invalid command to alias to: bar" + +gdb_test_no_output "alias spe = set p elem" +gdb_test_no_output "spe 50" +gdb_test "show print elements" "Limit .* is 50\[.\]" "verify spe" + +gdb_test_no_output "alias set pr elms = set p elem" +gdb_test_no_output "set pr elms 51" +gdb_test "show print elements" "Limit .* is 51\[.\]" "verify set pr elms" +gdb_test "help set print" "set print elms .*" diff --git a/gdb/utils.c b/gdb/utils.c index 08c08c6..4a0b5a2 100644 --- a/gdb/utils.c +++ b/gdb/utils.c @@ -20,6 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "defs.h" +#include "dyn-string.h" #include "gdb_assert.h" #include <ctype.h> #include "gdb_string.h" @@ -238,6 +239,18 @@ make_cleanup_freeargv (char **arg) } static void +do_dyn_string_delete (void *arg) +{ + dyn_string_delete ((dyn_string_t) arg); +} + +struct cleanup * +make_cleanup_dyn_string_delete (dyn_string_t arg) +{ + return make_my_cleanup (&cleanup_chain, do_dyn_string_delete, arg); +} + +static void do_bfd_close_cleanup (void *arg) { bfd_close (arg); |