Fix PR tui/21216: TUI line breaks regression

Commit d7e747318f ("Eliminate make_cleanup_ui_file_delete / make
ui_file a class hierarchy") regressed the TUI's command window.
Newlines miss doing a "carriage return", resulting in output like:

~~~~~~~~~~~~~~~~~~
(gdb) helpList of classes of commands:

                                      aliases -- Aliases of other commands
                                                                          breakpoints -- Making program stop at certain points
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Before the commit mentioned above, the default ui_file->to_write
implementation had a hack that would defer into the ui_file->to_fputs
method.  The TUI's ui_file did not implement the to_write method, so
all writes would end up going to the ncurses window via tui_file_fputs
-> tui_puts.

After the commit above, the hack is gone, but the TUI's ui_file still
does not implement the ui_file::write method.  Since tui_file inherits
from stdio_file, writing to a tui_file ends up doing fwrite on the
FILE stream the TUI is "associated" with, via stdio_file::write,
instead of writing to the ncurses window.

The fix is to have tui_file override the "write" method.

New test included.

gdb/ChangeLog:
2017-03-08  Pedro Alves  <palves@redhat.com>

	PR tui/21216
	* tui/tui-file.c (tui_file::write): New.
	* tui/tui-file.h (tui_file): Override "write".
	* tui/tui-io.c (do_tui_putc, update_start_line): New functions,
	factored out from ...
	(tui_puts): ... here.
	(tui_putc): Use them.
	(tui_write): New function.
	* tui/tui-io.h (tui_write): Declare.

gdb/testsuite/ChangeLog:
2017-03-08  Pedro Alves  <palves@redhat.com>

	PR tui/21216
	* gdb.tui/tui-nl-filtered-output.exp: New file.
This commit is contained in:
Pedro Alves 2017-03-08 00:14:59 +00:00
parent 1a4dd9ddae
commit 9753a2f6d7
7 changed files with 165 additions and 40 deletions

View File

@ -1,3 +1,15 @@
2017-03-08 Pedro Alves <palves@redhat.com>
PR tui/21216
* tui/tui-file.c (tui_file::write): New.
* tui/tui-file.h (tui_file): Override "write".
* tui/tui-io.c (do_tui_putc, update_start_line): New functions,
factored out from ...
(tui_puts): ... here.
(tui_putc): Use them.
(tui_write): New function.
* tui/tui-io.h (tui_write): Declare.
2017-03-07 Sergio Durigan Junior <sergiodj@redhat.com>
* Makefile.in (SFILES): Replace "environ.c" with

View File

@ -1,3 +1,8 @@
2017-03-08 Pedro Alves <palves@redhat.com>
PR tui/21216
* gdb.tui/tui-nl-filtered-output.exp: New file.
2017-03-08 Pedro Alves <palves@redhat.com>
* gdb.base/completion.exp: Move TUI completion tests to ...

View File

@ -0,0 +1,57 @@
# Copyright 2017 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 tui/21216 - TUI line breaks regression.
#
# Tests that newlines in filtered output force a "carriage return" in
# the TUI command window. With a broken gdb, instead of:
#
# (gdb) printf "hello\nworld\n"
# hello
# world
# (gdb)
#
# we'd get:
#
# (gdb) printf "hello\nworld\n"hello
# world
#
# (gdb)
gdb_exit
gdb_start
if {[skip_tui_tests]} {
return
}
# Enable the TUI.
set test "tui enable"
gdb_test_multiple "tui enable" $test {
-re "$gdb_prompt $" {
pass $test
}
}
# Make sure filtering/pagination is enabled, but make the window high
# enough that we don't paginate in practice.
gdb_test_no_output "set pagination on"
gdb_test_no_output "set height 2000"
gdb_test \
{printf "hello\nworld\n"} \
"hello\r\nworld" \
"correct line breaks"

View File

@ -43,6 +43,16 @@ tui_file::puts (const char *linebuffer)
tui_refresh_cmd_win ();
}
void
tui_file::write (const char *buf, long length_buf)
{
tui_write (buf, length_buf);
/* gdb_stdout is buffered, and the caller must gdb_flush it at
appropriate times. Other streams are not so buffered. */
if (this != gdb_stdout)
tui_refresh_cmd_win ();
}
void
tui_file::flush ()
{

View File

@ -28,8 +28,9 @@ class tui_file : public stdio_file
public:
explicit tui_file (FILE *stream);
void flush () override;
void write (const char *buf, long length_buf) override;
void puts (const char *) override;
void flush () override;
};
#endif

View File

@ -137,58 +137,94 @@ static int tui_readline_pipe[2];
This may be the main gdb prompt or a secondary prompt. */
static char *tui_rl_saved_prompt;
/* Print a character in the curses command window. The output is
buffered. It is up to the caller to refresh the screen if
necessary. */
static void
do_tui_putc (WINDOW *w, char c)
{
static int tui_skip_line = -1;
/* Catch annotation and discard them. We need two \032 and discard
until a \n is seen. */
if (c == '\032')
{
tui_skip_line++;
}
else if (tui_skip_line != 1)
{
tui_skip_line = -1;
/* Expand TABs, since ncurses on MS-Windows doesn't. */
if (c == '\t')
{
int col;
col = getcurx (w);
do
{
waddch (w, ' ');
col++;
}
while ((col % 8) != 0);
}
else
waddch (w, c);
}
else if (c == '\n')
tui_skip_line = -1;
}
/* Update the cached value of the command window's start line based on
the window's current Y coordinate. */
static void
update_cmdwin_start_line ()
{
TUI_CMD_WIN->detail.command_info.start_line
= getcury (TUI_CMD_WIN->generic.handle);
}
/* Print a character in the curses command window. The output is
buffered. It is up to the caller to refresh the screen if
necessary. */
static void
tui_putc (char c)
{
char buf[2];
WINDOW *w = TUI_CMD_WIN->generic.handle;
buf[0] = c;
buf[1] = 0;
tui_puts (buf);
do_tui_putc (w, c);
update_cmdwin_start_line ();
}
/* Print the string in the curses command window.
The output is buffered. It is up to the caller to refresh the screen
if necessary. */
/* Print LENGTH characters from the buffer pointed to by BUF to the
curses command window. The output is buffered. It is up to the
caller to refresh the screen if necessary. */
void
tui_write (const char *buf, size_t length)
{
WINDOW *w = TUI_CMD_WIN->generic.handle;
for (size_t i = 0; i < length; i++)
do_tui_putc (w, buf[i]);
update_cmdwin_start_line ();
}
/* Print a string in the curses command window. The output is
buffered. It is up to the caller to refresh the screen if
necessary. */
void
tui_puts (const char *string)
{
static int tui_skip_line = -1;
WINDOW *w = TUI_CMD_WIN->generic.handle;
char c;
WINDOW *w;
w = TUI_CMD_WIN->generic.handle;
while ((c = *string++) != 0)
{
/* Catch annotation and discard them. We need two \032 and
discard until a \n is seen. */
if (c == '\032')
{
tui_skip_line++;
}
else if (tui_skip_line != 1)
{
tui_skip_line = -1;
/* Expand TABs, since ncurses on MS-Windows doesn't. */
if (c == '\t')
{
int col;
col = getcurx (w);
do
{
waddch (w, ' ');
col++;
} while ((col % 8) != 0);
}
else
waddch (w, c);
}
else if (c == '\n')
tui_skip_line = -1;
}
TUI_CMD_WIN->detail.command_info.start_line = getcury (w);
do_tui_putc (w, c);
update_cmdwin_start_line ();
}
/* Readline callback.

View File

@ -28,6 +28,10 @@ class cli_ui_out;
/* Print the string in the curses command window. */
extern void tui_puts (const char *);
/* Print LENGTH characters from the buffer pointed to by BUF to the
curses command window. */
extern void tui_write (const char *buf, size_t length);
/* Setup the IO for curses or non-curses mode. */
extern void tui_setup_io (int mode);