From patchwork Wed Feb 3 16:43:37 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pedro Alves X-Patchwork-Id: 10708 Received: (qmail 25108 invoked by alias); 3 Feb 2016 16:44:19 -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 24888 invoked by uid 89); 3 Feb 2016 16:44:17 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=0.4 required=5.0 tests=BAYES_50, RP_MATCHES_RCVD, SPF_HELO_PASS autolearn=ham version=3.3.2 spammy=sk:mi_inte, 14320, Leaves, saver 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; Wed, 03 Feb 2016 16:44:02 +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 97B87C0293B3 for ; Wed, 3 Feb 2016 16:44:01 +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 u13GhwPK022971 for ; Wed, 3 Feb 2016 11:44:00 -0500 From: Pedro Alves To: gdb-patches@sourceware.org Subject: [PATCH 02/23] Command line input handling TLC Date: Wed, 3 Feb 2016 16:43:37 +0000 Message-Id: <1454517838-7784-3-git-send-email-palves@redhat.com> In-Reply-To: <1454517838-7784-1-git-send-email-palves@redhat.com> References: <1454517838-7784-1-git-send-email-palves@redhat.com> The main motivation here was factor out all the globals used by the line input handling code to a structure, so that we can keep multiple instances of that. But I found that this code is a lot messier than it needs to be, hence this patch cleans things up significantly in the process as well. --- gdb/common/buffer.h | 8 + gdb/defs.h | 2 - gdb/event-top.c | 457 ++++++++++++++++++++++------------------------------ gdb/event-top.h | 5 +- gdb/inflow.c | 2 +- gdb/inflow.h | 4 + gdb/main.c | 3 +- gdb/mi/mi-interp.c | 2 +- gdb/top.c | 239 ++++++--------------------- gdb/top.h | 7 +- 10 files changed, 267 insertions(+), 462 deletions(-) diff --git a/gdb/common/buffer.h b/gdb/common/buffer.h index 2a47db4..bad2c3a 100644 --- a/gdb/common/buffer.h +++ b/gdb/common/buffer.h @@ -31,6 +31,14 @@ struct buffer accommodate the new data. */ void buffer_grow (struct buffer *buffer, const char *data, size_t size); +/* Append CH to the end of BUFFER. Grows the buffer to accommodate + the new data. */ +static inline void +buffer_grow_char (struct buffer *buffer, char c) +{ + buffer_grow (buffer, &c, 1); +} + /* Release any memory held by BUFFER. */ void buffer_free (struct buffer *buffer); diff --git a/gdb/defs.h b/gdb/defs.h index f6ffeac..b94df30 100644 --- a/gdb/defs.h +++ b/gdb/defs.h @@ -285,8 +285,6 @@ extern void print_transfer_performance (struct ui_file *stream, typedef void initialize_file_ftype (void); -extern char *gdb_readline (const char *); - extern char *gdb_readline_wrapper (const char *); extern char *command_line_input (const char *, int, char *); diff --git a/gdb/event-top.c b/gdb/event-top.c index 2309cce..969124e 100644 --- a/gdb/event-top.c +++ b/gdb/event-top.c @@ -37,6 +37,9 @@ #include "gdbcmd.h" /* for dont_repeat() */ #include "annotate.h" #include "maint.h" +#include "inflow.h" +#include "serial.h" +#include "buffer.h" /* readline include files. */ #include "readline/readline.h" @@ -48,7 +51,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. */ @@ -107,10 +109,6 @@ void (*call_readline) (gdb_client_data); loop as default engine, and event-top.c is merged into top.c. */ int async_command_editing_p; -/* This is the annotation suffix that will be used when the - annotation_level is 2. */ -char *async_annotation_suffix; - /* This is used to display the notification of the completion of an asynchronous execution command. */ int exec_done_display_p = 0; @@ -143,20 +141,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); @@ -211,7 +195,7 @@ change_line_handler (void) { /* Turn off editing by using gdb_readline2. */ gdb_rl_callback_handler_remove (); - call_readline = gdb_readline2; + call_readline = gdb_readline_callback_no_editing; /* Set up the command handler as well, in case we are called as first thing from .gdbinit. */ @@ -363,55 +347,45 @@ display_gdb_prompt (const char *new_prompt) static char * top_level_prompt (void) { - char *prefix; - char *prompt = NULL; - char *suffix; - char *composed_prompt; - size_t prompt_length; + char *prompt; /* Give observers a chance of changing the prompt. E.g., the python `gdb.prompt_hook' is installed as an observer. */ observer_notify_before_prompt (get_prompt ()); - prompt = xstrdup (get_prompt ()); + prompt = get_prompt (); if (annotation_level >= 2) { /* Prefix needs to have new line at end. */ - prefix = (char *) alloca (strlen (async_annotation_suffix) + 10); - strcpy (prefix, "\n\032\032pre-"); - strcat (prefix, async_annotation_suffix); - strcat (prefix, "\n"); + const char prefix[] = "\n\032\032pre-prompt\n"; /* Suffix needs to have a new line at end and \032 \032 at beginning. */ - suffix = (char *) alloca (strlen (async_annotation_suffix) + 6); - strcpy (suffix, "\n\032\032"); - strcat (suffix, async_annotation_suffix); - strcat (suffix, "\n"); - } - else - { - prefix = ""; - suffix = ""; + const char suffix[] = "\n\032\032prompt\n"; + + return concat (prefix, prompt, suffix, NULL); } - prompt_length = strlen (prefix) + strlen (prompt) + strlen (suffix); - composed_prompt = (char *) xmalloc (prompt_length + 1); + return xstrdup (prompt); +} - strcpy (composed_prompt, prefix); - strcat (composed_prompt, prompt); - strcat (composed_prompt, suffix); +/* Get a pointer to the command line builder. This is to used to + construct a whole line of input from partial input. */ - xfree (prompt); +static struct buffer * +get_line_builder (void) +{ + static struct buffer line_builder; - return composed_prompt; + return &line_builder; } -/* When there is an event ready on the stdin file desriptor, instead +/* 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_readline2, give gdb a chance to detect errors and do something. */ + void stdin_event_handler (int error, gdb_client_data client_data) { @@ -460,156 +434,120 @@ 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 to the buffer maintained by CMD_BUILDER. Returns false if + more input is expected (line ends in a backslash), true if we have + a whole command line. 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 * +cmd_builder_append (struct buffer *cmd_builder, 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) + len = strlen (rl); + + if (len > 0 && rl[len - 1] == '\\') { - printf_unfiltered (("\n\032\032post-")); - puts_unfiltered (async_annotation_suffix); - printf_unfiltered (("\n")); + /* Don't copy the backslash and wait for more. */ + buffer_grow (cmd_builder, rl, len - 1); + cmd = NULL; } - - if (linebuffer == 0) + else { - linelength = 80; - linebuffer = (char *) xmalloc (linelength); - linebuffer[0] = '\0'; + /* Copy whole line including terminating null, and we're + done. */ + buffer_grow (cmd_builder, rl, len + 1); + cmd = cmd_builder->buffer; } - p = linebuffer; + /* Allocated in readline. */ + xfree (rl); - if (more_to_come) - { - strcpy (linebuffer, readline_input_state.linebuffer); - p = readline_input_state.linebuffer_ptr; - xfree (readline_input_state.linebuffer); - more_to_come = 0; - } + return cmd; +} -#ifdef STOP_SIGNAL - if (job_control) - signal (STOP_SIGNAL, handle_stop_sig); -#endif +/* Handle a line of input coming from readline. - /* 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 the read line ends with a continuation character (backslash), + save the partial input in CMD_BUILDER (except the backslash), and + return NULL. Otherwise, save the partial input and return a + pointer to CMD_BUILDER's buffer (null terminated), indicating a + whole command line is ready to be executed. - if (source_file_name != NULL) - ++source_line_number; + Returns EOF on end of file. - /* 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 REPEAT, handle command repetitions: - xfree (rl); /* Allocated in readline. */ + - 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. - if (p > linebuffer && *(p - 1) == '\\') - { - *p = '\0'; - p--; /* Put on top of '\'. */ + - OTOH, if the input command line IS empty, return the previously + saved command instead of the empty input line. +*/ - readline_input_state.linebuffer = xstrdup (linebuffer); - readline_input_state.linebuffer_ptr = p; +char * +handle_line_of_input (struct buffer *cmd_builder, + char *rl, int repeat, char *annotation_suffix) +{ + char *p1; + char *cmd; - /* 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; - } + if (rl == NULL) + return (char *) EOF; -#ifdef STOP_SIGNAL - if (job_control) - signal (STOP_SIGNAL, SIG_DFL); -#endif + cmd = cmd_builder_append (cmd_builder, 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_builder->used_size = 0; -#define SERVER_COMMAND_LENGTH 7 - server_command = - (p - linebuffer > SERVER_COMMAND_LENGTH) - && strncmp (linebuffer, "server ", SERVER_COMMAND_LENGTH) == 0; - if (server_command) + if (annotation_level > 1 && instream == stdin) { - /* 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; + printf_unfiltered (("\n\032\032post-")); + puts_unfiltered (annotation_suffix); + printf_unfiltered (("\n")); + } + +#define SERVER_COMMAND_PREFIX "server " + if (startswith (cmd, SERVER_COMMAND_PREFIX)) + { + /* 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. */ @@ -619,10 +557,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); @@ -630,102 +569,93 @@ 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_builder)); + cmd_builder->buffer = history_value; + cmd_builder->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 && 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 *cmd_builder = get_line_builder (); + char *cmd; - /* Save into global buffer if appropriate. */ - if (repeat) + cmd = handle_line_of_input (cmd_builder, rl, instream == stdin, "prompt"); + if (cmd == (char *) EOF) { - if (linelength > saved_command_line_size) - { - saved_command_line - = (char *) xrealloc (saved_command_line, linelength); - saved_command_line_size = linelength; - } - strcpy (saved_command_line, 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 - provided by the readline library. */ + provided by the readline library. Calls the line input handler + once we have a whole input line. */ -/* NOTE: 1999-04-30 Asynchronous version of gdb_readline; gdb_readline - will become obsolete when the event loop is made the default - execution for gdb. */ void -gdb_readline2 (gdb_client_data client_data) +gdb_readline_callback_no_editing (gdb_client_data client_data) { + struct buffer *builder = get_line_builder (); int c; - char *result; - int input_index = 0; - int result_size = 80; - static int done_once = 0; - - /* Unbuffer the input stream, so that, later on, the calls to fgetc - fetch only one char at the time from the stream. The fgetc's will - get up to the first newline, but there may be more chars in the - stream after '\n'. If we buffer the input and fgetc drains the - stream, getting stuff beyond the newline as well, a select, done - afterwards will not trigger. */ - if (!done_once && !ISATTY (instream)) - { - setbuf (instream, NULL); - done_once = 1; - } - - result = (char *) xmalloc (result_size); + char *r; /* We still need the while loop here, even though it would seem obvious to invoke gdb_readline2 at every character entered. If @@ -736,39 +666,44 @@ gdb_readline2 (gdb_client_data client_data) while (1) { - /* Read from stdin if we are executing a user defined command. - This is the right thing for prompt_for_continue, at least. */ - c = fgetc (instream ? instream : stdin); + /* A non-blocking read. */ + c = serial_readchar (stdin_serial, 0); - if (c == EOF) + if (c == SERIAL_ERROR) { - if (input_index > 0) - /* The last line does not end with a newline. Return it, - and if we are called again fgetc will still return EOF - and we'll return NULL then. */ - break; - xfree (result); - (*input_handler) (0); + /* No chars left. Go back to event loop. */ return; } - if (c == '\n') + if (c == SERIAL_EOF) { - if (input_index > 0 && result[input_index - 1] == '\r') - input_index--; - break; + if (builder->used_size > 0) + { + /* The last line does not end with a newline. Return + it, and if we are called again serial_readchar will + still return EOF and we'll signal error then. */ + break; + } + xfree (buffer_finish (builder)); + (*input_handler) (NULL); + return; } - result[input_index++] = c; - while (input_index >= result_size) + if (c == '\n') { - result_size *= 2; - result = (char *) xrealloc (result, result_size); + if (builder->used_size > 0 + && builder->buffer[builder->used_size - 1] == '\r') + builder->used_size--; + break; } + + buffer_grow_char (builder, c); } - result[input_index++] = '\0'; - (*input_handler) (result); + buffer_grow_char (builder, '\0'); + + r = buffer_finish (builder); + (*input_handler) (r); } @@ -1055,7 +990,7 @@ gdb_setup_readline (void) else { async_command_editing_p = 0; - call_readline = gdb_readline2; + call_readline = gdb_readline_callback_no_editing; } /* When readline has read an end-of-line character, it passes the diff --git a/gdb/event-top.h b/gdb/event-top.h index 4f20770..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 @@ -44,7 +46,6 @@ extern void handle_stop_sig (int sig); #endif extern void handle_sigint (int sig); extern void handle_sigterm (int sig); -extern void gdb_readline2 (void *client_data); extern void async_request_quit (void *arg); extern void stdin_event_handler (int error, void *client_data); extern void async_disable_stdin (void); @@ -55,13 +56,13 @@ extern void async_enable_stdin (void); extern int async_command_editing_p; extern int exec_done_display_p; -extern char *async_annotation_suffix; extern struct prompts the_prompts; extern void (*call_readline) (void *); extern void (*input_handler) (char *); extern int input_fd; extern void (*after_char_processing_hook) (void); extern int call_stdin_event_handler_again_p; +extern void gdb_readline_callback_no_editing (void *client_data); /* Wrappers for rl_callback_handler_remove and rl_callback_handler_install that keep track of whether the callback diff --git a/gdb/inflow.c b/gdb/inflow.c index 4c80dbd..4ccd806 100644 --- a/gdb/inflow.c +++ b/gdb/inflow.c @@ -48,7 +48,7 @@ static void child_terminal_ours_1 (int); /* Record terminal status separately for debugger and inferior. */ -static struct serial *stdin_serial; +struct serial *stdin_serial; /* Terminal related info we need to keep track of. Each inferior holds an instance of this structure --- we save it whenever the diff --git a/gdb/inflow.h b/gdb/inflow.h index 0d0242e..1c2278e 100644 --- a/gdb/inflow.h +++ b/gdb/inflow.h @@ -33,4 +33,8 @@ extern PROCESS_GROUP_TYPE inferior_process_group (void); #endif +/* The serial object that wraps stdin. */ + +extern struct serial *stdin_serial; + #endif /* inflow.h */ diff --git a/gdb/main.c b/gdb/main.c index a338b90..93ed98f 100644 --- a/gdb/main.c +++ b/gdb/main.c @@ -506,8 +506,7 @@ captured_main (void *data) ndir = 0; clear_quit_flag (); - saved_command_line = (char *) xmalloc (saved_command_line_size); - saved_command_line[0] = '\0'; + saved_command_line = (char *) xstrdup (""); instream = stdin; #ifdef __MINGW32__ diff --git a/gdb/mi/mi-interp.c b/gdb/mi/mi-interp.c index 7f42367..80c8259 100644 --- a/gdb/mi/mi-interp.c +++ b/gdb/mi/mi-interp.c @@ -181,7 +181,7 @@ mi_interpreter_resume (void *data) /* These overwrite some of the initialization done in _intialize_event_loop. */ - call_readline = gdb_readline2; + call_readline = gdb_readline_callback_no_editing; input_handler = mi_execute_command_input_handler; async_command_editing_p = 0; /* FIXME: This is a total hack for now. PB's use of the MI diff --git a/gdb/top.c b/gdb/top.c index ed200ba..8e5489e 100644 --- a/gdb/top.c +++ b/gdb/top.c @@ -67,6 +67,7 @@ #include "cli-out.h" #include "tracepoint.h" #include "inf-loop.h" +#include "buffer.h" #if defined(TUI) # include "tui/tui.h" @@ -124,17 +125,9 @@ char *current_directory; /* The directory name is actually stored here (usually). */ char gdb_dirbuf[1024]; -/* Function to call before reading a command, if nonzero. - The function receives two args: an input stream, - and a prompt string. */ - -void (*window_hook) (FILE *, char *); - -/* Buffer used for reading command lines, and the size - allocated for it so far. */ - +/* The last command line executed on the console. Used for command + repetitions. */ char *saved_command_line; -int saved_command_line_size = 100; /* Nonzero if the current command is modified by "server ". This affects things like recording into the command history, commands @@ -540,40 +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)) { - if (window_hook && instream == stdin) - (*window_hook) (instream, get_prompt ()); - - 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); } } @@ -612,64 +582,60 @@ prevent_dont_repeat (void) /* Read a line from the stream "instream" without command line editing. - It prints PROMPT_ARG once at the start. + It prints PROMPT once at the start. Action is compatible with "readline", e.g. space for the result is malloc'd and should be freed by the caller. A NULL return means end of file. */ -char * -gdb_readline (const char *prompt_arg) + +static char * +gdb_readline_no_editing (const char *prompt) { - int c; - char *result; - int input_index = 0; - int result_size = 80; + struct buffer line_builder; + + buffer_init (&line_builder); - if (prompt_arg) + if (prompt != NULL) { /* Don't use a _filtered function here. It causes the assumed character position to be off, since the newline we read from the user is not accounted for. */ - fputs_unfiltered (prompt_arg, gdb_stdout); + fputs_unfiltered (prompt, gdb_stdout); gdb_flush (gdb_stdout); } - result = (char *) xmalloc (result_size); - while (1) { + int c; + /* Read from stdin if we are executing a user defined command. This is the right thing for prompt_for_continue, at least. */ c = fgetc (instream ? instream : stdin); if (c == EOF) { - if (input_index > 0) + if (line_builder.used_size > 0) /* The last line does not end with a newline. Return it, and if we are called again fgetc will still return EOF and we'll return NULL then. */ break; - xfree (result); + xfree (buffer_finish (&line_builder)); return NULL; } if (c == '\n') { - if (input_index > 0 && result[input_index - 1] == '\r') - input_index--; + if (line_builder.used_size > 0 + && line_builder.buffer[line_builder.used_size - 1] == '\r') + line_builder.used_size--; break; } - result[input_index++] = c; - while (input_index >= result_size) - { - result_size *= 2; - result = (char *) xrealloc (result, result_size); - } + buffer_grow_char (&line_builder, c); } - result[input_index++] = '\0'; - return result; + buffer_grow_char (&line_builder, '\0'); + return buffer_finish (&line_builder); } /* Variables which control command line editing and history @@ -1030,32 +996,28 @@ 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 builder; const char *prompt = prompt_arg; - char *p; - char *p1; - char *rl; - char *nline; - char got_eof = 0; + char *cmd; + + /* Starting a new command line. */ + builder.used_size = 0; /* The annotation suffix must be non-NULL. */ if (annotation_suffix == NULL) @@ -1079,14 +1041,6 @@ command_line_input (const char *prompt_arg, int repeat, char *annotation_suffix) prompt = local_prompt; } - if (linebuffer == 0) - { - linelength = 80; - linebuffer = (char *) xmalloc (linelength); - } - - p = linebuffer; - /* Control-C quits instantly if typed while in this loop since it should not wait until the user types a newline. */ immediate_quit++; @@ -1098,6 +1052,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. */ @@ -1126,40 +1082,18 @@ command_line_input (const char *prompt_arg, int repeat, char *annotation_suffix) } else { - rl = gdb_readline (prompt); + rl = gdb_readline_no_editing (prompt); } - if (annotation_level > 1 && instream == stdin) + cmd = handle_line_of_input (&builder, 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; } @@ -1169,82 +1103,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) - { - if (linelength > saved_command_line_size) - { - saved_command_line - = (char *) xrealloc (saved_command_line, linelength); - saved_command_line_size = linelength; - } - strcpy (saved_command_line, linebuffer); - return saved_command_line; - } - - return linebuffer; + return cmd; } /* Print the GDB banner. */ @@ -1906,10 +1765,6 @@ init_main (void) the DEFAULT_PROMPT is. */ set_prompt (DEFAULT_PROMPT); - /* Set things up for annotation_level > 1, if the user ever decides - to use it. */ - async_annotation_suffix = "prompt"; - /* Set the important stuff up for command editing. */ command_editing_p = 1; history_expansion_p = 0; diff --git a/gdb/top.h b/gdb/top.h index c450c6e..d5f1464 100644 --- a/gdb/top.h +++ b/gdb/top.h @@ -20,9 +20,10 @@ #ifndef TOP_H #define TOP_H +struct buffer; + /* From top.c. */ extern char *saved_command_line; -extern int saved_command_line_size; extern FILE *instream; extern int in_user_command; extern int confirm; @@ -98,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 *builder, + char *rl, int repeat, + char *annotation_suffix); + #endif