[RFA, doc RFA] Add gdb.add_command_alias (now: alias foo = bar)

Doug Evans dje@google.com
Sun Oct 9 22:27:00 GMT 2011


Here is the patch I checked in.


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.

        doc/
        * gdb.texinfo (Extending GDB): Document alias command.

        testsuite/
        * gdb.base/alias.exp: Add tests for alias command.
-------------- next part --------------
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.

	doc/
	* gdb.texinfo (Extending GDB): Document alias command.

	testsuite/
	* gdb.base/alias.exp: Add tests for alias command.

Index: NEWS
===================================================================
RCS file: /cvs/src/src/gdb/NEWS,v
retrieving revision 1.456
diff -u -p -r1.456 NEWS
--- NEWS	9 Oct 2011 19:46:04 -0000	1.456
+++ NEWS	9 Oct 2011 22:15:56 -0000
@@ -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
Index: command.h
===================================================================
RCS file: /cvs/src/src/gdb/command.h,v
retrieving revision 1.74
diff -u -p -r1.74 command.h
--- command.h	14 Feb 2011 23:41:33 -0000	1.74
+++ command.h	9 Oct 2011 22:15:56 -0000
@@ -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 **);
Index: defs.h
===================================================================
RCS file: /cvs/src/src/gdb/defs.h,v
retrieving revision 1.302
diff -u -p -r1.302 defs.h
--- defs.h	9 Oct 2011 19:21:38 -0000	1.302
+++ defs.h	9 Oct 2011 22:15:56 -0000
@@ -340,6 +340,9 @@ extern struct cleanup *make_cleanup_dtor
 
 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 *);
 
Index: utils.c
===================================================================
RCS file: /cvs/src/src/gdb/utils.c,v
retrieving revision 1.262
diff -u -p -r1.262 utils.c
--- utils.c	9 Oct 2011 19:21:38 -0000	1.262
+++ utils.c	9 Oct 2011 22:15:56 -0000
@@ -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);
Index: cli/cli-cmds.c
===================================================================
RCS file: /cvs/src/src/gdb/cli/cli-cmds.c,v
retrieving revision 1.115
diff -u -p -r1.115 cli-cmds.c
--- cli/cli-cmds.c	4 Aug 2011 19:10:13 -0000	1.115
+++ cli/cli-cmds.c	9 Oct 2011 22:15:57 -0000
@@ -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 fr
       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 
 			   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"));
 }
Index: cli/cli-decode.c
===================================================================
RCS file: /cvs/src/src/gdb/cli/cli-decode.c,v
retrieving revision 1.97
diff -u -p -r1.97 cli-decode.c
--- cli/cli-decode.c	8 Sep 2011 17:20:43 -0000	1.97
+++ cli/cli-decode.c	9 Oct 2011 22:15:57 -0000
@@ -126,7 +126,6 @@ set_cmd_completer (struct cmd_list_eleme
   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 *te
   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
Index: doc/gdb.texinfo
===================================================================
RCS file: /cvs/src/src/gdb/doc/gdb.texinfo,v
retrieving revision 1.875
diff -u -p -r1.875 gdb.texinfo
--- doc/gdb.texinfo	9 Oct 2011 19:41:16 -0000	1.875
+++ doc/gdb.texinfo	9 Oct 2011 22:15:59 -0000
@@ -20609,11 +20609,12 @@ Displays whether the debugger is operati
 @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{s
 @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
Index: testsuite/gdb.base/alias.exp
===================================================================
RCS file: testsuite/gdb.base/alias.exp
diff -N testsuite/gdb.base/alias.exp
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ testsuite/gdb.base/alias.exp	9 Oct 2011 22:15:59 -0000
@@ -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 .*"


More information about the Gdb-patches mailing list