Fix PR tui/21216: TUI line breaks regression
authorPedro Alves <palves@redhat.com>
Wed, 8 Mar 2017 00:14:59 +0000 (00:14 +0000)
committerPedro Alves <palves@redhat.com>
Wed, 8 Mar 2017 00:14:59 +0000 (00:14 +0000)
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.

gdb/ChangeLog
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.tui/tui-nl-filtered-output.exp [new file with mode: 0644]
gdb/tui/tui-file.c
gdb/tui/tui-file.h
gdb/tui/tui-io.c
gdb/tui/tui-io.h

index 3a156ad4baa889896b13560402a4be2d816bc96b..432cdccd2ce7ffbaa255b36a9e221db292ef1d0b 100644 (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
index f9865207e4764f9e262b061d353c11cb4b972fb2..0654bef40de81afe5452aa50188f3ec81434a815 100644 (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 ...
diff --git a/gdb/testsuite/gdb.tui/tui-nl-filtered-output.exp b/gdb/testsuite/gdb.tui/tui-nl-filtered-output.exp
new file mode 100644 (file)
index 0000000..d1f56f2
--- /dev/null
@@ -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"
index 2f895b7a5348cae2d04f8e79231c3aefb1c16689..80a31f8bbe535e4aed9294e0a9d9b0441f812682 100644 (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 ()
 {
index aceaab6baba72e6577f3aed460c0d8158eebcca6..c426a8369c8bf3a05b4cbb5b6ec532cbc9b3c67c 100644 (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
index 433762bc235e7b7f487dad066e242d46988904e4..ba1ee9ad272cd66e71756851c014940c6dfe6516 100644 (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;
+
+  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.
index e1d5f8606b39291105e86ecf79a9cd36249956b9..3bd465f808d7fec6845b19c56f7ecb5078d48bfb 100644 (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);