This is the mail archive of the gdb-cvs@sourceware.org mailing list for the GDB 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]

[binutils-gdb] Add new command to create extra console/mi UIs


https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=60eb5395fa7a7b8e3cd1841e38b6d1a0c16be0d0

commit 60eb5395fa7a7b8e3cd1841e38b6d1a0c16be0d0
Author: Pedro Alves <palves@redhat.com>
Date:   Tue Jun 21 01:11:55 2016 +0100

    Add new command to create extra console/mi UIs
    
    With all the previous plumbing in place, it's now easy to add a
    command that actually creates a new console/mi UI.
    
    The intended use case is to make it possible and easy for MI frontends
    to provide a fully featured GDB console to users, with readline
    support, command line editing, history, etc., just like if gdb was
    started on the command line.  Currently MI frontends have to try to
    implement all of that theirselves and make use of "-interpreter-exec
    console ...", which is far from perfect.  If you ever tried Eclipse's
    gdb console window, you'll know what I mean...
    
    Instead of trying to multiplex console through MI, this command let's
    just leverage all the built in readline/editing support already inside
    gdb.
    
    The plan is for the MI frontend to start GDB in regular console mode,
    running inside a terminal emulator widget embedded in Eclipse (which
    already exists, for supporting the shell widget; other frontends have
    similar widgets), and then tell GDB to run a full MI interpreter on an
    specified input/output device, independent of the console.
    
    My original prototype planned to do things the other way around --
    start GDB in MI mode, and then start an extra CLI console on separate
    tty.  I handed over that prototype to Marc Khouzam @ Eclipse CDT, and
    after experimentation and discussion, we ended up concluding that
    starting GDB in CLI mode instead was both easier and actually also
    supported an interesting use case -- connect an Eclipse frontend to a
    GDB that is already running outside Eclipse.
    
    The current usage is "new-ui <interpreter> <tty>".
    
    E.g., on a terminal run this scriplet:
    
     $ cat gdb-client
     #!/bin/bash
    
     reset
     tty
     tail -f /dev/null
    
     $ gdb-client
     /dev/pts/15
    
    Now run gdb on another terminal, and tell it to start a MI interpreter
    on the tty of the other terminal:
    
     ...
     (gdb) new-ui mi /dev/pts/15
     New UI allocated
    
    Now back to the the gdb-client terminal, we'll get an MI prompt, ready
    for MI input:
    
     /dev/pts/15
     =thread-group-added,id="i1"
     (gdb)
    
    You can also start a new UI running a CLI, with:
    
     (gdb) new-ui console /dev/pts/15
    
    Though note that this console won't support readline command editing.
    It works as if "set editing off" was entered.
    
    gdb/ChangeLog:
    2016-06-21  Pedro Alves  <palves@redhat.com>
    
    	* interps.c (set_top_level_interpreter): New function, factored
    	out from captured_main.
    	(interpreter_completer): Make extern.
    	* interps.h (set_top_level_interpreter, interpreter_completer):
    	New declarations.
    	(captured_main): Use set_top_level_interpreter.
    	* top.c [!O_NOCTTY] (O_NOCTTY): Define as 0.
    	(open_terminal_stream, new_ui_command): New functions.
    	(init_main): Install the "new-ui" command.

Diff:
---
 gdb/ChangeLog | 12 +++++++++
 gdb/interps.c | 20 ++++++++++++--
 gdb/interps.h | 11 ++++++++
 gdb/main.c    | 12 +--------
 gdb/top.c     | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 127 insertions(+), 13 deletions(-)

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index ed1f53a..45e41f1 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,17 @@
 2016-06-21  Pedro Alves  <palves@redhat.com>
 
+	* interps.c (set_top_level_interpreter): New function, factored
+	out from captured_main.
+	(interpreter_completer): Make extern.
+	* interps.h (set_top_level_interpreter, interpreter_completer):
+	New declarations.
+	(captured_main): Use set_top_level_interpreter.
+	* top.c [!O_NOCTTY] (O_NOCTTY): Define as 0.
+	(open_terminal_stream, new_ui_command): New functions.
+	(init_main): Install the "new-ui" command.
+
+2016-06-21  Pedro Alves  <palves@redhat.com>
+
 	* cli/cli-script.c (read_next_line): Adjust to per-UI stdin.
 	(read_command_lines): Use input_interactive_p instead of
 	input_from_terminal_p.
diff --git a/gdb/interps.c b/gdb/interps.c
index fdf1479..163c837 100644
--- a/gdb/interps.c
+++ b/gdb/interps.c
@@ -319,6 +319,21 @@ interp_lookup (struct ui *ui, const char *name)
   return NULL;
 }
 
+/* See interps.h.  */
+
+void
+set_top_level_interpreter (const char *name)
+{
+  /* Find it.  */
+  struct interp *interp = interp_lookup (current_ui, name);
+
+  if (interp == NULL)
+    error (_("Interpreter `%s' unrecognized"), name);
+  /* Install it.  */
+  if (!interp_set (interp, 1))
+    error (_("Interpreter `%s' failed to initialize."), name);
+}
+
 /* Returns the current interpreter.  */
 
 struct ui_out *
@@ -550,8 +565,9 @@ interpreter_exec_cmd (char *args, int from_tty)
   do_cleanups (cleanup);
 }
 
-/* List the possible interpreters which could complete the given text.  */
-static VEC (char_ptr) *
+/* See interps.h.  */
+
+VEC (char_ptr) *
 interpreter_completer (struct cmd_list_element *ignore,
 		       const char *text, const char *word)
 {
diff --git a/gdb/interps.h b/gdb/interps.h
index 4ac0845..97d510f 100644
--- a/gdb/interps.h
+++ b/gdb/interps.h
@@ -95,6 +95,11 @@ extern int interp_set (struct interp *interp, int top_level);
    the interpreter.  */
 extern struct interp *interp_lookup (struct ui *ui, const char *name);
 
+/* Set the current UI's top level interpreter to the interpreter named
+   NAME.  Throws an error if NAME is not a known interpreter or the
+   interpreter fails to initialize.  */
+extern void set_top_level_interpreter (const char *name);
+
 extern struct ui_out *interp_ui_out (struct interp *interp);
 extern void *interp_data (struct interp *interp);
 extern const char *interp_name (struct interp *interp);
@@ -131,6 +136,12 @@ extern int interp_supports_command_editing (struct interp *interp);
    chance to e.g., print a prompt.  */
 extern void interp_pre_command_loop (struct interp *interp);
 
+/* List the possible interpreters which could complete the given
+   text.  */
+extern VEC (char_ptr) *interpreter_completer (struct cmd_list_element *ignore,
+					      const char *text,
+					      const char *word);
+
 /* well-known interpreters */
 #define INTERP_CONSOLE		"console"
 #define INTERP_MI1             "mi1"
diff --git a/gdb/main.c b/gdb/main.c
index 947ed55..5477379 100644
--- a/gdb/main.c
+++ b/gdb/main.c
@@ -963,17 +963,7 @@ captured_main (void *data)
 
   /* Install the default UI.  All the interpreters should have had a
      look at things by now.  Initialize the default interpreter.  */
-
-  {
-    /* Find it.  */
-    struct interp *interp = interp_lookup (current_ui, interpreter_p);
-
-    if (interp == NULL)
-      error (_("Interpreter `%s' unrecognized"), interpreter_p);
-    /* Install it.  */
-    if (!interp_set (interp, 1))
-      error (_("Interpreter `%s' failed to initialize."), interpreter_p);
-  }
+  set_top_level_interpreter (interpreter_p);
 
   /* FIXME: cagney/2003-02-03: The big hack (part 2 of 2) that lets
      GDB retain the old MI1 interpreter startup behavior.  Output the
diff --git a/gdb/top.c b/gdb/top.c
index 7506c45..3174f3c 100644
--- a/gdb/top.c
+++ b/gdb/top.c
@@ -74,6 +74,10 @@
 # include "tui/tui.h"
 #endif
 
+#ifndef O_NOCTTY
+# define O_NOCTTY 0
+#endif
+
 extern void initialize_all_files (void);
 
 #define PROMPT(X) the_prompts.prompt_stack[the_prompts.top + X].prompt
@@ -320,6 +324,79 @@ delete_ui (struct ui *todel)
   free_ui (ui);
 }
 
+/* Open file named NAME for read/write, making sure not to make it the
+   controlling terminal.  */
+
+static FILE *
+open_terminal_stream (const char *name)
+{
+  int fd;
+
+  fd = open (name, O_RDWR | O_NOCTTY);
+  if (fd < 0)
+    perror_with_name  (_("opening terminal failed"));
+
+  return fdopen (fd, "w+");
+}
+
+/* Implementation of the "new-ui" command.  */
+
+static void
+new_ui_command (char *args, int from_tty)
+{
+  struct ui *ui;
+  struct interp *interp;
+  FILE *stream[3] = { NULL, NULL, NULL };
+  int i;
+  int res;
+  int argc;
+  char **argv;
+  const char *interpreter_name;
+  const char *tty_name;
+  struct cleanup *back_to;
+  struct cleanup *streams_chain;
+
+  dont_repeat ();
+
+  argv = gdb_buildargv (args);
+  back_to = make_cleanup_freeargv (argv);
+  argc = countargv (argv);
+
+  if (argc < 2)
+    error (_("usage: new-ui <interpreter> <tty>"));
+
+  interpreter_name = argv[0];
+  tty_name = argv[1];
+
+  streams_chain = make_cleanup (null_cleanup, NULL);
+
+  /* Open specified terminal, once for each of
+     stdin/stdout/stderr.  */
+  for (i = 0; i < 3; i++)
+    {
+      stream[i] = open_terminal_stream (tty_name);
+      make_cleanup_fclose (stream[i]);
+    }
+
+  ui = new_ui (stream[0], stream[1], stream[2]);
+
+  discard_cleanups (streams_chain);
+
+  ui->async = 1;
+
+  make_cleanup (restore_ui_cleanup, current_ui);
+  current_ui = ui;
+
+  set_top_level_interpreter (interpreter_name);
+
+  interp_pre_command_loop (top_level_interpreter ());
+
+  /* This restores the previous UI.  */
+  do_cleanups (back_to);
+
+  printf_unfiltered ("New UI allocated\n");
+}
+
 /* Handler for SIGHUP.  */
 
 #ifdef SIGHUP
@@ -1923,6 +2000,8 @@ set_history_filename (char *args, int from_tty, struct cmd_list_element *c)
 static void
 init_main (void)
 {
+  struct cmd_list_element *c;
+
   /* Initialize the prompt to a simple "(gdb) " prompt or to whatever
      the DEFAULT_PROMPT is.  */
   set_prompt (DEFAULT_PROMPT);
@@ -2060,6 +2139,12 @@ input settings."),
                         NULL,
                         show_interactive_mode,
                         &setlist, &showlist);
+
+  c = add_cmd ("new-ui", class_support, new_ui_command, _("\
+Create a new UI.  It takes two arguments:\n\
+The first argument is the name of the interpreter to run.\n\
+The second argument is the terminal the UI runs on.\n"), &cmdlist);
+  set_cmd_completer (c, interpreter_completer);
 }
 
 void


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