PR tui/16138, PR tui/17519, and misc failures to initialize the terminal

PR tui/16138 is about failure to initialize curses resulting in GDB
exiting instead of throwing an error.  E.g.:

 $ TERM=foo gdb
 (gdb) layout asm
 Error opening terminal: foo.
 $

The problem is that we're calling initscr to initialize the screen.
As mentioned in
http://pubs.opengroup.org/onlinepubs/7908799/xcurses/initscr.html:

 If errors occur, initscr() writes an appropriate error message to
 standard error and exits.
                    ^^^^^

Instead, we should use newterm:

 "A program that needs an indication of error conditions, so it can
 continue to run in a line-oriented mode if the terminal cannot support
 a screen-oriented program, would also use this function."

After the patch:

 $ TERM=foo gdb -q -nx
 (gdb) layout asm
 Cannot enable the TUI: error opening terminal [TERM=foo]
 (gdb)

And then PR tui/17519 is about GDB not validating whether the terminal
has the necessary capabilities when enabling the TUI.  If one tries to
enable the TUI with TERM=dumb (and e.g., from a shell within emacs),
GDB ends up with a clear screen, the cursor is placed at the
bottom/right corner of the screen, there's no prompt, typing shows no
echo, and there's no indication of what's going on.  c-x,a gets you
out of the TUI, but it's completely non-obvious.

After the patch, we get:

 $ TERM=dumb gdb -q -nx
 (gdb) layout asm
 Cannot enable the TUI: terminal doesn't support cursor addressing [TERM=dumb]
 (gdb)

While at it, I've moved all the tui_allowed_p validation to
tui_enable, and expanded the error messages.  Previously we'd get:

 $ gdb -q -nx -i=mi
 (gdb)
 layout asm
 &"layout asm\n"
 &"TUI mode not allowed\n"
 ^error,msg="TUI mode not allowed"

and:

 $ gdb -q -nx -ex "layout asm" > foo
 TUI mode not allowed

While now we get:

 $ gdb -q -nx -i=mi
 (gdb)
 layout asm
 &"layout asm\n"
 &"Cannot enable the TUI when the interpreter is 'mi'\n"
 ^error,msg="Cannot enable the TUI when the interpreter is 'mi'"
 (gdb)

and:

 $ gdb -q -nx -ex "layout asm" > foo
 Cannot enable the TUI when output is not a terminal

Tested on x86_64 Fedora 20.

gdb/
2014-10-29  Pedro Alves  <palves@redhat.com>

	PR tui/16138
	PR tui/17519
	* tui/tui-interp.c (tui_is_toplevel): Delete global.
	(tui_allowed_p): Delete function.
	* tui/tui.c: Include "interps.h".
	(tui_enable): Don't use tui_allowed_p.  Error out here with
	detailed error messages if the TUI is the top level interpreter,
	or if output is not a terminal.  Use newterm instead of initscr,
	and error out if initializing the terminal fails.  Also error out if
	the terminal doesn't support cursor addressing.
	* tui/tui.h (tui_allowed_p): Delete declaration.
This commit is contained in:
Pedro Alves
2014-10-29 14:23:57 +00:00
parent 563e8d8516
commit 84eda397bc
3 changed files with 49 additions and 25 deletions

View File

@ -51,9 +51,6 @@ tui_exit (void)
tui_disable (); tui_disable ();
} }
/* True if TUI is the top-level interpreter. */
static int tui_is_toplevel = 0;
/* Observers for several run control events. If the interpreter is /* Observers for several run control events. If the interpreter is
quiet (i.e., another interpreter is being run with quiet (i.e., another interpreter is being run with
interpreter-exec), print nothing. */ interpreter-exec), print nothing. */
@ -126,8 +123,6 @@ tui_on_command_error (void)
static void * static void *
tui_init (struct interp *self, int top_level) tui_init (struct interp *self, int top_level)
{ {
tui_is_toplevel = top_level;
/* Install exit handler to leave the screen in a good shape. */ /* Install exit handler to leave the screen in a good shape. */
atexit (tui_exit); atexit (tui_exit);
@ -150,18 +145,6 @@ tui_init (struct interp *self, int top_level)
return NULL; return NULL;
} }
/* True if enabling the TUI is allowed. Example, if the top level
interpreter is MI, enabling curses will certainly lose. */
int
tui_allowed_p (void)
{
/* Only if TUI is the top level interpreter. Also don't try to
setup curses (and print funny control characters) if we're not
outputting to a terminal. */
return tui_is_toplevel && ui_file_isatty (gdb_stdout);
}
static int static int
tui_resume (void *data) tui_resume (void *data)
{ {

View File

@ -48,6 +48,7 @@
#include <setjmp.h> #include <setjmp.h>
#include "gdb_curses.h" #include "gdb_curses.h"
#include "interps.h"
/* This redefines CTRL if it is not already defined, so it must come /* This redefines CTRL if it is not already defined, so it must come
after terminal state releated include files like <term.h> and after terminal state releated include files like <term.h> and
@ -361,6 +362,20 @@ tui_initialize_readline (void)
rl_bind_key_in_map ('s', tui_rl_next_keymap, tui_ctlx_keymap); rl_bind_key_in_map ('s', tui_rl_next_keymap, tui_ctlx_keymap);
} }
/* Return the TERM variable from the environment, or "<unset>"
if not set. */
static const char *
gdb_getenv_term (void)
{
const char *term;
term = getenv ("TERM");
if (term != NULL)
return term;
return "<unset>";
}
/* Enter in the tui mode (curses). /* Enter in the tui mode (curses).
When in normal mode, it installs the tui hooks in gdb, redirects When in normal mode, it installs the tui hooks in gdb, redirects
the gdb output, configures the readline to work in tui mode. the gdb output, configures the readline to work in tui mode.
@ -368,8 +383,7 @@ tui_initialize_readline (void)
void void
tui_enable (void) tui_enable (void)
{ {
if (!tui_allowed_p ()) struct interp *interp;
error (_("TUI mode not allowed"));
if (tui_active) if (tui_active)
return; return;
@ -380,8 +394,39 @@ tui_enable (void)
if (tui_finish_init) if (tui_finish_init)
{ {
WINDOW *w; WINDOW *w;
SCREEN *s;
const char *cap;
const char *interp;
w = initscr (); /* If the top level interpreter is not the console/tui (e.g.,
MI), enabling curses will certainly lose. */
interp = interp_name (top_level_interpreter ());
if (strcmp (interp, INTERP_TUI) != 0)
error (_("Cannot enable the TUI when the interpreter is '%s'"), interp);
/* Don't try to setup curses (and print funny control
characters) if we're not outputting to a terminal. */
if (!ui_file_isatty (gdb_stdout))
error (_("Cannot enable the TUI when output is not a terminal"));
s = newterm (NULL, NULL, NULL);
if (s == NULL)
{
error (_("Cannot enable the TUI: error opening terminal [TERM=%s]"),
gdb_getenv_term ());
}
w = stdscr;
/* Check required terminal capabilities. */
cap = tigetstr ("cup");
if (cap == NULL || cap == (char *) -1 || *cap == '\0')
{
endwin ();
delscreen (s);
error (_("Cannot enable the TUI: "
"terminal doesn't support cursor addressing [TERM=%s]"),
gdb_getenv_term ());
}
cbreak (); cbreak ();
noecho (); noecho ();

View File

@ -64,10 +64,6 @@ extern int tui_get_command_dimension (unsigned int *width,
key shortcut. */ key shortcut. */
extern void tui_initialize_readline (void); extern void tui_initialize_readline (void);
/* True if enabling the TUI is allowed. Example, if the top level
interpreter is MI, enabling curses will certainly lose. */
extern int tui_allowed_p (void);
/* Enter in the tui mode (curses). */ /* Enter in the tui mode (curses). */
extern void tui_enable (void); extern void tui_enable (void);