This is the mail archive of the gdb-patches@sources.redhat.com mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH]: Fix readline/TUI deadlock


Hi!

The TUI creates a pipe and redirects all readline output to that pipe.
It was designed like that because it was the only way to make readline
clean enough to work with TUI.  So, readline sometimes (not always!) writes
in the pipe and later on the TUI flushes the pipe in the command window.

The problem is that since gdb is mono-thread, if the pipe if full we are blocked.
This happens when a big completion list is printed by readline.

This patch fixes that by installing a readline hook to print the completion
list.  The display functions come from readline/complete.c and have been
modified to print in TUI command window directly.  It is installed only
when we are in TUI mode.

With this patch, the pipe is no longer necessary.  This is a good news for
Windows & Mingw32 as the TUI has more chances to work on that platform
(if we are willing to integrate mingw32-readline!).  Despite that, it is
still there in case readline still writes on the output file (kind of backup wheel).
I've #if'ed the code so that it's possible to compile with and without the pipe
(only for testing).

I committed this patch on mainline.

	Stephane

2002-09-04  Stephane Carrez  <stcarrez@nerim.fr>

	* tuiIO.c (tui_putc): New function to print one character.
	(printable_part): New function from readline/complete.c.
	(PUTX): New macro, likewise.
	(print_filename): New function, likewise.
	(get_y_or_n): New function, likewise and adapted for TUI.
	(tui_rl_display_match_list): New function from readline/complete.c
	and writes on TUI command window.
	(tui_setup_io): Install or remove the readline hook
	rl_completion_display_matches_hook so that completion is written
	directly in TUI command window instead of in the TUI pipe.
	(tui_initialize_io): Use #ifdef TUI_USE_PIPE_FOR_READLINE for the
	TUI redirection pipe.
	(tui_getc): Likewise for call to tui_readline_output.
	(tui_readline_output): Likewise for function.
	* tui.c (tui_rl_startup_hook): Always take care of gdb prompt.

Index: tui.c
===================================================================
RCS file: /cvs/src/src/gdb/tui/tui.c,v
retrieving revision 1.24
diff -u -p -r1.24 tui.c
--- tui.c	31 Aug 2002 12:02:14 -0000	1.24
+++ tui.c	3 Sep 2002 20:18:25 -0000
@@ -270,12 +270,10 @@ tui_rl_next_keymap (void)
 static int
 tui_rl_startup_hook ()
 {
-  rl_already_prompted = (tui_current_key_mode != tui_command_mode);
-  if (rl_already_prompted)
-    {
-      tui_set_key_mode (tui_single_key_mode);
-      tui_redisplay_readline ();
-    }
+  rl_already_prompted = 1;
+  if (tui_current_key_mode != tui_command_mode)
+    tui_set_key_mode (tui_single_key_mode);
+  tui_redisplay_readline ();
   return 0;
 }
 
Index: tuiIO.c
===================================================================
RCS file: /cvs/src/src/gdb/tui/tuiIO.c,v
retrieving revision 1.14
diff -u -p -r1.14 tuiIO.c
--- tuiIO.c	31 Aug 2002 12:02:14 -0000	1.14
+++ tuiIO.c	3 Sep 2002 20:18:25 -0000
@@ -80,14 +80,24 @@
    is as if TUI is not used.  Readline also uses its original getc()
    function with stdin.
 
-   Note: the current readline is not clean in its management of the output.
-   Even if we install a redisplay handler, it sometimes writes on a stdout
-   file.  It is important to redirect every output produced by readline,
-   otherwise the curses window will be garbled.  This is implemented with
-   a pipe that TUI reads and readline writes to.  A gdb input handler
+   Note SCz/2001-07-21: the current readline is not clean in its management of
+   the output.  Even if we install a redisplay handler, it sometimes writes on
+   a stdout file.  It is important to redirect every output produced by
+   readline, otherwise the curses window will be garbled.  This is implemented
+   with a pipe that TUI reads and readline writes to.  A gdb input handler
    is created so that reading the pipe is handled automatically.
    This will probably not work on non-Unix platforms.  The best fix is
-   to make readline clean enougth so that is never write on stdout.  */
+   to make readline clean enougth so that is never write on stdout.
+
+   Note SCz/2002-09-01: we now use more readline hooks and it seems that
+   with them we don't need the pipe anymore (verified by creating the pipe
+   and closing its end so that write causes a SIGPIPE).  The old pipe code
+   is still there and can be conditionally removed by
+   #undef TUI_USE_PIPE_FOR_READLINE.  */
+
+/* For gdb 5.3, prefer to continue the pipe hack as a backup wheel.  */
+#define TUI_USE_PIPE_FOR_READLINE
+/*#undef TUI_USE_PIPE_FOR_READLINE*/
 
 /* TUI output files.  */
 static struct ui_file *tui_stdout;
@@ -110,10 +120,21 @@ static int tui_old_readline_echoing_p;
    Should be removed when readline is clean.  */
 static FILE *tui_rl_outstream;
 static FILE *tui_old_rl_outstream;
+#ifdef TUI_USE_PIPE_FOR_READLINE
 static int tui_readline_pipe[2];
+#endif
 
 static unsigned int _tuiHandleResizeDuringIO (unsigned int);
 
+static void
+tui_putc (char c)
+{
+  char buf[2];
+
+  buf[0] = c;
+  buf[1] = 0;
+  tui_puts (buf);
+}
 
 /* Print the string in the curses command window.  */
 void
@@ -243,6 +264,7 @@ tui_deprep_terminal (void)
 {
 }
 
+#ifdef TUI_USE_PIPE_FOR_READLINE
 /* Read readline output pipe and feed the command window with it.
    Should be removed when readline is clean.  */
 static void
@@ -258,6 +280,194 @@ tui_readline_output (int code, gdb_clien
       tui_puts (buf);
     }
 }
+#endif
+
+/* Return the portion of PATHNAME that should be output when listing
+   possible completions.  If we are hacking filename completion, we
+   are only interested in the basename, the portion following the
+   final slash.  Otherwise, we return what we were passed.
+
+   Comes from readline/complete.c  */
+static char *
+printable_part (pathname)
+     char *pathname;
+{
+  char *temp;
+
+  temp = rl_filename_completion_desired ? strrchr (pathname, '/') : (char *)NULL;
+#if defined (__MSDOS__)
+  if (rl_filename_completion_desired && temp == 0 && isalpha (pathname[0]) && pathname[1] == ':')
+    temp = pathname + 1;
+#endif
+  return (temp ? ++temp : pathname);
+}
+
+/* Output TO_PRINT to rl_outstream.  If VISIBLE_STATS is defined and we
+   are using it, check for and output a single character for `special'
+   filenames.  Return the number of characters we output. */
+
+#define PUTX(c) \
+    do { \
+      if (CTRL_CHAR (c)) \
+        { \
+          tui_puts ("^"); \
+          tui_putc (UNCTRL (c)); \
+          printed_len += 2; \
+        } \
+      else if (c == RUBOUT) \
+	{ \
+	  tui_puts ("^?"); \
+	  printed_len += 2; \
+	} \
+      else \
+	{ \
+	  tui_putc (c); \
+	  printed_len++; \
+	} \
+    } while (0)
+
+static int
+print_filename (to_print, full_pathname)
+     char *to_print, *full_pathname;
+{
+  int printed_len = 0;
+  char *s;
+
+  for (s = to_print; *s; s++)
+    {
+      PUTX (*s);
+    }
+  return printed_len;
+}
+
+/* The user must press "y" or "n".  Non-zero return means "y" pressed.
+   Comes from readline/complete.c  */
+static int
+get_y_or_n ()
+{
+  extern int _rl_abort_internal ();
+  int c;
+
+  for (;;)
+    {
+      c = rl_read_key ();
+      if (c == 'y' || c == 'Y' || c == ' ')
+	return (1);
+      if (c == 'n' || c == 'N' || c == RUBOUT)
+	return (0);
+      if (c == ABORT_CHAR)
+	_rl_abort_internal ();
+      beep ();
+    }
+}
+
+/* A convenience function for displaying a list of strings in
+   columnar format on readline's output stream.  MATCHES is the list
+   of strings, in argv format, LEN is the number of strings in MATCHES,
+   and MAX is the length of the longest string in MATCHES.
+
+   Comes from readline/complete.c and modified to write in
+   the TUI command window using tui_putc/tui_puts.  */
+static void
+tui_rl_display_match_list (matches, len, max)
+     char **matches;
+     int len, max;
+{
+  typedef int QSFUNC (const void *, const void *);
+  extern int _rl_qsort_string_compare (const void*, const void*);
+  extern int _rl_print_completions_horizontally;
+  
+  int count, limit, printed_len;
+  int i, j, k, l;
+  char *temp;
+
+  /* Screen dimension correspond to the TUI command window.  */
+  int screenwidth = cmdWin->generic.width;
+  int screenheight = cmdWin->generic.height;
+
+  /* If there are many items, then ask the user if she really wants to
+     see them all. */
+  if (len >= rl_completion_query_items)
+    {
+      char msg[256];
+
+      sprintf (msg, "\nDisplay all %d possibilities? (y or n)", len);
+      tui_puts (msg);
+      if (get_y_or_n () == 0)
+	{
+	  tui_puts ("\n");
+	  return;
+	}
+    }
+
+  /* How many items of MAX length can we fit in the screen window? */
+  max += 2;
+  limit = screenwidth / max;
+  if (limit != 1 && (limit * max == screenwidth))
+    limit--;
+
+  /* Avoid a possible floating exception.  If max > screenwidth,
+     limit will be 0 and a divide-by-zero fault will result. */
+  if (limit == 0)
+    limit = 1;
+
+  /* How many iterations of the printing loop? */
+  count = (len + (limit - 1)) / limit;
+
+  /* Watch out for special case.  If LEN is less than LIMIT, then
+     just do the inner printing loop.
+	   0 < len <= limit  implies  count = 1. */
+
+  /* Sort the items if they are not already sorted. */
+  if (rl_ignore_completion_duplicates == 0)
+    qsort (matches + 1, len, sizeof (char *),
+           (QSFUNC *)_rl_qsort_string_compare);
+
+  tui_putc ('\n');
+
+  if (_rl_print_completions_horizontally == 0)
+    {
+      /* Print the sorted items, up-and-down alphabetically, like ls. */
+      for (i = 1; i <= count; i++)
+	{
+	  for (j = 0, l = i; j < limit; j++)
+	    {
+	      if (l > len || matches[l] == 0)
+		break;
+	      else
+		{
+		  temp = printable_part (matches[l]);
+		  printed_len = print_filename (temp, matches[l]);
+
+		  if (j + 1 < limit)
+		    for (k = 0; k < max - printed_len; k++)
+		      tui_putc (' ');
+		}
+	      l += count;
+	    }
+	  tui_putc ('\n');
+	}
+    }
+  else
+    {
+      /* Print the sorted items, across alphabetically, like ls -x. */
+      for (i = 1; matches[i]; i++)
+	{
+	  temp = printable_part (matches[i]);
+	  printed_len = print_filename (temp, matches[i]);
+	  /* Have we reached the end of this line? */
+	  if (matches[i+1])
+	    {
+	      if (i && (limit > 1) && (i % limit) == 0)
+		tui_putc ('\n');
+	      else
+		for (k = 0; k < max - printed_len; k++)
+		  tui_putc (' ');
+	    }
+	}
+      tui_putc ('\n');
+    }
+}
 
 /* Setup the IO for curses or non-curses mode.
    - In non-curses mode, readline and gdb use the standard input and
@@ -288,6 +498,8 @@ tui_setup_io (int mode)
       readline_echoing_p = 0;
       rl_outstream = tui_rl_outstream;
       rl_prompt = 0;
+      rl_completion_display_matches_hook = tui_rl_display_match_list;
+      rl_already_prompted = 0;
 
       /* Keep track of previous gdb output.  */
       tui_old_stdout = gdb_stdout;
@@ -319,7 +531,9 @@ tui_setup_io (int mode)
       rl_prep_term_function = tui_old_rl_prep_terminal;
       rl_getc_function = tui_old_rl_getc_function;
       rl_outstream = tui_old_rl_outstream;
+      rl_completion_display_matches_hook = 0;
       readline_echoing_p = tui_old_readline_echoing_p;
+      rl_already_prompted = 1;
 
       /* Save tty for SIGCONT.  */
       savetty ();
@@ -367,6 +581,7 @@ tui_initialize_io ()
      a init_ui_hook.  */
   uiout = cli_out_new (gdb_stdout);
 
+#ifdef TUI_USE_PIPE_FOR_READLINE
   /* Temporary solution for readline writing to stdout:
      redirect readline output in a pipe, read that pipe and
      output the content in the curses command window.  */
@@ -390,8 +605,10 @@ tui_initialize_io ()
   (void) fcntl (tui_readline_pipe[0], F_SETFL, O_NDELAY);
 #endif
 #endif
-
   add_file_handler (tui_readline_pipe[0], tui_readline_output, 0);
+#else
+  tui_rl_outstream = stdout;
+#endif
 }
 
 /* Get a character from the command window.  This is called from the readline
@@ -404,9 +621,11 @@ tui_getc (FILE *fp)
 
   w = cmdWin->generic.handle;
 
+#ifdef TUI_USE_PIPE_FOR_READLINE
   /* Flush readline output.  */
   tui_readline_output (GDB_READABLE, 0);
-  
+#endif
+
   ch = wgetch (w);
   ch = _tuiHandleResizeDuringIO (ch);
 

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]