2008-04-29 Tom Tromey * Makefile.in (SUBDIR_PYTHON_OBS): Add python-cmd.o. (SUBDIR_PYTHON_SRCS): Add python/cmd.c. (python-cmd.o): New target. * cli/cli-cmds.c (edit_command): Delete log10 code. * cli/cli-decode.c (delete_cmd): Rewrote. Handle 'destroyer' field. (add_cmd): Initialize 'destroyer'. * cli/cli-decode.h (struct cmd_list_element): Added 'destroyer' field. * python/cmd.c: New file. * python/python-internal.h (gdbpy_initialize_commands): Declare. * python/python.c (demand_python): Call gdbpy_initialize_commands. Index: tromey.git/gdb/Makefile.in =================================================================== --- tromey.git.orig/gdb/Makefile.in 2008-04-29 11:05:15.000000000 -0300 +++ tromey.git/gdb/Makefile.in 2008-04-29 11:05:20.000000000 -0300 @@ -263,11 +263,13 @@ SUBDIR_TUI_CFLAGS= \ SUBDIR_PYTHON_OBS = \ python.o \ python-breakpoint.o \ + python-cmd.o \ python-hooks.o \ python-value.o SUBDIR_PYTHON_SRCS = \ python/python.c \ python/breakpoint.c \ + python/cmd.c \ python/hooks.c \ python/value.c SUBDIR_PYTHON_DEPS = @@ -3422,6 +3424,11 @@ python-breakpoint.o: $(srcdir)/python/br $(breakpoint_h) $(gdbcmd_h) $(CC) -c $(INTERNAL_CFLAGS) $(PYTHON_CFLAGS) \ $(srcdir)/python/breakpoint.c -o python-breakpoint.o +python-cmd.o: $(srcdir)/python/cmd.c $(defs_h) $(python_h) $(value_h) \ + $(exceptions_h) $(python_internal_h) $(charset_h) \ + $(gdbcmd_h) $(cli_decode_h) + $(CC) -c $(INTERNAL_CFLAGS) $(PYTHON_CFLAGS) \ + $(srcdir)/python/cmd.c -o python-cmd.o python-hooks.o: $(srcdir)/python/hooks.c $(defs_h) $(cli_decode_h) \ $(charset_h) $(gdb_events_h) $(python_h) $(python_internal_h) $(CC) -c $(INTERNAL_CFLAGS) $(PYTHON_CFLAGS) \ Index: tromey.git/gdb/cli/cli-cmds.c =================================================================== --- tromey.git.orig/gdb/cli/cli-cmds.c 2008-04-29 10:57:39.000000000 -0300 +++ tromey.git/gdb/cli/cli-cmds.c 2008-04-29 11:05:20.000000000 -0300 @@ -615,8 +615,7 @@ edit_command (char *arg, int from_tty) struct symtab_and_line sal; struct symbol *sym; char *arg1; - int cmdlen, log10; - unsigned m; + int cmdlen; char *editor; char *p, *fn; @@ -692,10 +691,6 @@ edit_command (char *arg, int from_tty) if ((editor = (char *) getenv ("EDITOR")) == NULL) editor = "/bin/ex"; - /* Approximate base-10 log of line to 1 unit for digit count */ - for(log10=32, m=0x80000000; !(sal.line & m) && log10>0; log10--, m=m>>1); - log10 = 1 + (int)((log10 + (0 == ((m-1) & sal.line)))/3.32192809); - /* If we don't already know the full absolute file name of the source file, find it now. */ if (!sal.symtab->fullname) Index: tromey.git/gdb/cli/cli-decode.c =================================================================== --- tromey.git.orig/gdb/cli/cli-decode.c 2008-04-29 10:57:39.000000000 -0300 +++ tromey.git/gdb/cli/cli-decode.c 2008-04-29 11:05:20.000000000 -0300 @@ -190,6 +190,7 @@ add_cmd (char *name, enum command_class c->allow_unknown = 0; c->abbrev_flag = 0; set_cmd_completer (c, make_symbol_completion_list); + c->destroyer = NULL; c->type = not_set_cmd; c->var = NULL; c->var_type = var_boolean; @@ -614,37 +615,32 @@ add_setshow_zinteger_cmd (char *name, en void delete_cmd (char *name, struct cmd_list_element **list) { - struct cmd_list_element *c; - struct cmd_list_element *p; + struct cmd_list_element *iter; + struct cmd_list_element **previous_chain_ptr; + + previous_chain_ptr = list; - while (*list && strcmp ((*list)->name, name) == 0) + for (iter = *previous_chain_ptr; iter; iter = *previous_chain_ptr) { - if ((*list)->hookee_pre) - (*list)->hookee_pre->hook_pre = 0; /* Hook slips out of its mouth */ - if ((*list)->hookee_post) - (*list)->hookee_post->hook_post = 0; /* Hook slips out of its bottom */ - p = (*list)->next; - xfree (* list); - *list = p; - } + if (strcmp (iter->name, name) == 0) + { + if (iter->destroyer) + iter->destroyer (iter, iter->context); + if (iter->hookee_pre) + iter->hookee_pre->hook_pre = 0; + if (iter->hookee_post) + iter->hookee_post->hook_post = 0; + + /* Update the link. Note that we don't change + previous_chain_ptr; next time through the loop this must + stay the same. */ + *previous_chain_ptr = iter->next; - if (*list) - for (c = *list; c->next;) - { - if (strcmp (c->next->name, name) == 0) - { - if (c->next->hookee_pre) - c->next->hookee_pre->hook_pre = 0; /* hooked cmd gets away. */ - if (c->next->hookee_post) - c->next->hookee_post->hook_post = 0; /* remove post hook */ - /* :( no fishing metaphore */ - p = c->next->next; - xfree (c->next); - c->next = p; - } - else - c = c->next; - } + xfree (iter); + } + else + previous_chain_ptr = &iter->next; + } } /* Shorthands to the commands above. */ Index: tromey.git/gdb/cli/cli-decode.h =================================================================== --- tromey.git.orig/gdb/cli/cli-decode.h 2008-04-29 10:57:39.000000000 -0300 +++ tromey.git/gdb/cli/cli-decode.h 2008-04-29 11:05:20.000000000 -0300 @@ -168,6 +168,11 @@ struct cmd_list_element "oobar"; if WORD is "baz/foo", return "baz/foobar". */ char **(*completer) (char *text, char *word); + /* Destruction routine for this command. If non-NULL, this is + called when this command instance is destroyed. This may be + used to finalize the CONTEXT field, if needed. */ + void (*destroyer) (struct cmd_list_element *self, void *context); + /* Type of "set" or "show" command (or SET_NOT_SET if not "set" or "show"). */ cmd_types type; Index: tromey.git/gdb/python/cmd.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ tromey.git/gdb/python/cmd.c 2008-04-29 11:05:40.000000000 -0300 @@ -0,0 +1,255 @@ +/* gdb commands implemented in Python + + Copyright (C) 2008 Free Software Foundation, Inc. + + This file is part of GDB. + + 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 . */ + + +#include "defs.h" +#include "value.h" +#include "exceptions.h" +#include "python-internal.h" +#include "charset.h" +#include "gdbcmd.h" +#include "cli/cli-decode.h" + +/* A gdb command. For the time being only ordinary commands (not + set/show commands) are allowed. */ +struct cmdpy_object +{ + PyObject_HEAD + + /* 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; +}; + +typedef struct cmdpy_object cmdpy_object; + +static PyObject *cmdpy_dont_repeat (PyObject *self, PyObject *args); + +static PyMethodDef cmdpy_object_methods[] = +{ + { "dont_repeat", cmdpy_dont_repeat, METH_NOARGS, + "Prevent command repetition when user enters empty line." }, + + { 0 } +}; + +static PyTypeObject cmdpy_object_type = +{ + PyObject_HEAD_INIT (NULL) + 0, /*ob_size*/ + "gdb.Command", /*tp_name*/ + sizeof (cmdpy_object), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + 0, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "GDB command object", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + cmdpy_object_methods /* tp_methods */ +}; + + + +static PyObject * +cmdpy_dont_repeat (PyObject *self, PyObject *args) +{ + dont_repeat (); + Py_RETURN_NONE; +} + + + + +/* Called if the gdb cmd_list_element is destroyed. */ +static void +cmdpy_destroyer (struct cmd_list_element *self, void *context) +{ + /* Release our hold on the command object. */ + cmdpy_object *cmd = (cmdpy_object *) context; + cmd->command = NULL; + Py_DECREF (cmd); + + /* We allocated the name and doc string. */ + xfree (self->name); + xfree (self->doc); +} + +/* Called by gdb to invoke the command. */ +static void +cmdpy_function (struct cmd_list_element *command, char *args, int from_tty) +{ + cmdpy_object *obj = (cmdpy_object *) get_cmd_context (command); + PyObject *method, *argobj, *ttyobj, *result; + + if (! obj) + error ("Invalid invocation of Python command object"); + if (! PyObject_HasAttrString ((PyObject *) obj, "invoke")) + error ("Python command object missing 'invoke' method"); + + method = PyString_FromString ("invoke"); + if (! args) + { + argobj = Py_None; + Py_INCREF (argobj); + } + else + { + argobj = PyString_FromString (args); + if (! argobj) + error ("Couldn't convert arguments to Python string"); + } + ttyobj = from_tty ? Py_True : Py_False; + Py_INCREF (ttyobj); + result = PyObject_CallMethodObjArgs ((PyObject *) obj, method, argobj, + ttyobj, NULL); + Py_DECREF (method); + Py_DECREF (argobj); + Py_DECREF (ttyobj); + if (! result) + { + /* FIXME: pretty lame. */ + PyErr_Print (); + error ("Error occurred in Python command"); + } + Py_DECREF (result); +} + +/* Object initializer; sets up gdb-side structures for command. + + Use: __init__(NAME, CMDCLASS). + + NAME is the name of the command; spaces in the name mean to look up + initial segments as prefixes. + + CMDCLASS is the kind of command. It should be one of the COMMAND_* + constants defined in the gdb module. + + The documentation for the command is taken from the doc string for + the python class. + +*/ +static int +cmdpy_init (PyObject *self, PyObject *args, PyObject *kwds) +{ + cmdpy_object *obj = (cmdpy_object *) self; + char *name; + int cmdtype; + volatile struct gdb_exception except; + + if (obj->command) + { + /* Note: this is apparently not documented in Python. We return + 0 for success, -1 for failure. */ + PyErr_Format (PyExc_RuntimeError, "Command object already initialized"); + return -1; + } + + if (! PyArg_ParseTuple (args, "si", &name, &cmdtype)) + return -1; + + if (cmdtype != no_class && cmdtype != class_run + && cmdtype != class_vars && cmdtype != class_stack + && cmdtype != class_files && cmdtype != class_support + && cmdtype != class_info && cmdtype != class_breakpoint + && cmdtype != class_trace && cmdtype != class_obscure + && cmdtype != class_maintenance) + { + PyErr_Format (PyExc_RuntimeError, "invalid command class argument"); + return -1; + } + + TRY_CATCH (except, RETURN_MASK_ALL) + { + /* FIXME: decoding NAME if it has spaces. Also, how to add new + prefix commands? */ + struct cmd_list_element *cmd = add_cmd (xstrdup (name), + (enum command_class) cmdtype, + NULL, + xstrdup ("FIXME: docstring"), + &cmdlist /* FIXME */); + /* There appears to be no API to set this. */ + cmd->func = cmdpy_function; + /* FIXME: route completion requests through user object. */ + + obj->command = cmd; + Py_INCREF (self); + set_cmd_context (cmd, self); + } + if (except.reason < 0) + { + PyErr_Format (except.reason == RETURN_QUIT + ? PyExc_KeyboardInterrupt : PyExc_RuntimeError, + "%s", except.message); + return -1; + } + return 0; +} + + + +void +gdbpy_initialize_commands (void) +{ + cmdpy_object_type.tp_new = PyType_GenericNew; + cmdpy_object_type.tp_init = cmdpy_init; + if (PyType_Ready (&cmdpy_object_type) < 0) + return; + + /* Note: alias and user seem to be special; pseudo appears to be + unused, and there is no reason to expose tui or xdb, I think. */ + if (PyModule_AddIntConstant (gdb_module, "COMMAND_NONE", no_class) < 0 + || PyModule_AddIntConstant (gdb_module, "COMMAND_RUN", class_run) < 0 + || PyModule_AddIntConstant (gdb_module, "COMMAND_VARS", class_vars) < 0 + || PyModule_AddIntConstant (gdb_module, "COMMAND_STACK", class_stack) < 0 + || PyModule_AddIntConstant (gdb_module, "COMMAND_FILES", class_files) < 0 + || PyModule_AddIntConstant (gdb_module, "COMMAND_SUPPORT", + class_support) < 0 + || PyModule_AddIntConstant (gdb_module, "COMMAND_INFO", class_info) < 0 + || PyModule_AddIntConstant (gdb_module, "COMMAND_BREAKPOINT", + class_breakpoint) < 0 + || PyModule_AddIntConstant (gdb_module, "COMMAND_TRACE", class_trace) < 0 + || PyModule_AddIntConstant (gdb_module, "COMMAND_OBSCURE", + class_obscure) < 0 + || PyModule_AddIntConstant (gdb_module, "COMMAND_MAINTENANCE", + class_maintenance) < 0) + return; + + Py_INCREF (&cmdpy_object_type); + PyModule_AddObject (gdb_module, "Command", + (PyObject *) &cmdpy_object_type); +} Index: tromey.git/gdb/python/python-internal.h =================================================================== --- tromey.git.orig/gdb/python/python-internal.h 2008-04-29 11:05:15.000000000 -0300 +++ tromey.git/gdb/python/python-internal.h 2008-04-29 11:05:20.000000000 -0300 @@ -44,6 +44,7 @@ PyObject *gdbpy_get_hook_function (const void gdbpy_initialize_values (void); void gdbpy_initialize_hooks (void); void gdbpy_initialize_breakpoints (void); +void gdbpy_initialize_commands (void); /* Use this after a TRY_EXCEPT to throw the appropriate Python exception. FIXME: throw different errors depending on Index: tromey.git/gdb/python/python.c =================================================================== --- tromey.git.orig/gdb/python/python.c 2008-04-29 11:05:15.000000000 -0300 +++ tromey.git/gdb/python/python.c 2008-04-29 11:05:20.000000000 -0300 @@ -70,6 +70,7 @@ demand_python () gdbpy_initialize_hooks (); gdbpy_initialize_values (); gdbpy_initialize_breakpoints (); + gdbpy_initialize_commands (); PyRun_SimpleString ("import gdb"); -- -- []'s Thiago Jung Bauermann Software Engineer IBM Linux Technology Center