+2016-06-21 Pedro Alves <palves@redhat.com>
+
+ PR mi/20034
+ * cli/cli-interp.c: Include cli-interp.h and event-top.h.
+ (cli_interpreter_resume): Pass 1 to gdb_setup_readline. Set the
+ UI's input_handler here.
+ (cli_interpreter_supports_command_editing): New function.
+ (cli_interp_procs): Install it.
+ * cli/cli-interp.h: New file.
+ * event-top.c (async_command_editing_p): Rename to ...
+ (set_editing_cmd_var): ... this.
+ (change_line_handler): Add parameter 'editing', and use it. Bail
+ early if the interpreter doesn't support editing. Don't touch
+ readline state if editing is off.
+ (gdb_rl_callback_handler_remove, gdb_rl_callback_handler_install)
+ (gdb_rl_callback_handler_reinstall): Assert the current UI is the
+ main UI.
+ (display_gdb_prompt): Don't call gdb_rl_callback_handler_remove if
+ not using readline. Check whether the current UI is using command
+ editing instead of checking the async_command_editing_p global.
+ (set_async_editing_command): Delete.
+ (gdb_setup_readline): Add 'editing' parameter. Only allow editing
+ on the main UI. Don't touch readline state if editing is off.
+ (gdb_disable_readline): Don't touch readline state if editing is
+ off.
+ * event-top.h (gdb_setup_readline): Add 'int' parameter.
+ (set_async_editing_command): Delete declaration.
+ (change_line_handler, command_line_handler): Declare.
+ (async_command_editing_p): Rename to ...
+ (set_editing_cmd_var): ... this.
+ * infrun.c (reinstall_readline_callback_handler_cleanup): Check
+ whether the current UI has editing enabled rather than checking
+ the async_command_editing_p global.
+ * interps.c (interp_supports_command_editing): New function.
+ * interps.h (interp_supports_command_editing_ftype): New typedef.
+ (struct interp_procs) <supports_command_editing_proc>: New field.
+ (interp_supports_command_editing): Declare.
+ * mi/mi-interp.c (mi_interpreter_resume): Pass 0 to
+ gdb_setup_readline. Don't clear the async_command_editing_p
+ global. Update comments.
+ * top.c (gdb_readline_wrapper_line, gdb_readline_wrapper): Check
+ whether the current UI has editing enabled rather than checking
+ the async_command_editing_p global. Don't touch readline state if
+ editing is off.
+ (undo_terminal_modifications_before_exit): Switch to the main UI.
+ Unconditionally call gdb_disable_readline.
+ (set_editing): New function.
+ (show_async_command_editing_p): Rename to ...
+ (show_editing): ... this. Show the state of the current UI.
+ (_initialize_top): Adjust.
+ * top.h (struct ui) <command_editing>: New field.
+ * tui/tui-interp.c: Include cli/cli-interp.h.
+ (tui_resume): Pass 1 to gdb_setup_readline. Set the UI's
+ input_handler.
+ (tui_interp_procs): Install
+ cli_interpreter_supports_command_editing.
+ * tui/tui-io.c (tui_getc): Check whether the current UI has
+ editing enabled rather than checking the async_command_editing_p
+ global.
+
2016-06-21 Pedro Alves <palves@redhat.com>
* top.c: Call gen_ret_current_ui_field_ptr for current_uiout.
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "defs.h"
+#include "cli-interp.h"
#include "interps.h"
#include "event-top.h"
#include "ui-out.h"
#include "cli-out.h"
#include "top.h" /* for "execute_command" */
+#include "event-top.h"
#include "infrun.h"
#include "observer.h"
static int
cli_interpreter_resume (void *data)
{
+ struct ui *ui = current_ui;
struct cli_interp *cli = (struct cli_interp *) data;
struct ui_file *stream;
stream = NULL;
}
- gdb_setup_readline ();
+ gdb_setup_readline (1);
+
+ ui->input_handler = command_line_handler;
if (stream != NULL)
cli_out_set_stream (cli->cli_uiout, gdb_stdout);
return result;
}
+int
+cli_interpreter_supports_command_editing (struct interp *interp)
+{
+ return 1;
+}
+
static struct gdb_exception
safe_execute_command (struct ui_out *command_uiout, char *command, int from_tty)
{
cli_interpreter_exec, /* exec_proc */
cli_ui_out, /* ui_out_proc */
NULL, /* set_logging_proc */
- cli_command_loop /* command_loop_proc */
+ cli_command_loop, /* command_loop_proc */
+ cli_interpreter_supports_command_editing, /* supports_command_editing_proc */
};
/* Factory for CLI interpreters. */
--- /dev/null
+/* CLI Definitions for GDB, the GNU debugger.
+
+ Copyright (C) 2016 Free Software Foundation, Inc.
+
+ 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef CLI_INTERP_H
+#define CLI_INTERP_H 1
+
+struct interp;
+
+extern int cli_interpreter_supports_command_editing (struct interp *interp);
+
+#endif
/* readline defines this. */
#undef savestring
-static void command_line_handler (char *rl);
-static void change_line_handler (void);
static char *top_level_prompt (void);
/* Signal handlers. */
ezannoni: as of 1999-04-29 I expect that this
variable will not be used after gdb is changed to use the event
loop as default engine, and event-top.c is merged into top.c. */
-int async_command_editing_p;
+int set_editing_cmd_var;
/* This is used to display the notification of the completion of an
asynchronous execution command. */
therefore bypassing readline, and letting gdb handle the input
itself, via gdb_readline_no_editing_callback. Also it is used in
the opposite case in which the user sets editing on again, by
- restoring readline handling of the input. */
-static void
-change_line_handler (void)
+ restoring readline handling of the input.
+
+ NOTE: this operates on input_fd, not instream. If we are reading
+ commands from a file, instream will point to the file. However, we
+ always read commands from a file with editing off. This means that
+ the 'set editing on/off' will have effect only on the interactive
+ session. */
+
+void
+change_line_handler (int editing)
{
struct ui *ui = current_ui;
- /* NOTE: this operates on input_fd, not instream. If we are reading
- commands from a file, instream will point to the file. However in
- async mode, we always read commands from a file with editing
- off. This means that the 'set editing on/off' will have effect
- only on the interactive session. */
+ /* We can only have one instance of readline, so we only allow
+ editing on the main UI. */
+ if (ui != main_ui)
+ return;
+
+ /* Don't try enabling editing if the interpreter doesn't support it
+ (e.g., MI). */
+ if (!interp_supports_command_editing (top_level_interpreter ())
+ || !interp_supports_command_editing (command_interp ()))
+ return;
- if (async_command_editing_p)
+ if (editing)
{
+ gdb_assert (ui == main_ui);
+
/* Turn on editing by using readline. */
ui->call_readline = gdb_rl_callback_read_char_wrapper;
- ui->input_handler = command_line_handler;
}
else
{
/* Turn off editing by using gdb_readline_no_editing_callback. */
- gdb_rl_callback_handler_remove ();
+ if (ui->command_editing)
+ gdb_rl_callback_handler_remove ();
ui->call_readline = gdb_readline_no_editing_callback;
-
- /* Set up the command handler as well, in case we are called as
- first thing from .gdbinit. */
- ui->input_handler = command_line_handler;
}
+ ui->command_editing = editing;
}
/* The functions below are wrappers for rl_callback_handler_remove and
void
gdb_rl_callback_handler_remove (void)
{
+ gdb_assert (current_ui == main_ui);
+
rl_callback_handler_remove ();
callback_handler_installed = 0;
}
void
gdb_rl_callback_handler_install (const char *prompt)
{
+ gdb_assert (current_ui == main_ui);
+
/* Calling rl_callback_handler_install resets readline's input
buffer. Calling this when we were already processing input
therefore loses input. */
void
gdb_rl_callback_handler_reinstall (void)
{
+ gdb_assert (current_ui == main_ui);
+
if (!callback_handler_installed)
{
/* Passing NULL as prompt argument tells readline to not display
the above two functions. Calling
rl_callback_handler_remove(), does the job. */
- gdb_rl_callback_handler_remove ();
+ if (current_ui->command_editing)
+ gdb_rl_callback_handler_remove ();
do_cleanups (old_chain);
return;
}
else
actual_gdb_prompt = xstrdup (new_prompt);
- if (async_command_editing_p)
+ if (current_ui->command_editing)
{
gdb_rl_callback_handler_remove ();
gdb_rl_callback_handler_install (actual_gdb_prompt);
}
\f
-/* Called by do_setshow_command. */
-void
-set_async_editing_command (char *args, int from_tty,
- struct cmd_list_element *c)
-{
- change_line_handler ();
-}
-
/* Set things up for readline to be invoked via the alternate
interface, i.e. via a callback function
(gdb_rl_callback_read_char), and hook up instream to the event
loop. */
void
-gdb_setup_readline (void)
+gdb_setup_readline (int editing)
{
struct ui *ui = current_ui;
gdb_stdtarg = gdb_stderr; /* for moment */
gdb_stdtargerr = gdb_stderr; /* for moment */
- /* If the input stream is connected to a terminal, turn on
- editing. */
- if (ISATTY (ui->instream))
+ /* If the input stream is connected to a terminal, turn on editing.
+ However, that is only allowed on the main UI, as we can only have
+ one instance of readline. */
+ if (ISATTY (ui->instream) && editing && ui == main_ui)
{
/* Tell gdb that we will be using the readline library. This
could be overwritten by a command in .gdbinit like 'set
editing on' or 'off'. */
- async_command_editing_p = 1;
-
+ ui->command_editing = 1;
+
/* When a character is detected on instream by select or poll,
readline will be invoked via this callback function. */
ui->call_readline = gdb_rl_callback_read_char_wrapper;
+
+ /* Tell readline to use the same input stream that gdb uses. */
+ rl_instream = ui->instream;
}
else
{
- async_command_editing_p = 0;
+ ui->command_editing = 0;
ui->call_readline = gdb_readline_no_editing_callback;
}
-
- /* When readline has read an end-of-line character, it passes the
- complete line to gdb for processing; command_line_handler is the
- function that does this. */
- ui->input_handler = command_line_handler;
-
- /* Tell readline to use the same input stream that gdb uses. */
- rl_instream = ui->instream;
/* Now create the event source for this UI's input file descriptor.
Another source is going to be the target program (inferior), but
/* Disable command input through the standard CLI channels. Used in
the suspend proc for interpreters that use the standard gdb readline
interface, like the cli & the mi. */
+
void
gdb_disable_readline (void)
{
gdb_stdtargerr = NULL;
#endif
- gdb_rl_callback_handler_remove ();
+ if (ui->command_editing)
+ gdb_rl_callback_handler_remove ();
delete_file_handler (ui->input_fd);
}
FIXME: these should really go into top.h. */
extern void display_gdb_prompt (const char *new_prompt);
-void gdb_setup_readline (void);
-void gdb_disable_readline (void);
+extern void gdb_setup_readline (int);
+extern void gdb_disable_readline (void);
extern void async_init_signals (void);
-extern void set_async_editing_command (char *args, int from_tty,
- struct cmd_list_element *c);
+extern void change_line_handler (int);
+extern void command_line_handler (char *rl);
extern void command_handler (char *command);
/* Signal to catch ^Z typed while reading a command: SIGTSTP or SIGCONT. */
/* Exported variables from event-top.c.
FIXME: these should really go into top.h. */
-extern int async_command_editing_p;
+extern int set_editing_cmd_var;
extern int exec_done_display_p;
extern struct prompts the_prompts;
extern void (*after_char_processing_hook) (void);
return;
}
- if (async_command_editing_p && !sync_execution)
+ if (current_ui->command_editing && !sync_execution)
gdb_rl_callback_handler_reinstall ();
}
interp->procs->command_loop_proc (interp->data);
}
+/* See interp.h */
+
+int
+interp_supports_command_editing (struct interp *interp)
+{
+ if (interp->procs->supports_command_editing_proc != NULL)
+ return interp->procs->supports_command_editing_proc (interp);
+ return 0;
+}
+
int
interp_quiet_p (struct interp *interp)
{
struct ui_file *out,
struct ui_file *logfile);
+typedef int (interp_supports_command_editing_ftype) (struct interp *self);
+
struct interp_procs
{
interp_init_ftype *init_proc;
interp_set_logging_ftype *set_logging_proc;
interp_command_loop_ftype *command_loop_proc;
+
+ /* Returns true if this interpreter supports using the readline
+ library; false if it uses GDB's own simplified readline
+ emulation. */
+ interp_supports_command_editing_ftype *supports_command_editing_proc;
};
extern struct interp *interp_new (const char *name,
extern void clear_interpreter_hooks (void);
+/* Returns true if INTERP supports using the readline library; false
+ if it uses GDB's own simplified form of readline. */
+extern int interp_supports_command_editing (struct interp *interp);
+
/* well-known interpreters */
#define INTERP_CONSOLE "console"
#define INTERP_MI1 "mi1"
/* As per hack note in mi_interpreter_init, swap in the output
channels... */
- gdb_setup_readline ();
+ gdb_setup_readline (0);
- /* These overwrite some of the initialization done in
- _intialize_event_loop. */
ui->call_readline = gdb_readline_no_editing_callback;
ui->input_handler = mi_execute_command_input_handler;
- async_command_editing_p = 0;
/* FIXME: This is a total hack for now. PB's use of the MI
implicitly relies on a bug in the async support which allows
asynchronous commands to leak through the commmand loop. The bug
--- /dev/null
+# Copyright 2016 Free Software Foundation, Inc.
+
+# 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 <http://www.gnu.org/licenses/>.
+
+# Regression test for PR mi/20034. Trying to turn on "set editing"
+# when the top-level interpreter is MI would result in GDB/readline
+# aborting with:
+#
+# readline: readline_callback_read_char() called with no handler!
+# Aborted (core dumped)
+
+load_lib mi-support.exp
+set MIFLAGS "-i=mi"
+
+gdb_exit
+if {[mi_gdb_start]} {
+ continue
+}
+
+mi_gdb_test "-interpreter-exec console \"set editing on\"" \
+ {=cmd-param-changed,param=\"editing\",.*\^done} \
+ "-interpreter-exec console \"set editing on\""
+
+mi_gdb_test "-interpreter-exec console \"show editing\"" \
+ ".*Editing of command lines as they are typed is off.*" \
+ "-interpreter-exec console \"show editing\""
we're handling an asynchronous target event and running in the
background, just before returning to the event loop to process
further input (or more target events). */
- if (async_command_editing_p)
+ if (current_ui->command_editing)
gdb_rl_callback_handler_remove ();
}
struct gdb_readline_wrapper_cleanup *cleanup
= (struct gdb_readline_wrapper_cleanup *) arg;
- rl_already_prompted = cleanup->already_prompted_orig;
+ if (ui->command_editing)
+ rl_already_prompted = cleanup->already_prompted_orig;
gdb_assert (ui->input_handler == gdb_readline_wrapper_line);
ui->input_handler = cleanup->handler_orig;
cleanup->handler_orig = ui->input_handler;
ui->input_handler = gdb_readline_wrapper_line;
- cleanup->already_prompted_orig = rl_already_prompted;
+ if (ui->command_editing)
+ cleanup->already_prompted_orig = rl_already_prompted;
+ else
+ cleanup->already_prompted_orig = 0;
cleanup->target_is_async_orig = target_is_async_p ();
/* Display our prompt and prevent double prompt display. */
display_gdb_prompt (prompt);
- rl_already_prompted = 1;
+ if (ui->command_editing)
+ rl_already_prompted = 1;
if (after_char_processing_hook)
(*after_char_processing_hook) ();
static void
undo_terminal_modifications_before_exit (void)
{
+ struct ui *saved_top_level = current_ui;
+
target_terminal_ours ();
+
+ current_ui = main_ui;
+
#if defined(TUI)
tui_disable ();
#endif
- if (async_command_editing_p)
- gdb_disable_readline ();
+ gdb_disable_readline ();
+
+ current_ui = saved_top_level;
}
fprintf_filtered (file, _("Gdb's prompt is \"%s\".\n"), value);
}
+/* "set editing" command. */
+
+static void
+set_editing (char *args, int from_tty, struct cmd_list_element *c)
+{
+ change_line_handler (set_editing_cmd_var);
+ /* Update the control variable so that MI's =cmd-param-changed event
+ shows the correct value. */
+ set_editing_cmd_var = current_ui->command_editing;
+}
+
static void
-show_async_command_editing_p (struct ui_file *file, int from_tty,
- struct cmd_list_element *c, const char *value)
+show_editing (struct ui_file *file, int from_tty,
+ struct cmd_list_element *c, const char *value)
{
fprintf_filtered (file, _("Editing of command lines as "
"they are typed is %s.\n"),
- value);
+ current_ui->command_editing ? _("on") : _("off"));
}
static void
hitting return."));
add_setshow_boolean_cmd ("editing", class_support,
- &async_command_editing_p, _("\
+ &set_editing_cmd_var, _("\
Set editing of command lines as they are typed."), _("\
Show editing of command lines as they are typed."), _("\
Use \"on\" to enable the editing, and \"off\" to disable it.\n\
Without an argument, command line editing is enabled. To edit, use\n\
EMACS-like or VI-like commands like control-P or ESC."),
- set_async_editing_command,
- show_async_command_editing_p,
+ set_editing,
+ show_editing,
&setlist, &showlist);
add_setshow_boolean_cmd ("save", no_class, &write_history_p, _("\
processing. */
void (*input_handler) (char *);
+ /* True if this UI is using the readline library for command
+ editing; false if using GDB's own simple readline emulation, with
+ no editing support. */
+ int command_editing;
+
/* Each UI has its own independent set of interpreters. */
struct ui_interp_info *interp_info;
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "defs.h"
+#include "cli/cli-interp.h"
#include "interps.h"
#include "top.h"
#include "event-top.h"
static int
tui_resume (void *data)
{
+ struct ui *ui = current_ui;
struct ui_file *stream;
/* gdb_setup_readline will change gdb_stdout. If the TUI was
stream = NULL;
}
- gdb_setup_readline ();
+ gdb_setup_readline (1);
+
+ ui->input_handler = command_line_handler;
if (stream != NULL)
cli_out_set_stream (tui_old_uiout, gdb_stdout);
tui_exec,
tui_ui_out,
NULL,
- cli_command_loop
+ cli_command_loop,
+ cli_interpreter_supports_command_editing,
};
/* Factory for TUI interpreters. */
if (ch == KEY_BACKSPACE)
return '\b';
- if (async_command_editing_p && key_is_start_sequence (ch))
+ if (current_ui->command_editing && key_is_start_sequence (ch))
{
int ch_pending;