From patchwork Thu Feb 18 17:40:36 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pedro Alves X-Patchwork-Id: 10904 Received: (qmail 37141 invoked by alias); 18 Feb 2016 17:49:37 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 37106 invoked by uid 89); 18 Feb 2016 17:49:34 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-1.9 required=5.0 tests=BAYES_00, RP_MATCHES_RCVD, SPF_HELO_PASS autolearn=ham version=3.3.2 spammy=execute_command, Control-C, ControlC, Leaves X-HELO: mx1.redhat.com Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES256-GCM-SHA384 encrypted) ESMTPS; Thu, 18 Feb 2016 17:49:32 +0000 Received: from int-mx11.intmail.prod.int.phx2.redhat.com (int-mx11.intmail.prod.int.phx2.redhat.com [10.5.11.24]) by mx1.redhat.com (Postfix) with ESMTPS id D4F6CC00F6E0 for ; Thu, 18 Feb 2016 17:40:45 +0000 (UTC) Received: from brno.lan (ovpn01.gateway.prod.ext.phx2.redhat.com [10.5.9.1]) by int-mx11.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id u1IHea7G012351 for ; Thu, 18 Feb 2016 12:40:45 -0500 From: Pedro Alves To: gdb-patches@sourceware.org Subject: [PATCH 10/10] Command line input handling TLC Date: Thu, 18 Feb 2016 17:40:36 +0000 Message-Id: <1455817236-13642-11-git-send-email-palves@redhat.com> In-Reply-To: <1455817236-13642-1-git-send-email-palves@redhat.com> References: <1455817236-13642-1-git-send-email-palves@redhat.com> I didn't manage to usefully split this further into smaller independent pieces, so: - Use "struct buffer" more. - Split out the responsability 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-02-18 Pedro Alves * 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. --- gdb/event-top.c | 340 +++++++++++++++++++++++++++----------------------------- gdb/event-top.h | 2 + gdb/top.c | 170 +++++----------------------- gdb/top.h | 6 + 4 files changed, 200 insertions(+), 318 deletions(-) diff --git a/gdb/event-top.c b/gdb/event-top.c index 221e242..265e511 100644 --- a/gdb/event-top.c +++ b/gdb/event-top.c @@ -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_callback_no_editing, 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 false if more input is + expected (input line ends in a backslash), true if we have a whole + command line ready to be processed by the command interpreter. + 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) { - static char *linebuffer = 0; - static unsigned linelength = 0; - char *p; - char *p1; - char *nline; - int repeat = (instream == stdin); + char *cmd; + size_t len; - if (annotation_level > 1 && instream == stdin) - printf_unfiltered (("\n\032\032post-prompt\n")); + len = strlen (rl); - if (linebuffer == 0) + if (len > 0 && rl[len - 1] == '\\') { - linelength = 80; - linebuffer = (char *) xmalloc (linelength); - linebuffer[0] = '\0'; + /* Don't copy the backslash and wait for more. */ + buffer_grow (cmd_line_buffer, rl, len - 1); + cmd = NULL; } - - p = linebuffer; - - if (more_to_come) + else { - strcpy (linebuffer, readline_input_state.linebuffer); - p = readline_input_state.linebuffer_ptr; - xfree (readline_input_state.linebuffer); - more_to_come = 0; + /* Copy whole line including terminating null, and we're + done. */ + buffer_grow (cmd_line_buffer, rl, len + 1); + cmd = cmd_line_buffer->buffer; } -#ifdef STOP_SIGNAL - if (job_control) - signal (STOP_SIGNAL, handle_stop_sig); -#endif + /* Allocated in readline. */ + xfree (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. */ - wrap_here (""); - gdb_flush (gdb_stdout); - gdb_flush (gdb_stderr); + return cmd; +} - if (source_file_name != NULL) - ++source_line_number; +/* Handle a line of input coming from readline. - /* 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++; + 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. - xfree (rl); /* Allocated in readline. */ + Returns EOF on end of file. - if (p > linebuffer && *(p - 1) == '\\') - { - *p = '\0'; - p--; /* Put on top of '\'. */ + If REPEAT, handle command repetitions: - readline_input_state.linebuffer = xstrdup (linebuffer); - readline_input_state.linebuffer_ptr = p; + - 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. - /* 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; - } + - OTOH, if the input command line IS empty, return the previously + saved command instead of the empty input line. +*/ -#ifdef STOP_SIGNAL - if (job_control) - signal (STOP_SIGNAL, SIG_DFL); -#endif +char * +handle_line_of_input (struct buffer *cmd_line_buffer, + char *rl, int repeat, char *annotation_suffix) +{ + char *p1; + 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-")); + puts_unfiltered (annotation_suffix); + printf_unfiltered (("\n")); + } -#define SERVER_COMMAND_LENGTH 7 - server_command = - (p - linebuffer > SERVER_COMMAND_LENGTH) - && strncmp (linebuffer, "server ", SERVER_COMMAND_LENGTH) == 0; - if (server_command) +#define SERVER_COMMAND_PREFIX "server " + if (startswith (cmd, SERVER_COMMAND_PREFIX)) { - /* 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; + + /* 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); - for (p1 = linebuffer; *p1 == ' ' || *p1 == '\t'; p1++); - if (repeat && !*p1) + /* Save into global buffer if appropriate. */ + if (repeat) { - command_handler (saved_command_line); - display_gdb_prompt (0); - return; + xfree (saved_command_line); + saved_command_line = xstrdup (cmd); + return saved_command_line; } + else + return cmd; +} - *p = 0; +/* 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. - /* Add line to history if appropriate. */ - if (*linebuffer && input_from_terminal_p ()) - gdb_add_history (linebuffer); + NOTE: This is the asynchronous version of the command_line_input + function. */ - /* 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. */ +void +command_line_handler (char *rl) +{ + struct buffer *line_buffer = get_command_line_buffer (); + char *cmd; - /* Save into global buffer if appropriate. */ - if (repeat) + cmd = handle_line_of_input (line_buffer, rl, instream == stdin, "prompt"); + if (cmd == (char *) EOF) { - xfree (saved_command_line); - saved_command_line = xstrdup (linebuffer); - if (!more_to_come) - { - command_handler (saved_command_line); - display_gdb_prompt (0); - } - return; + /* 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); } - - command_handler (linebuffer); - display_gdb_prompt (0); - return; } /* Does reading of input from terminal w/o the editing features diff --git a/gdb/event-top.h b/gdb/event-top.h index 1a79d62..44e2041 100644 --- a/gdb/event-top.h +++ b/gdb/event-top.h @@ -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 diff --git a/gdb/top.c b/gdb/top.c index 1a5c3f9..89fe832 100644 --- a/gdb/top.c +++ b/gdb/top.c @@ -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. */ diff --git a/gdb/top.h b/gdb/top.h index f3b080b..a498f39 100644 --- a/gdb/top.h +++ b/gdb/top.h @@ -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