This is the mail archive of the
archer@sourceware.org
mailing list for the Archer project.
[python] allow prefix commands
- From: Tom Tromey <tromey at redhat dot com>
- To: Project Archer <archer at sourceware dot org>
- Date: Tue, 18 Nov 2008 11:51:30 -0700
- Subject: [python] allow prefix commands
- Reply-to: Tom Tromey <tromey at redhat dot com>
This updates the python Command class to allow new prefix commands to
be defined.
Tom
2008-11-18 Tom Tromey <tromey@redhat.com>
* python/python-cmd.c (struct cmdpy_object) <sub_list>: New field.
(cmdpy_init): Allow prefix commands.
(cmdpy_destroyer): Free the prefix name.
(cmdpy_function): Handle prefix commands.
2008-11-18 Tom Tromey <tromey@redhat.com>
* gdb.texinfo (Commands In Python): Document prefix commands.
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 9446ce4..1231ab3 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -18308,7 +18308,7 @@ You can implement new @value{GDBN} CLI commands in Python. A CLI
command is implemented using an instance of the @code{gdb.Command}
class, most commonly using a subclass.
-@defmethod Command __init__ name @var{command-class} @r{[}@var{completer-class}@r{]}
+@defmethod Command __init__ name @var{command-class} @r{[}@var{completer-class} @var{prefix}@r{]}
The object initializer for @code{Command} registers the new command
with @value{GDBN}. This initializer is normally invoked from the
subclass' own @code{__init__} method.
@@ -18318,8 +18318,7 @@ multiple words, then the initial words are looked for as prefix
commands. In this case, if one of the prefix commands does not exist,
an exception is raised.
-There is no support for multi-line commands. Currently, there is also
-no way to define new prefix commands.
+There is no support for multi-line commands.
@var{command-class} should be one of the @samp{COMMAND_} constants
defined below. This argument tells @value{GDBN} how to categorize the
@@ -18332,6 +18331,10 @@ given, @value{GDBN} will attempt to complete using the object's
@code{complete} method (see below); if no such method is found, an
error will occur when completion is attempted.
+@var{prefix} is an optional argument. If @code{True}, then the new
+command is a prefix command; sub-commands of this command may be
+registered.
+
The help text for the new command is taken from the Python
documentation string for the command's class, if there is one. If
there is no documentation string, a default value is used.
diff --git a/gdb/python/python-cmd.c b/gdb/python/python-cmd.c
index 91cdc7f..014274b 100644
--- a/gdb/python/python-cmd.c
+++ b/gdb/python/python-cmd.c
@@ -54,8 +54,10 @@ struct cmdpy_object
/* The corresponding gdb command object, or NULL if the command is
no longer installed. */
- /* It isn't clear if we will ever care about this. */
struct cmd_list_element *command;
+
+ /* For a prefix command, this is the list of sub-commands. */
+ struct cmd_list_element *sub_list;
};
typedef struct cmdpy_object cmdpy_object;
@@ -92,9 +94,11 @@ cmdpy_destroyer (struct cmd_list_element *self, void *context)
cmd->command = NULL;
Py_DECREF (cmd);
- /* We allocated the name and doc string. */
+ /* We allocated the name, doc string, and perhaps the prefix
+ name. */
xfree (self->name);
xfree (self->doc);
+ xfree (self->prefixname);
PyGILState_Release (state);
}
@@ -114,7 +118,15 @@ cmdpy_function (struct cmd_list_element *command, char *args, int from_tty)
if (! obj)
error (_("Invalid invocation of Python command object."));
if (! PyObject_HasAttr ((PyObject *) obj, invoke_cst))
- error (_("Python command object missing 'invoke' method."));
+ {
+ if (obj->command->prefixname)
+ {
+ /* A prefix command does not need an invoke method. */
+ do_cleanups (cleanup);
+ return;
+ }
+ error (_("Python command object missing 'invoke' method."));
+ }
if (! args)
{
@@ -327,7 +339,7 @@ gdbpy_parse_command_name (char *text, struct cmd_list_element ***base_list,
/* Object initializer; sets up gdb-side structures for command.
- Use: __init__(NAME, CMDCLASS, [COMPLETERCLASS]).
+ Use: __init__(NAME, CMDCLASS, [COMPLETERCLASS, [PREFIX]]).
NAME is the name of the command. It may consist of multiple words,
in which case the final word is the name of the new command, and
@@ -340,6 +352,8 @@ gdbpy_parse_command_name (char *text, struct cmd_list_element ***base_list,
"complete" method will be used. Otherwise, it should be one of the
COMPLETE_* constants defined in the gdb module.
+ If PREFIX is True, then this command is a prefix command.
+
The documentation for the command is taken from the doc string for
the python class.
@@ -354,7 +368,8 @@ cmdpy_init (PyObject *self, PyObject *args, PyObject *kwds)
char *docstring = NULL;
volatile struct gdb_exception except;
struct cmd_list_element **cmd_list;
- char *cmd_name;
+ char *cmd_name, *pfx_name;
+ PyObject *is_prefix = NULL;
if (obj->command)
{
@@ -365,7 +380,8 @@ cmdpy_init (PyObject *self, PyObject *args, PyObject *kwds)
return -1;
}
- if (! PyArg_ParseTuple (args, "si|i", &name, &cmdtype, &completetype))
+ if (! PyArg_ParseTuple (args, "si|iO", &name, &cmdtype,
+ &completetype, &is_prefix))
return -1;
if (cmdtype != no_class && cmdtype != class_run
@@ -389,6 +405,31 @@ cmdpy_init (PyObject *self, PyObject *args, PyObject *kwds)
if (! cmd_name)
return -1;
+ pfx_name = NULL;
+ if (is_prefix == Py_True)
+ {
+ int i, out;
+
+ /* Make a normalized form of the command name. */
+ pfx_name = xmalloc (strlen (name) + 2);
+
+ i = 0;
+ out = 0;
+ while (name[i])
+ {
+ /* Skip whitespace. */
+ while (name[i] == ' ' || name[i] == '\t')
+ ++i;
+ /* Copy non-whitespace characters. */
+ while (name[i] && name[i] != ' ' && name[i] != '\t')
+ pfx_name[out++] = name[i++];
+ /* Add a single space after each word -- including the final
+ word. */
+ pfx_name[out++] = ' ';
+ }
+ pfx_name[out] = '\0';
+ }
+
if (PyObject_HasAttr (self, gdbpy_doc_cst))
{
PyObject *ds_obj = PyObject_GetAttr (self, gdbpy_doc_cst);
@@ -402,11 +443,23 @@ cmdpy_init (PyObject *self, PyObject *args, PyObject *kwds)
TRY_CATCH (except, RETURN_MASK_ALL)
{
- struct cmd_list_element *cmd = add_cmd (cmd_name,
- (enum command_class) cmdtype,
- NULL,
- docstring,
- cmd_list);
+ struct cmd_list_element *cmd;
+
+ if (pfx_name)
+ {
+ int allow_unknown;
+
+ /* If we have our own "invoke" method, then allow unknown
+ sub-commands. */
+ allow_unknown = PyObject_HasAttr (self, invoke_cst);
+ cmd = add_prefix_cmd (cmd_name, (enum command_class) cmdtype,
+ NULL, docstring, &obj->sub_list,
+ pfx_name, allow_unknown, cmd_list);
+ }
+ else
+ cmd = add_cmd (cmd_name, (enum command_class) cmdtype, NULL,
+ docstring, cmd_list);
+
/* There appears to be no API to set this. */
cmd->func = cmdpy_function;
cmd->destroyer = cmdpy_destroyer;
@@ -420,6 +473,7 @@ cmdpy_init (PyObject *self, PyObject *args, PyObject *kwds)
{
xfree (cmd_name);
xfree (docstring);
+ xfree (pfx_name);
Py_DECREF (self);
PyErr_Format (except.reason == RETURN_QUIT
? PyExc_KeyboardInterrupt : PyExc_RuntimeError,