This is the mail archive of the archer@sourceware.org mailing list for the Archer project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[python] allow prefix commands


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,


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]