Command line input handling TLC

I didn't manage to usefully split this further into smaller
independent pieces, so:

 - Use "struct buffer" more.

 - Split out the responsibility of composing a complete command line
   from multiple input lines split with backslash

    (
    E.g.:

       (gdb) print \
       1 + \
       2
       $1 = 3
       (gdb)
    )

   to a separate function.  Note we don't need the separate
   readline_input_state and more_to_come globals at all.  They were
   just obfuscating the logic.

 - Factor out the tricky mostly duplicated code in
   command_line_handler and command_line_input.

gdb/ChangeLog
2016-03-09  Pedro Alves  <palves@redhat.com>

	* event-top.c (more_to_come): Delete.
	(struct readline_input_state): Delete.
	(readline_input_state): Delete.
	(get_command_line_buffer): New function.
	(command_handler): Update comments.  Don't handle NULL commands
	here.  Do not execute commented lines.
	(command_line_append_input_line): New function.
	(handle_line_of_input): New function, partly based on
	command_line_handler and command_line_input.
	(command_line_handler): Rewrite.
	* event-top.h (command_handler): New declaration.
	(command_loop): Defer command execution to command_handler.
	(command_line_input): Update comments.  Simplify, using struct
	buffer and handle_line_of_input.
	* top.h (struct buffer): New forward declaration.
	(handle_line_of_input): New declaration.
This commit is contained in:
Pedro Alves 2016-03-09 18:25:00 +00:00
parent 2669cade3d
commit b69d38afde
5 changed files with 230 additions and 329 deletions

View File

@ -1,3 +1,22 @@
2016-03-09 Pedro Alves <palves@redhat.com>
* event-top.c (more_to_come): Delete.
(struct readline_input_state): Delete.
(readline_input_state): Delete.
(get_command_line_buffer): New function.
(command_handler): Update comments. Don't handle NULL commands
here. Do not execute commented lines.
(command_line_append_input_line): New function.
(handle_line_of_input): New function, partly based on
command_line_handler and command_line_input.
(command_line_handler): Rewrite.
* event-top.h (command_handler): New declaration.
(command_loop): Defer command execution to command_handler.
(command_line_input): Update comments. Simplify, using struct
buffer and handle_line_of_input.
* top.h (struct buffer): New forward declaration.
(handle_line_of_input): New declaration.
2016-03-09 Pedro Alves <palves@redhat.com>
* event-top.c (command_line_handler): Use xfree + xstrdup instead

View File

@ -49,7 +49,6 @@
static void rl_callback_read_char_wrapper (gdb_client_data client_data);
static void command_line_handler (char *rl);
static void change_line_handler (void);
static void command_handler (char *command);
static char *top_level_prompt (void);
/* Signal handlers. */
@ -140,20 +139,6 @@ static struct async_signal_handler *sigtstp_token;
#endif
static struct async_signal_handler *async_sigterm_token;
/* Structure to save a partially entered command. This is used when
the user types '\' at the end of a command line. This is necessary
because each line of input is handled by a different call to
command_line_handler, and normally there is no state retained
between different calls. */
static int more_to_come = 0;
struct readline_input_state
{
char *linebuffer;
char *linebuffer_ptr;
}
readline_input_state;
/* This hook is called by rl_callback_read_char_wrapper after each
character is processed. */
void (*after_char_processing_hook) (void);
@ -383,6 +368,24 @@ top_level_prompt (void)
return xstrdup (prompt);
}
/* Get a pointer to the command line buffer. This is used to
construct a whole line of input from partial input. */
static struct buffer *
get_command_line_buffer (void)
{
static struct buffer line_buffer;
static int line_buffer_initialized;
if (!line_buffer_initialized)
{
buffer_init (&line_buffer);
line_buffer_initialized = 1;
}
return &line_buffer;
}
/* When there is an event ready on the stdin file descriptor, instead
of calling readline directly throught the callback function, or
instead of calling gdb_readline_no_editing_callback, give gdb a
@ -436,152 +439,122 @@ async_disable_stdin (void)
}
/* Handles a gdb command. This function is called by
command_line_handler, which has processed one or more input lines
into COMMAND. */
/* NOTE: 1999-04-30 This is the asynchronous version of the command_loop
function. The command_loop function will be obsolete when we
switch to use the event loop at every execution of gdb. */
static void
/* Handle a gdb command line. This function is called when
handle_line_of_input has concatenated one or more input lines into
a whole command. */
void
command_handler (char *command)
{
struct cleanup *stat_chain;
char *c;
clear_quit_flag ();
if (instream == stdin)
reinitialize_more_filter ();
/* If readline returned a NULL command, it means that the connection
with the terminal is gone. This happens at the end of a
testsuite run, after Expect has hung up but GDB is still alive.
In such a case, we just quit gdb killing the inferior program
too. */
if (command == 0)
{
printf_unfiltered ("quit\n");
execute_command ("quit", stdin == instream);
}
stat_chain = make_command_stats_cleanup (1);
execute_command (command, instream == stdin);
/* Do not execute commented lines. */
for (c = command; *c == ' ' || *c == '\t'; c++)
;
if (c[0] != '#')
{
execute_command (command, instream == stdin);
/* Do any commands attached to breakpoint we stopped at. */
bpstat_do_actions ();
/* Do any commands attached to breakpoint we stopped at. */
bpstat_do_actions ();
}
do_cleanups (stat_chain);
}
/* Handle a complete line of input. This is called by the callback
mechanism within the readline library. Deal with incomplete
commands as well, by saving the partial input in a global
buffer. */
/* Append RL, an input line returned by readline or one of its
emulations, to CMD_LINE_BUFFER. Returns the command line if we
have a whole command line ready to be processed by the command
interpreter or NULL if the command line isn't complete yet (input
line ends in a backslash). Takes ownership of RL. */
/* NOTE: 1999-04-30 This is the asynchronous version of the
command_line_input function; command_line_input will become
obsolete once we use the event loop as the default mechanism in
GDB. */
static void
command_line_handler (char *rl)
static char *
command_line_append_input_line (struct buffer *cmd_line_buffer, char *rl)
{
char *cmd;
size_t len;
len = strlen (rl);
if (len > 0 && rl[len - 1] == '\\')
{
/* Don't copy the backslash and wait for more. */
buffer_grow (cmd_line_buffer, rl, len - 1);
cmd = NULL;
}
else
{
/* Copy whole line including terminating null, and we're
done. */
buffer_grow (cmd_line_buffer, rl, len + 1);
cmd = cmd_line_buffer->buffer;
}
/* Allocated in readline. */
xfree (rl);
return cmd;
}
/* Handle a line of input coming from readline.
If the read line ends with a continuation character (backslash),
save the partial input in CMD_LINE_BUFFER (except the backslash),
and return NULL. Otherwise, save the partial input and return a
pointer to CMD_LINE_BUFFER's buffer (null terminated), indicating a
whole command line is ready to be executed.
Returns EOF on end of file.
If REPEAT, handle command repetitions:
- If the input command line is NOT empty, the command returned is
copied into the global 'saved_command_line' var so that it can
be repeated later.
- OTOH, if the input command line IS empty, return the previously
saved command instead of the empty input line.
*/
char *
handle_line_of_input (struct buffer *cmd_line_buffer,
char *rl, int repeat, char *annotation_suffix)
{
static char *linebuffer = 0;
static unsigned linelength = 0;
char *p;
char *p1;
char *nline;
int repeat = (instream == stdin);
char *cmd;
if (rl == NULL)
return (char *) EOF;
cmd = command_line_append_input_line (cmd_line_buffer, rl);
if (cmd == NULL)
return NULL;
/* We have a complete command line now. Prepare for the next
command, but leave ownership of memory to the buffer . */
cmd_line_buffer->used_size = 0;
if (annotation_level > 1 && instream == stdin)
printf_unfiltered (("\n\032\032post-prompt\n"));
if (linebuffer == 0)
{
linelength = 80;
linebuffer = (char *) xmalloc (linelength);
linebuffer[0] = '\0';
printf_unfiltered (("\n\032\032post-"));
puts_unfiltered (annotation_suffix);
printf_unfiltered (("\n"));
}
p = linebuffer;
if (more_to_come)
#define SERVER_COMMAND_PREFIX "server "
if (startswith (cmd, SERVER_COMMAND_PREFIX))
{
strcpy (linebuffer, readline_input_state.linebuffer);
p = readline_input_state.linebuffer_ptr;
xfree (readline_input_state.linebuffer);
more_to_come = 0;
}
#ifdef STOP_SIGNAL
if (job_control)
signal (STOP_SIGNAL, handle_stop_sig);
#endif
/* Make sure that all output has been output. Some machines may let
you get away with leaving out some of the gdb_flush, but not
all. */
wrap_here ("");
gdb_flush (gdb_stdout);
gdb_flush (gdb_stderr);
if (source_file_name != NULL)
++source_line_number;
/* If we are in this case, then command_handler will call quit
and exit from gdb. */
if (!rl || rl == (char *) EOF)
{
command_handler (0);
return; /* Lint. */
}
if (strlen (rl) + 1 + (p - linebuffer) > linelength)
{
linelength = strlen (rl) + 1 + (p - linebuffer);
nline = (char *) xrealloc (linebuffer, linelength);
p += nline - linebuffer;
linebuffer = nline;
}
p1 = rl;
/* Copy line. Don't copy null at end. (Leaves line alone
if this was just a newline). */
while (*p1)
*p++ = *p1++;
xfree (rl); /* Allocated in readline. */
if (p > linebuffer && *(p - 1) == '\\')
{
*p = '\0';
p--; /* Put on top of '\'. */
readline_input_state.linebuffer = xstrdup (linebuffer);
readline_input_state.linebuffer_ptr = p;
/* We will not invoke a execute_command if there is more
input expected to complete the command. So, we need to
print an empty prompt here. */
more_to_come = 1;
display_gdb_prompt ("");
return;
}
#ifdef STOP_SIGNAL
if (job_control)
signal (STOP_SIGNAL, SIG_DFL);
#endif
#define SERVER_COMMAND_LENGTH 7
server_command =
(p - linebuffer > SERVER_COMMAND_LENGTH)
&& strncmp (linebuffer, "server ", SERVER_COMMAND_LENGTH) == 0;
if (server_command)
{
/* Note that we don't set `line'. Between this and the check in
dont_repeat, this insures that repeating will still do the
right thing. */
*p = '\0';
command_handler (linebuffer + SERVER_COMMAND_LENGTH);
display_gdb_prompt (0);
return;
/* Note that we don't set `saved_command_line'. Between this
and the check in dont_repeat, this insures that repeating
will still do the right thing. */
return cmd + strlen (SERVER_COMMAND_PREFIX);
}
/* Do history expansion if that is wished. */
@ -591,10 +564,11 @@ command_line_handler (char *rl)
char *history_value;
int expanded;
*p = '\0'; /* Insert null now. */
expanded = history_expand (linebuffer, &history_value);
expanded = history_expand (cmd, &history_value);
if (expanded)
{
size_t len;
/* Print the changes. */
printf_unfiltered ("%s\n", history_value);
@ -602,67 +576,81 @@ command_line_handler (char *rl)
if (expanded < 0)
{
xfree (history_value);
return;
return cmd;
}
if (strlen (history_value) > linelength)
{
linelength = strlen (history_value) + 1;
linebuffer = (char *) xrealloc (linebuffer, linelength);
}
strcpy (linebuffer, history_value);
p = linebuffer + strlen (linebuffer);
/* history_expand returns an allocated string. Just replace
our buffer with it. */
len = strlen (history_value);
xfree (buffer_finish (cmd_line_buffer));
cmd_line_buffer->buffer = history_value;
cmd_line_buffer->buffer_size = len + 1;
cmd = history_value;
}
xfree (history_value);
}
/* If we just got an empty line, and that is supposed to repeat the
previous command, return the value in the global buffer. */
if (repeat && p == linebuffer && *p != '\\')
{
command_handler (saved_command_line);
display_gdb_prompt (0);
return;
}
previous command, return the previously saved command. */
for (p1 = cmd; *p1 == ' ' || *p1 == '\t'; p1++)
;
if (repeat && *p1 == '\0')
return saved_command_line;
for (p1 = linebuffer; *p1 == ' ' || *p1 == '\t'; p1++);
if (repeat && !*p1)
{
command_handler (saved_command_line);
display_gdb_prompt (0);
return;
}
*p = 0;
/* Add line to history if appropriate. */
if (*linebuffer && input_from_terminal_p ())
gdb_add_history (linebuffer);
/* Note: lines consisting solely of comments are added to the command
history. This is useful when you type a command, and then
realize you don't want to execute it quite yet. You can comment
out the command and then later fetch it from the value history
and remove the '#'. The kill ring is probably better, but some
people are in the habit of commenting things out. */
if (*p1 == '#')
*p1 = '\0'; /* Found a comment. */
/* Add command to history if appropriate. Note: lines consisting
solely of comments are also added to the command history. This
is useful when you type a command, and then realize you don't
want to execute it quite yet. You can comment out the command
and then later fetch it from the value history and remove the
'#'. The kill ring is probably better, but some people are in
the habit of commenting things out. */
if (*cmd != '\0' && input_from_terminal_p ())
gdb_add_history (cmd);
/* Save into global buffer if appropriate. */
if (repeat)
{
xfree (saved_command_line);
saved_command_line = xstrdup (linebuffer);
if (!more_to_come)
{
command_handler (saved_command_line);
display_gdb_prompt (0);
}
return;
saved_command_line = xstrdup (cmd);
return saved_command_line;
}
else
return cmd;
}
command_handler (linebuffer);
display_gdb_prompt (0);
return;
/* Handle a complete line of input. This is called by the callback
mechanism within the readline library. Deal with incomplete
commands as well, by saving the partial input in a global
buffer.
NOTE: This is the asynchronous version of the command_line_input
function. */
void
command_line_handler (char *rl)
{
struct buffer *line_buffer = get_command_line_buffer ();
char *cmd;
cmd = handle_line_of_input (line_buffer, rl, instream == stdin, "prompt");
if (cmd == (char *) EOF)
{
/* stdin closed. The connection with the terminal is gone.
This happens at the end of a testsuite run, after Expect has
hung up but GDB is still alive. In such a case, we just quit
gdb killing the inferior program too. */
printf_unfiltered ("quit\n");
execute_command ("quit", stdin == instream);
}
else if (cmd == NULL)
{
/* We don't have a full line yet. Print an empty prompt. */
display_gdb_prompt ("");
}
else
{
command_handler (cmd);
display_gdb_prompt (0);
}
}
/* Does reading of input from terminal w/o the editing features

View File

@ -34,6 +34,8 @@ extern void async_init_signals (void);
extern void set_async_editing_command (char *args, int from_tty,
struct cmd_list_element *c);
extern void command_handler (char *command);
/* Signal to catch ^Z typed while reading a command: SIGTSTP or SIGCONT. */
#ifndef STOP_SIGNAL
#include <signal.h>

170
gdb/top.c
View File

@ -533,37 +533,17 @@ execute_command_to_string (char *p, int from_tty)
void
command_loop (void)
{
struct cleanup *old_chain;
char *command;
while (instream && !feof (instream))
{
clear_quit_flag ();
if (instream == stdin)
reinitialize_more_filter ();
old_chain = make_cleanup (null_cleanup, 0);
char *command;
/* Get a command-line. This calls the readline package. */
command = command_line_input (instream == stdin ?
get_prompt () : (char *) NULL,
instream == stdin, "prompt");
if (command == 0)
{
do_cleanups (old_chain);
return;
}
make_command_stats_cleanup (1);
/* Do not execute commented lines. */
if (command[0] != '#')
{
execute_command (command, instream == stdin);
/* Do any commands attached to breakpoint we are stopped at. */
bpstat_do_actions ();
}
do_cleanups (old_chain);
if (command == NULL)
return;
command_handler (command);
}
}
@ -1016,32 +996,26 @@ gdb_safe_append_history (void)
do_cleanups (old_chain);
}
/* Read one line from the command input stream `instream'
into the local static buffer `linebuffer' (whose current length
is `linelength').
The buffer is made bigger as necessary.
Returns the address of the start of the line.
/* Read one line from the command input stream `instream' into a local
static buffer. The buffer is made bigger as necessary. Returns
the address of the start of the line.
NULL is returned for end of file.
*If* the instream == stdin & stdin is a terminal, the line read
is copied into the file line saver (global var char *line,
length linesize) so that it can be duplicated.
*If* the instream == stdin & stdin is a terminal, the line read is
copied into the global 'saved_command_line' so that it can be
repeated.
This routine either uses fancy command line editing or
simple input as the user has requested. */
This routine either uses fancy command line editing or simple input
as the user has requested. */
char *
command_line_input (const char *prompt_arg, int repeat, char *annotation_suffix)
{
static char *linebuffer = 0;
static unsigned linelength = 0;
static struct buffer cmd_line_buffer;
static int cmd_line_buffer_initialized;
const char *prompt = prompt_arg;
char *p;
char *p1;
char *rl;
char *nline;
char got_eof = 0;
char *cmd;
/* The annotation suffix must be non-NULL. */
if (annotation_suffix == NULL)
@ -1065,13 +1039,14 @@ command_line_input (const char *prompt_arg, int repeat, char *annotation_suffix)
prompt = local_prompt;
}
if (linebuffer == 0)
if (!cmd_line_buffer_initialized)
{
linelength = 80;
linebuffer = (char *) xmalloc (linelength);
buffer_init (&cmd_line_buffer);
cmd_line_buffer_initialized = 1;
}
p = linebuffer;
/* Starting a new command line. */
cmd_line_buffer.used_size = 0;
/* Control-C quits instantly if typed while in this loop
since it should not wait until the user types a newline. */
@ -1084,6 +1059,8 @@ command_line_input (const char *prompt_arg, int repeat, char *annotation_suffix)
while (1)
{
char *rl;
/* Make sure that all output has been output. Some machines may
let you get away with leaving out some of the gdb_flush, but
not all. */
@ -1115,37 +1092,16 @@ command_line_input (const char *prompt_arg, int repeat, char *annotation_suffix)
rl = gdb_readline_no_editing (prompt);
}
if (annotation_level > 1 && instream == stdin)
cmd = handle_line_of_input (&cmd_line_buffer, rl,
repeat, annotation_suffix);
if (cmd == (char *) EOF)
{
puts_unfiltered ("\n\032\032post-");
puts_unfiltered (annotation_suffix);
puts_unfiltered ("\n");
}
if (!rl || rl == (char *) EOF)
{
got_eof = 1;
cmd = NULL;
break;
}
if (strlen (rl) + 1 + (p - linebuffer) > linelength)
{
linelength = strlen (rl) + 1 + (p - linebuffer);
nline = (char *) xrealloc (linebuffer, linelength);
p += nline - linebuffer;
linebuffer = nline;
}
p1 = rl;
/* Copy line. Don't copy null at end. (Leaves line alone
if this was just a newline). */
while (*p1)
*p++ = *p1++;
xfree (rl); /* Allocated in readline. */
if (p == linebuffer || *(p - 1) != '\\')
if (cmd != NULL)
break;
p--; /* Put on top of '\'. */
prompt = NULL;
}
@ -1155,77 +1111,7 @@ command_line_input (const char *prompt_arg, int repeat, char *annotation_suffix)
#endif
immediate_quit--;
if (got_eof)
return NULL;
#define SERVER_COMMAND_LENGTH 7
server_command =
(p - linebuffer > SERVER_COMMAND_LENGTH)
&& strncmp (linebuffer, "server ", SERVER_COMMAND_LENGTH) == 0;
if (server_command)
{
/* Note that we don't set `line'. Between this and the check in
dont_repeat, this insures that repeating will still do the
right thing. */
*p = '\0';
return linebuffer + SERVER_COMMAND_LENGTH;
}
/* Do history expansion if that is wished. */
if (history_expansion_p && instream == stdin
&& ISATTY (instream))
{
char *history_value;
int expanded;
*p = '\0'; /* Insert null now. */
expanded = history_expand (linebuffer, &history_value);
if (expanded)
{
/* Print the changes. */
printf_unfiltered ("%s\n", history_value);
/* If there was an error, call this function again. */
if (expanded < 0)
{
xfree (history_value);
return command_line_input (prompt, repeat,
annotation_suffix);
}
if (strlen (history_value) > linelength)
{
linelength = strlen (history_value) + 1;
linebuffer = (char *) xrealloc (linebuffer, linelength);
}
strcpy (linebuffer, history_value);
p = linebuffer + strlen (linebuffer);
}
xfree (history_value);
}
/* If we just got an empty line, and that is supposed to repeat the
previous command, return the value in the global buffer. */
if (repeat && p == linebuffer)
return saved_command_line;
for (p1 = linebuffer; *p1 == ' ' || *p1 == '\t'; p1++);
if (repeat && !*p1)
return saved_command_line;
*p = 0;
/* Add line to history if appropriate. */
if (*linebuffer && input_from_terminal_p ())
gdb_add_history (linebuffer);
/* Save into global buffer if appropriate. */
if (repeat)
{
xfree (saved_command_line);
saved_command_line = xstrdup (linebuffer);
return saved_command_line;
}
return linebuffer;
return cmd;
}
/* Print the GDB banner. */

View File

@ -20,6 +20,8 @@
#ifndef TOP_H
#define TOP_H
struct buffer;
/* From top.c. */
extern char *saved_command_line;
extern FILE *instream;
@ -97,4 +99,8 @@ extern void set_verbose (char *, int, struct cmd_list_element *);
extern void do_restore_instream_cleanup (void *stream);
extern char *handle_line_of_input (struct buffer *cmd_line_buffer,
char *rl, int repeat,
char *annotation_suffix);
#endif