2011-07-21 Phil Muldoon <pmuldoon@redhat.com>
authorPhil Muldoon <pmuldoon@redhat.com>
Thu, 21 Jul 2011 11:03:48 +0000 (11:03 +0000)
committerPhil Muldoon <pmuldoon@redhat.com>
Thu, 21 Jul 2011 11:03:48 +0000 (11:03 +0000)
            Tom Tromey  <tromey@redhat.com>

* top.c (set_prompt): Rewrite to free previous prompt, free
asynch_new_prompt and set both on new prompts.
* event-top.c (display_gdb_prompt): Add prompt substitution
logic.
* python/python.c (before_prompt_hook): New function.

2011-07-21  Phil Muldoon  <pmuldoon@redhat.com>

* gdb.python/python.exp: Add prompt substitution tests.

2011-07-21  Phil Muldoon  <pmuldoon@redhat.com>

* observer.texi (GDB Observers): Add before_prompt observer.
* gdb.texinfo (Basic Python): Add documentation for prompt
substitution.

gdb/ChangeLog
gdb/doc/ChangeLog
gdb/doc/gdb.texinfo
gdb/doc/observer.texi
gdb/event-top.c
gdb/python/python.c
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.python/python.exp
gdb/top.c
gdb/top.h

index 5e4ab8c8e870177c359e92d0d94e2382d815f348..7b0b3b0bb660509d27ea32cd0cba32d2b04c71d6 100644 (file)
@@ -1,3 +1,12 @@
+2011-07-21  Phil Muldoon  <pmuldoon@redhat.com>
+            Tom Tromey  <tromey@redhat.com>
+
+       * top.c (set_prompt): Rewrite to free previous prompt, free
+       asynch_new_prompt and set both on new prompts.
+       * event-top.c (display_gdb_prompt): Add prompt substitution
+       logic.
+       * python/python.c (before_prompt_hook): New function.
+
 2011-07-20  Matt Rice  <ratmice@gmail.com>
 
        * bfin-tdep.c (bfin_extract_return_value): Fix swapped
index 4a2d015d2b3da18875eecbda66ec05c304b6622f..e3e02c8441f25a9a450dc752b04ad3bf5a3388d4 100644 (file)
@@ -1,3 +1,9 @@
+2011-07-21  Phil Muldoon  <pmuldoon@redhat.com>
+
+       * observer.texi (GDB Observers): Add before_prompt observer.
+       * gdb.texinfo (Basic Python): Add documentation for prompt
+       substitution.
+
 2011-07-11  Phil Muldoon  <pmuldoon@redhat.com>
 
         PR python/12438
index 3a3a9fbf1974e7cbbd1522d6405a54cd8f3b42aa..5b26bbbd7d64fce109da67d8104cd45fb8740193 100644 (file)
@@ -21080,6 +21080,22 @@ provided, it is decoded the way that @value{GDBN}'s inbuilt
 @code{break} or @code{edit} commands do (@pxref{Specify Location}).
 @end defun
 
+@defop Operation {@value{GDBN}} prompt_hook current_prompt
+If @var{prompt_hook} is callable, @value{GDBN} will call the method
+assigned to this operation before a prompt is displayed by
+@value{GDBN}.
+
+The parameter @code{current_prompt} contains the current @value{GDBN} 
+prompt.  This method must return a Python string, or @code{None}.  If
+a string is returned, the @value{GDBN} prompt will be set to that
+string.  If @code{None} is returned, @value{GDBN} will continue to use
+the current prompt.
+
+Some prompts cannot be substituted in @value{GDBN}.  Secondary prompts
+such as those used by readline for command input, and annotation
+related prompts are prohibited from being changed.
+@end defop
+
 @node Exception Handling
 @subsubsection Exception Handling
 @cindex python exceptions
index d8c392478946b6f9438b0698ee91b50cea042846..689b3039f4c3e1fb4864efaaa84f1b339ee59e92 100644 (file)
@@ -222,6 +222,11 @@ Bytes from @var{data} to @var{data} + @var{len} have been written
 to the current inferior at @var{addr}.
 @end deftypefun
 
+@deftypefun void before_prompt (const char *@var{current_prompt})
+Called before a top-level prompt is displayed.  @var{current_prompt} is
+the current top-level prompt.
+@end deftypefun
+
 @deftypefun void test_notification (int @var{somearg})
 This observer is used for internal testing.  Do not use.  
 See testsuite/gdb.gdb/observer.exp.
index 72cbfdcfc6e98169ee6768fcd97cf5d3adc9b7a6..22f94409acff7dc516b570a272602d5d324991ac 100644 (file)
@@ -33,6 +33,7 @@
 #include "cli/cli-script.h"     /* for reset_command_nest_depth */
 #include "main.h"
 #include "gdbthread.h"
+#include "observer.h"
 #include "continuations.h"
 #include "gdbcmd.h"            /* for dont_repeat() */
 
@@ -258,7 +259,7 @@ void
 display_gdb_prompt (char *new_prompt)
 {
   int prompt_length = 0;
-  char *gdb_prompt = get_prompt ();
+  char *actual_gdb_prompt = NULL;
 
   /* Reset the nesting depth used when trace-commands is set.  */
   reset_command_nest_depth ();
@@ -268,6 +269,25 @@ display_gdb_prompt (char *new_prompt)
   if (!current_interp_display_prompt_p ())
     return;
 
+  /* Get the prompt before the observers are called as observer hook
+     functions may change the prompt.  Do not call observers on an
+     explicit prompt change as passed to this function, as this forms
+     a temporary prompt, IE, displayed but not set.  */
+  if (! new_prompt)
+    {
+      char *post_gdb_prompt = NULL;
+      char *pre_gdb_prompt = xstrdup (get_prompt ());
+
+      observer_notify_before_prompt (pre_gdb_prompt);
+      post_gdb_prompt = get_prompt ();
+
+      /* If the observer changed the prompt, use that prompt.  */
+      if (strcmp (pre_gdb_prompt, post_gdb_prompt) != 0)
+       actual_gdb_prompt = post_gdb_prompt;
+
+      xfree (pre_gdb_prompt);
+    }
+
   if (sync_execution && is_running (inferior_ptid))
     {
       /* This is to trick readline into not trying to display the
@@ -289,27 +309,35 @@ display_gdb_prompt (char *new_prompt)
       return;
     }
 
-  if (!new_prompt)
+  /* If the observer changed the prompt, ACTUAL_GDB_PROMPT will not be
+     NULL.  Otherwise, either copy the existing prompt, or set it to
+     NEW_PROMPT.  */
+  if (! actual_gdb_prompt)
     {
-      /* Just use the top of the prompt stack.  */
-      prompt_length = strlen (PREFIX (0)) +
-       strlen (SUFFIX (0)) +
-       strlen (gdb_prompt) + 1;
-
-      new_prompt = (char *) alloca (prompt_length);
-
-      /* Prefix needs to have new line at end.  */
-      strcpy (new_prompt, PREFIX (0));
-      strcat (new_prompt, gdb_prompt);
-      /* Suffix needs to have a new line at end and \032 \032 at
-         beginning.  */
-      strcat (new_prompt, SUFFIX (0));
+      if (! new_prompt)
+       {
+         /* Just use the top of the prompt stack.  */
+         prompt_length = strlen (PREFIX (0)) +
+           strlen (SUFFIX (0)) +
+           strlen (get_prompt()) + 1;
+
+         actual_gdb_prompt = (char *) alloca (prompt_length);
+
+         /* Prefix needs to have new line at end.  */
+         strcpy (actual_gdb_prompt, PREFIX (0));
+         strcat (actual_gdb_prompt, get_prompt());
+         /* Suffix needs to have a new line at end and \032 \032 at
+            beginning.  */
+         strcat (actual_gdb_prompt, SUFFIX (0));
+       }
+      else
+       actual_gdb_prompt = new_prompt;;
     }
 
   if (async_command_editing_p)
     {
       rl_callback_handler_remove ();
-      rl_callback_handler_install (new_prompt, input_handler);
+      rl_callback_handler_install (actual_gdb_prompt, input_handler);
     }
   /* new_prompt at this point can be the top of the stack or the one
      passed in.  It can't be NULL.  */
@@ -318,7 +346,7 @@ display_gdb_prompt (char *new_prompt)
       /* Don't use a _filtered function here.  It causes the assumed
          character position to be off, since the newline we read from
          the user is not accounted for.  */
-      fputs_unfiltered (new_prompt, gdb_stdout);
+      fputs_unfiltered (actual_gdb_prompt, gdb_stdout);
       gdb_flush (gdb_stdout);
     }
 }
index f15936dae3c049a715c675a1c178b6c95f40157e..e3a8d195e456f57d71fdcaca5dc0355b0e584f58 100644 (file)
@@ -51,6 +51,7 @@ static int gdbpy_should_print_stack = 0;
 #include "version.h"
 #include "target.h"
 #include "gdbthread.h"
+#include "observer.h"
 
 static PyMethodDef GdbMethods[];
 
@@ -682,6 +683,81 @@ gdbpy_initialize_events (void)
     }
 }
 
+\f
+
+static void
+before_prompt_hook (const char *current_gdb_prompt)
+{
+  struct cleanup *cleanup;
+  char *prompt = NULL;
+
+  cleanup = ensure_python_env (get_current_arch (), current_language);
+
+  if (PyObject_HasAttrString (gdb_module, "prompt_hook"))
+    {
+      PyObject *hook;
+
+      hook = PyObject_GetAttrString (gdb_module, "prompt_hook");
+      if (hook == NULL)
+       goto fail;
+
+      if (PyCallable_Check (hook))
+       {
+         PyObject *result;
+         PyObject *current_prompt;
+
+         current_prompt = PyString_FromString (current_gdb_prompt);
+         if (current_prompt == NULL)
+           goto fail;
+
+         result = PyObject_CallFunctionObjArgs (hook, current_prompt, NULL);
+
+         Py_DECREF (current_prompt);
+
+         if (result == NULL)
+           goto fail;
+
+         make_cleanup_py_decref (result);
+
+         /* Return type should be None, or a String.  If it is None,
+            fall through, we will not set a prompt.  If it is a
+            string, set  PROMPT.  Anything else, set an exception.  */
+         if (result != Py_None && ! PyString_Check (result))
+           {
+             PyErr_Format (PyExc_RuntimeError,
+                           _("Return from prompt_hook must " \
+                             "be either a Python string, or None"));
+             goto fail;
+           }
+
+         if (result != Py_None)
+           {
+             prompt = python_string_to_host_string (result);
+
+             if (prompt == NULL)
+               goto fail;
+             else
+               make_cleanup (xfree, prompt);
+           }
+       }
+    }
+
+  /* If a prompt has been set, PROMPT will not be NULL.  If it is
+     NULL, do not set the prompt.  */
+  if (prompt != NULL)
+    set_prompt (prompt);
+
+  do_cleanups (cleanup);
+  return;
+
+ fail:
+  gdbpy_print_stack ();
+  do_cleanups (cleanup);
+  return;
+}
+
+\f
+
 /* Printing.  */
 
 /* A python function to write a single string using gdb's filtered
@@ -1134,6 +1210,8 @@ Enables or disables printing of Python stack traces."),
   gdbpy_initialize_exited_event ();
   gdbpy_initialize_thread_event ();
 
+  observer_attach_before_prompt (before_prompt_hook);
+
   PyRun_SimpleString ("import gdb");
   PyRun_SimpleString ("gdb.pretty_printers = []");
 
@@ -1236,6 +1314,8 @@ def GdbSetPythonDirectory (dir):\n\
 \n\
 # Install the default gdb.PYTHONDIR.\n\
 GdbSetPythonDirectory (gdb.PYTHONDIR)\n\
+# Default prompt hook does nothing.\n\
+prompt_hook = None\n\
 ");
 
   do_cleanups (cleanup);
index e3b3adb8ec987a51f94148f66ea79d023fb33895..ac815ca20e6a5591536c70c6e073e4074ba4956b 100644 (file)
@@ -1,3 +1,7 @@
+2011-07-21  Phil Muldoon  <pmuldoon@redhat.com>
+
+       * gdb.python/python.exp: Add prompt substitution tests.
+
 2011-07-19  Jan Kratochvil  <jan.kratochvil@redhat.com>
 
        Fix crash if referenced CU is aged out.
index 8906bbc97a36deb0ac060a2d77e2d36661568ca5..832afc0eded0d20ddef3261d5a417094066ae49e 100644 (file)
@@ -193,3 +193,85 @@ gdb_py_test_silent_cmd "set python print-stack on" \
     "Test print-backtrace set setting" 1
 gdb_test "show python print-stack" \
     "Whether Python stack will be printed on error is on.*" \
+
+# Test prompt substituion
+
+gdb_py_test_multiple "prompt substitution" \
+  "python" "" \
+  "someCounter = 0" "" \
+  "def prompt(current):" "" \
+  "   global someCounter" "" \
+  "   if (current == \"testfake \"):" "" \
+  "      return None" "" \
+  "   someCounter = someCounter + 1" "" \
+  "   return \"py prompt \" + str (someCounter) + \" \"" "" \
+  "end" ""
+
+gdb_py_test_multiple "prompt substitution readline" \
+  "python" "" \
+  "pCounter = 0" "" \
+  "def program_prompt(current):" "" \
+  "   global pCounter" "" \
+  "   if (current == \">\"):" "" \
+  "      pCounter = pCounter + 1" "" \
+  "      return \"python line \" + str (pCounter) + \": \"" "" \
+  "   return None" "" \
+  "end" ""
+
+set newprompt "py prompt 1"
+set newprompt2 "py prompt 2"
+set testfake "testfake"
+
+gdb_test_multiple "python gdb.prompt_hook = prompt" "set the hook" {
+    -re "\[\r\n\]$newprompt $" {
+       pass "set hook"
+    }
+}
+
+gdb_test_multiple "set prompt testfake " "set testfake prompt in GDB" {
+    -re "\[\r\n\]$testfake $" {
+       pass "set prompt testfake"
+    }
+}
+
+gdb_test_multiple "show prompt" "show testfake prompt" {
+    -re "Gdb's prompt is \"$testfake \"..* $" {
+       pass "show prompt shows guarded prompt"
+    }
+}
+
+gdb_test_multiple "set prompt blah " "set blah in GDB" {
+    -re "\[\r\n\]$newprompt2 $" {
+       pass "set prompt blah overriden"
+    }
+}
+
+gdb_test_multiple "python gdb.prompt_hook = None" "Delete hook" {
+    -re "\[\r\n\]$newprompt2 $" {
+       pass "Delete old hook"
+    }
+}
+
+gdb_test_multiple "set prompt $gdb_prompt " "set default prompt" {
+    -re "\[\r\n\]$gdb_prompt $" {
+       pass "set default prompt"
+    }
+}
+
+gdb_test_multiple "python gdb.prompt_hook = program_prompt" "set the hook" {
+    -re "\[\r\n\]$gdb_prompt $" {
+       pass "set programming hook"
+    }
+}
+
+gdb_test_multiple "python" "test we ignore substituion for seconday prompts" {
+    -re "\r\n>$" {
+       pass "readline secondary are not substituted"
+    }
+}
+
+gdb_test_multiple "end" "end programming" {
+    -re "\[\r\n\]$gdb_prompt $" {
+       pass "end programming"
+    }
+}
index ecb91f2b0efd274bcb50df3cd323096ab01dee6e..3ffd000257544e6d3182bc6f108804a46a75e017 100644 (file)
--- a/gdb/top.c
+++ b/gdb/top.c
@@ -1133,14 +1133,17 @@ get_prompt (void)
 }
 
 void
-set_prompt (char *s)
+set_prompt (const char *s)
 {
-/* ??rehrauer: I don't know why this fails, since it looks as though
-   assignments to prompt are wrapped in calls to xstrdup...
-   if (prompt != NULL)
-     xfree (prompt);
- */
-  PROMPT (0) = xstrdup (s);
+  char *p = xstrdup (s);
+
+  xfree (PROMPT (0));
+  PROMPT (0) = p;
+
+  /* Also, free and set new_async_prompt so prompt changes sync up
+     with set/show prompt.  */
+  xfree (new_async_prompt);
+  new_async_prompt = xstrdup (PROMPT (0));
 }
 \f
 
index 4a6e8fba8702c51220d79d2bec570f4b6fc40cf3..24ec2f20c518bc5149841140832bc74356eb5605 100644 (file)
--- a/gdb/top.h
+++ b/gdb/top.h
@@ -55,7 +55,7 @@ extern char *get_prompt (void);
 
 /* This function copies the specified string into the string that
    is used by gdb for its command prompt.  */
-extern void set_prompt (char *);
+extern void set_prompt (const char *);
 
 /* From random places.  */
 extern int readnow_symbol_files;