[PATCH 02/23] Command line input handling TLC

Pedro Alves palves@redhat.com
Wed Feb 3 16:44:00 GMT 2016


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 <signal.h>
@@ -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
-- 
1.9.3



More information about the Gdb-patches mailing list