/* TUI support I/O functions.
- Copyright (C) 1998-2013 Free Software Foundation, Inc.
+ Copyright (C) 1998-2016 Free Software Foundation, Inc.
Contributed by Hewlett-Packard Company.
#include "cli-out.h"
#include <fcntl.h>
#include <signal.h>
-#include <stdio.h>
#include "filestuff.h"
-
+#include "completer.h"
#include "gdb_curses.h"
/* This redefines CTRL if it is not already defined, so it must come
return (ch == 8);
}
-int
-key_is_command_char (int ch)
-{
- return ((ch == KEY_NPAGE) || (ch == KEY_PPAGE)
- || (ch == KEY_LEFT) || (ch == KEY_RIGHT)
- || (ch == KEY_UP) || (ch == KEY_DOWN)
- || (ch == KEY_SF) || (ch == KEY_SR)
- || (ch == (int)'\f')
- || key_is_start_sequence (ch));
-}
-
/* Use definition from readline 4.3. */
#undef CTRL_CHAR
#define CTRL_CHAR(c) \
struct ui_out *tui_old_uiout;
/* Readline previous hooks. */
-static Function *tui_old_rl_getc_function;
-static VFunction *tui_old_rl_redisplay_function;
-static VFunction *tui_old_rl_prep_terminal;
-static VFunction *tui_old_rl_deprep_terminal;
+static rl_getc_func_t *tui_old_rl_getc_function;
+static rl_voidfunc_t *tui_old_rl_redisplay_function;
+static rl_vintfunc_t *tui_old_rl_prep_terminal;
+static rl_voidfunc_t *tui_old_rl_deprep_terminal;
+static rl_compdisp_func_t *tui_old_rl_display_matches_hook;
static int tui_old_rl_echoing_p;
/* Readline output stream.
This may be the main gdb prompt or a secondary prompt. */
static char *tui_rl_saved_prompt;
-static unsigned int tui_handle_resize_during_io (unsigned int);
-
static void
tui_putc (char c)
{
tui_puts (buf);
}
-/* Print the string in the curses command window. */
+/* Print the 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)
{
else if (tui_skip_line != 1)
{
tui_skip_line = -1;
- waddch (w, c);
+ /* Expand TABs, since ncurses on MS-Windows doesn't. */
+ if (c == '\t')
+ {
+ int line, col;
+
+ getyx (w, line, col);
+ do
+ {
+ waddch (w, ' ');
+ col++;
+ } while ((col % 8) != 0);
+ }
+ else
+ waddch (w, c);
}
else if (c == '\n')
tui_skip_line = -1;
}
- getyx (w, TUI_CMD_WIN->detail.command_info.cur_line,
- TUI_CMD_WIN->detail.command_info.curch);
- TUI_CMD_WIN->detail.command_info.start_line
- = TUI_CMD_WIN->detail.command_info.cur_line;
-
- /* We could defer the following. */
- wrefresh (w);
- fflush (stdout);
+ TUI_CMD_WIN->detail.command_info.start_line = getcury (w);
}
/* Readline callback.
int c_line;
int in;
WINDOW *w;
- char *prompt;
+ const char *prompt;
int start_line;
/* Detect when we temporarily left SingleKey and now the readline
The command could call prompt_for_continue and we must not
restore SingleKey so that the prompt and normal keymap are used. */
if (tui_current_key_mode == TUI_ONE_COMMAND_MODE && rl_end == 0
- && immediate_quit == 0)
+ && !gdb_in_secondary_prompt_p ())
tui_set_key_mode (TUI_SINGLE_KEY_MODE);
if (tui_current_key_mode == TUI_SINGLE_KEY_MODE)
{
waddch (w, prompt[in]);
getyx (w, line, col);
- if (col < prev_col)
+ if (col <= prev_col)
height++;
prev_col = col;
}
- for (in = 0; in < rl_end; in++)
+ for (in = 0; in <= rl_end; in++)
{
unsigned char c;
- c = (unsigned char) rl_line_buffer[in];
if (in == rl_point)
{
getyx (w, c_line, c_pos);
}
+ if (in == rl_end)
+ break;
+
+ c = (unsigned char) rl_line_buffer[in];
if (CTRL_CHAR (c) || c == RUBOUT)
{
waddch (w, '^');
waddch (w, CTRL_CHAR (c) ? UNCTRL (c) : '?');
}
+ else if (c == '\t')
+ {
+ /* Expand TABs, since ncurses on MS-Windows doesn't. */
+ getyx (w, line, col);
+ do
+ {
+ waddch (w, ' ');
+ col++;
+ } while ((col % 8) != 0);
+ }
else
{
waddch (w, c);
}
if (c == '\n')
- {
- getyx (w, TUI_CMD_WIN->detail.command_info.start_line,
- TUI_CMD_WIN->detail.command_info.curch);
- }
+ TUI_CMD_WIN->detail.command_info.start_line = getcury (w);
getyx (w, line, col);
if (col < prev_col)
height++;
prev_col = col;
}
wclrtobot (w);
- getyx (w, TUI_CMD_WIN->detail.command_info.start_line,
- TUI_CMD_WIN->detail.command_info.curch);
+ TUI_CMD_WIN->detail.command_info.start_line = getcury (w);
if (c_line >= 0)
- {
- wmove (w, c_line, c_pos);
- TUI_CMD_WIN->detail.command_info.cur_line = c_line;
- TUI_CMD_WIN->detail.command_info.curch = c_pos;
- }
+ wmove (w, c_line, c_pos);
TUI_CMD_WIN->detail.command_info.start_line -= height - 1;
wrefresh (w);
(we can't use gdb_prompt() due to secondary prompts and can't use
rl_prompt because it points to an alloca buffer). */
xfree (tui_rl_saved_prompt);
- tui_rl_saved_prompt = xstrdup (rl_prompt);
+ tui_rl_saved_prompt = rl_prompt != NULL ? xstrdup (rl_prompt) : NULL;
}
/* Readline callback to restore the terminal. It is called once each
}
#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.
+/* TUI version of displayer.crlf. */
- Comes from readline/complete.c. */
-static const char *
-printable_part (const char *pathname)
+static void
+tui_mld_crlf (const struct match_list_displayer *displayer)
{
- return rl_filename_completion_desired ? lbasename (pathname) : pathname;
+ tui_putc ('\n');
}
-/* 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)
+/* TUI version of displayer.putch. */
-static int
-print_filename (const char *to_print, const char *full_pathname)
+static void
+tui_mld_putch (const struct match_list_displayer *displayer, int ch)
{
- int printed_len = 0;
- const char *s;
-
- for (s = to_print; *s; s++)
- {
- PUTX (*s);
- }
- return printed_len;
+ tui_putc (ch);
}
-/* The user must press "y" or "n". Non-zero return means "y" pressed.
- Comes from readline/complete.c. */
-static int
-get_y_or_n (void)
+/* TUI version of displayer.puts. */
+
+static void
+tui_mld_puts (const struct match_list_displayer *displayer, const char *s)
{
- extern int _rl_abort_internal ();
- int c;
+ tui_puts (s);
+}
- 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 ();
- }
+/* TUI version of displayer.flush. */
+
+static void
+tui_mld_flush (const struct match_list_displayer *displayer)
+{
+ wrefresh (TUI_CMD_WIN->generic.handle);
}
-/* 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.
+/* TUI version of displayer.erase_entire_line. */
- 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 (char **matches, int len, int max)
+tui_mld_erase_entire_line (const struct match_list_displayer *displayer)
{
- 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;
- const char *temp;
+ WINDOW *w = TUI_CMD_WIN->generic.handle;
+ int cur_y = getcury (w);
- /* Screen dimension correspond to the TUI command window. */
- int screenwidth = TUI_CMD_WIN->generic.width;
+ wmove (w, cur_y, 0);
+ wclrtoeol (w);
+ wmove (w, cur_y, 0);
+}
- /* 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];
+/* TUI version of displayer.beep. */
- xsnprintf (msg, sizeof (msg),
- "\nDisplay all %d possibilities? (y or n)", len);
- tui_puts (msg);
- if (get_y_or_n () == 0)
- {
- tui_puts ("\n");
- return;
- }
- }
+static void
+tui_mld_beep (const struct match_list_displayer *displayer)
+{
+ beep ();
+}
- /* 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--;
+/* Helper function for tui_mld_read_key.
+ This temporarily replaces tui_getc for use during tab-completion
+ match list display. */
- /* 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;
+static int
+tui_mld_getc (FILE *fp)
+{
+ WINDOW *w = TUI_CMD_WIN->generic.handle;
+ int c = wgetch (w);
- /* How many iterations of the printing loop? */
- count = (len + (limit - 1)) / limit;
+ return c;
+}
- /* Watch out for special case. If LEN is less than LIMIT, then
- just do the inner printing loop.
- 0 < len <= limit implies count = 1. */
+/* TUI version of displayer.read_key. */
- /* 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);
+static int
+tui_mld_read_key (const struct match_list_displayer *displayer)
+{
+ rl_getc_func_t *prev = rl_getc_function;
+ int c;
- tui_putc ('\n');
+ /* We can't use tui_getc as we need NEWLINE to not get emitted. */
+ rl_getc_function = tui_mld_getc;
+ c = rl_read_key ();
+ rl_getc_function = prev;
+ return c;
+}
- 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');
- }
+/* TUI version of rl_completion_display_matches_hook.
+ See gdb_display_match_list for a description of the arguments. */
+
+static void
+tui_rl_display_match_list (char **matches, int len, int max)
+{
+ struct match_list_displayer displayer;
+
+ rl_get_screen_size (&displayer.height, &displayer.width);
+ displayer.crlf = tui_mld_crlf;
+ displayer.putch = tui_mld_putch;
+ displayer.puts = tui_mld_puts;
+ displayer.flush = tui_mld_flush;
+ displayer.erase_entire_line = tui_mld_erase_entire_line;
+ displayer.beep = tui_mld_beep;
+ displayer.read_key = tui_mld_read_key;
+
+ gdb_display_match_list (matches, len, max, &displayer);
}
/* Setup the IO for curses or non-curses mode.
tui_old_rl_deprep_terminal = rl_deprep_term_function;
tui_old_rl_prep_terminal = rl_prep_term_function;
tui_old_rl_getc_function = rl_getc_function;
+ tui_old_rl_display_matches_hook = rl_completion_display_matches_hook;
tui_old_rl_outstream = rl_outstream;
tui_old_rl_echoing_p = _rl_echoing_p;
rl_redisplay_function = tui_redisplay_readline;
rl_deprep_term_function = tui_old_rl_deprep_terminal;
rl_prep_term_function = tui_old_rl_prep_terminal;
rl_getc_function = tui_old_rl_getc_function;
+ rl_completion_display_matches_hook = tui_old_rl_display_matches_hook;
rl_outstream = tui_old_rl_outstream;
- rl_completion_display_matches_hook = 0;
_rl_echoing_p = tui_old_rl_echoing_p;
rl_already_prompted = 0;
/* Force a refresh of the screen. */
tui_refresh_all_win ();
- /* Update cursor position on the screen. */
- wmove (TUI_CMD_WIN->generic.handle,
- TUI_CMD_WIN->detail.command_info.start_line,
- TUI_CMD_WIN->detail.command_info.curch);
wrefresh (TUI_CMD_WIN->generic.handle);
}
signal (sig, tui_cont_sig);
tui_stderr = tui_fileopen (stderr);
tui_out = tui_out_new (tui_stdout);
- /* Create the default UI. It is not created because we installed a
- deprecated_init_ui_hook. */
+ /* Create the default UI. */
tui_old_uiout = cli_out_new (gdb_stdout);
#ifdef TUI_USE_PIPE_FOR_READLINE
readline output in a pipe, read that pipe and output the content
in the curses command window. */
if (gdb_pipe_cloexec (tui_readline_pipe) != 0)
- {
- fprintf_unfiltered (gdb_stderr, "Cannot create pipe for readline");
- exit (1);
- }
+ error (_("Cannot create pipe for readline"));
+
tui_rl_outstream = fdopen (tui_readline_pipe[1], "w");
if (tui_rl_outstream == 0)
- {
- fprintf_unfiltered (gdb_stderr, "Cannot redirect readline output");
- exit (1);
- }
+ error (_("Cannot redirect readline output"));
+
setvbuf (tui_rl_outstream, (char*) NULL, _IOLBF, 0);
#ifdef O_NONBLOCK
#endif
ch = wgetch (w);
- ch = tui_handle_resize_during_io (ch);
/* The \n must be echoed because it will not be printed by
readline. */
with empty lines with gdb prompt at beginning. Instead of that,
stay on the same line but provide a visual effect to show the
user we recognized the command. */
- if (rl_end == 0)
+ if (rl_end == 0 && !gdb_in_secondary_prompt_p ())
{
- wmove (w, TUI_CMD_WIN->detail.command_info.cur_line, 0);
+ wmove (w, getcury (w), 0);
/* Clear the line. This will blink the gdb prompt since
it will be redrawn at the same line. */
}
else
{
- wmove (w, TUI_CMD_WIN->detail.command_info.cur_line,
- TUI_CMD_WIN->detail.command_info.curch);
- waddch (w, ch);
+ /* Move cursor to the end of the command line before emitting the
+ newline. We need to do so because when ncurses outputs a newline
+ it truncates any text that appears past the end of the cursor. */
+ int px, py;
+ getyx (w, py, px);
+ px += rl_end - rl_point;
+ py += px / TUI_CMD_WIN->generic.width;
+ px %= TUI_CMD_WIN->generic.width;
+ wmove (w, py, px);
+ tui_putc ('\n');
}
}
- if (key_is_command_char (ch))
- { /* Handle prev/next/up/down here. */
- ch = tui_dispatch_ctrl_char (ch);
- }
+ /* Handle prev/next/up/down here. */
+ ch = tui_dispatch_ctrl_char (ch);
- if (ch == '\n' || ch == '\r' || ch == '\f')
- TUI_CMD_WIN->detail.command_info.curch = 0;
if (ch == KEY_BACKSPACE)
return '\b';
-
+
+ if (current_ui->command_editing && key_is_start_sequence (ch))
+ {
+ int ch_pending;
+
+ nodelay (w, TRUE);
+ ch_pending = wgetch (w);
+ nodelay (w, FALSE);
+
+ /* If we have pending input following a start sequence, call the stdin
+ event handler again because ncurses may have already read and stored
+ the input into its internal buffer, meaning that we won't get an stdin
+ event for it. If we don't compensate for this missed stdin event, key
+ sequences as Alt_F (^[f) will not behave promptly.
+
+ (We only compensates for the missed 2nd byte of a key sequence because
+ 2-byte sequences are by far the most commonly used. ncurses may have
+ buffered a larger, 3+-byte key sequence though it remains to be seen
+ whether it is useful to compensate for all the bytes of such
+ sequences.) */
+ if (ch_pending != ERR)
+ {
+ ungetch (ch_pending);
+ call_stdin_event_handler_again_p = 1;
+ }
+ }
+
return ch;
}
+/* Utility function to expand TABs in a STRING into spaces. STRING
+ will be displayed starting at column COL, and is assumed to include
+ no newlines. The returned expanded string is malloc'ed. */
-/* Cleanup when a resize has occured.
- Returns the character that must be processed. */
-static unsigned int
-tui_handle_resize_during_io (unsigned int original_ch)
+char *
+tui_expand_tabs (const char *string, int col)
{
- if (tui_win_resized ())
+ int n_adjust, ncol;
+ const char *s;
+ char *ret, *q;
+
+ /* 1. How many additional characters do we need? */
+ for (ncol = col, n_adjust = 0, s = string; s; )
{
- tui_resize_all ();
- tui_refresh_all_win ();
- dont_repeat ();
- tui_set_win_resized_to (FALSE);
- return '\n';
+ s = strpbrk (s, "\t");
+ if (s)
+ {
+ ncol += (s - string) + n_adjust;
+ /* Adjustment for the next tab stop, minus one for the TAB
+ we replace with spaces. */
+ n_adjust += 8 - (ncol % 8) - 1;
+ s++;
+ }
}
- else
- return original_ch;
+
+ /* Allocate the copy. */
+ ret = q = (char *) xmalloc (strlen (string) + n_adjust + 1);
+
+ /* 2. Copy the original string while replacing TABs with spaces. */
+ for (ncol = col, s = string; s; )
+ {
+ const char *s1 = strpbrk (s, "\t");
+ if (s1)
+ {
+ if (s1 > s)
+ {
+ strncpy (q, s, s1 - s);
+ q += s1 - s;
+ ncol += s1 - s;
+ }
+ do {
+ *q++ = ' ';
+ ncol++;
+ } while ((ncol % 8) != 0);
+ s1++;
+ }
+ else
+ strcpy (q, s);
+ s = s1;
+ }
+
+ return ret;
}