Commit
d7e747318f4d04 ("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.
+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
+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 ...
--- /dev/null
+# 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"
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 ()
{
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
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;
+
+ do_tui_putc (w, c);
+ update_cmdwin_start_line ();
+}
+
+/* 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;
- buf[0] = c;
- buf[1] = 0;
- tui_puts (buf);
+ for (size_t i = 0; i < length; i++)
+ do_tui_putc (w, buf[i]);
+ 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 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.
/* 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);