* defs.h (enum misc_command_type, command_control_type): Enums
authorJeff Law <law@redhat.com>
Thu, 8 Sep 1994 05:32:34 +0000 (05:32 +0000)
committerJeff Law <law@redhat.com>
Thu, 8 Sep 1994 05:32:34 +0000 (05:32 +0000)
for describing the command and control types.
(struct command_line): Add new fields to keep track of the command
type and body associated with the command.
* top.c: Include value.h.  Delete whitespace at the end of lines.
(build_command_line, get_command_line): New functions.
(execute_control_command, while_command, if_command): Likewise.
(realloc_body_list, read_next_line): Likewise.
(recurse_read_control_structure): Likewise.
(execute_user_command): Call execute_control_command.
(read_command_lines): Simplify by calling read_next_line, call
read_control_structure for "if" and "while" commands.
(free_command_lines): Free new fields in the command structure.
(define_command): Reset control_level to zero.
(init_main): Install command handlers for "if" and "while" commands.

gdb/ChangeLog
gdb/defs.h
gdb/top.c

index 5520f01ecad45f1cd394839eb47d3fb0b5875b11..b9acd796d298ec63590769f75e5c3402ab9917a1 100644 (file)
@@ -1,3 +1,21 @@
+Wed Sep  7 23:24:50 1994  Jeff Law  (law@snake.cs.utah.edu)
+
+       * defs.h (enum misc_command_type, command_control_type): Enums
+       for describing the command and control types.
+       (struct command_line): Add new fields to keep track of the command
+       type and body associated with the command.
+       * top.c: Include value.h.  Delete whitespace at the end of lines.
+       (build_command_line, get_command_line): New functions.
+       (execute_control_command, while_command, if_command): Likewise.
+       (realloc_body_list, read_next_line): Likewise.
+       (recurse_read_control_structure): Likewise.
+       (execute_user_command): Call execute_control_command.
+       (read_command_lines): Simplify by calling read_next_line, call
+       read_control_structure for "if" and "while" commands.
+       (free_command_lines): Free new fields in the command structure.
+       (define_command): Reset control_level to zero.
+       (init_main): Install command handlers for "if" and "while" commands.
+
 Tue Sep  6 16:24:07 1994  Stan Shebs  (shebs@andros.cygnus.com)
 
         * c-typeprint.c (c_type_print_varspec_prefix,
index 728dccfa0c030850439931fc8480255731b04f0d..4e98e2129346c7f37e4d0e2a3e71da398e3811a2 100644 (file)
@@ -97,6 +97,7 @@ enum language
    language_c,                         /* C */
    language_cplus,             /* C++ */
    language_chill,             /* Chill */
+   language_fortran,           /* Fortran */
    language_m2,                        /* Modula-2 */
    language_asm                        /* Assembly language */
 };
@@ -317,6 +318,26 @@ extern int read_relative_register_raw_bytes PARAMS ((int, char *));
 
 extern char *tilde_expand PARAMS ((char *));
 
+/* Control types for commands */
+
+enum misc_command_type
+{
+  ok_command,
+  end_command,
+  else_command,
+  nop_command,
+};
+
+enum command_control_type
+{
+  simple_control,
+  break_control,
+  continue_control,
+  while_control,
+  if_control,
+  invalid_control
+};
+
 /* Structure for saved commands lines
    (for breakpoints, defined commands, etc).  */
 
@@ -324,6 +345,9 @@ struct command_line
 {
   struct command_line *next;
   char *line;
+  enum command_control_type control_type;
+  int body_count;
+  struct command_line **body_list;
 };
 
 extern struct command_line *read_command_lines PARAMS ((void));
index 27616aeca7ac7505c9e3aab7d76d92ed6303afd6..2b5e9afe7a948dcd3c87dfda5e6e0761c5028cf5 100644 (file)
--- a/gdb/top.c
+++ b/gdb/top.c
@@ -28,6 +28,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #include "breakpoint.h"
 #include "gdbtypes.h"
 #include "expression.h"
+#include "value.h"
 #include "language.h"
 #include "terminal.h" /* For job_control.  */
 #include "annotate.h"
@@ -57,80 +58,76 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
 
 /* Prototypes for local functions */
 
-static char *
-symbol_completion_function PARAMS ((char *, int));
+static char * symbol_completion_function PARAMS ((char *, int));
 
-static void
-command_loop_marker PARAMS ((int));
+static void command_loop_marker PARAMS ((int));
 
-static void
-init_main PARAMS ((void));
+static void while_command PARAMS ((char *, int));
 
-static void
-init_cmd_lists PARAMS ((void));
+static void if_command PARAMS ((char *, int));
 
-static void
-float_handler PARAMS ((int));
+static enum command_control_type 
+execute_control_command PARAMS ((struct command_line *));
 
-static void
-init_signals PARAMS ((void));
+static struct command_line *
+build_command_line PARAMS ((enum command_control_type, char *));
 
-static void 
-set_verbose PARAMS ((char *, int, struct cmd_list_element *));
+static struct command_line *
+get_command_line PARAMS ((enum command_control_type, char *));
 
-static void
-show_history PARAMS ((char *, int));
+static void realloc_body_list PARAMS ((struct command_line *, int));
 
-static void
-set_history PARAMS ((char *, int));
+static enum misc_command_type read_next_line PARAMS ((struct command_line **));
 
-static void
-set_history_size_command PARAMS ((char *, int, struct cmd_list_element *));
+static enum command_control_type
+recurse_read_control_structure PARAMS ((struct command_line *));
 
-static void
-show_commands PARAMS ((char *, int));
+static void init_main PARAMS ((void));
 
-static void
-echo_command PARAMS ((char *, int));
+static void init_cmd_lists PARAMS ((void));
 
-static void
-pwd_command PARAMS ((char *, int));
+static void float_handler PARAMS ((int));
 
-static void
-show_version PARAMS ((char *, int));
+static void init_signals PARAMS ((void));
 
-static void
-document_command PARAMS ((char *, int));
+static void set_verbose PARAMS ((char *, int, struct cmd_list_element *));
 
-static void
-define_command PARAMS ((char *, int));
+static void show_history PARAMS ((char *, int));
 
-static void
-validate_comname PARAMS ((char *));
+static void set_history PARAMS ((char *, int));
 
-static void
-help_command PARAMS ((char *, int));
+static void set_history_size_command PARAMS ((char *, int,
+                                             struct cmd_list_element *));
 
-static void
-show_command PARAMS ((char *, int));
+static void show_commands PARAMS ((char *, int));
 
-static void
-info_command PARAMS ((char *, int));
+static void echo_command PARAMS ((char *, int));
 
-static void
-complete_command PARAMS ((char *, int));
+static void pwd_command PARAMS ((char *, int));
 
-static void
-do_nothing PARAMS ((int));
+static void show_version PARAMS ((char *, int));
 
-static int
-quit_cover PARAMS ((char *));
+static void document_command PARAMS ((char *, int));
 
-static void
-disconnect PARAMS ((int));
+static void define_command PARAMS ((char *, int));
 
-static void
-source_cleanup PARAMS ((FILE *));
+static void validate_comname PARAMS ((char *));
+
+static void help_command PARAMS ((char *, int));
+
+static void show_command PARAMS ((char *, int));
+
+static void info_command PARAMS ((char *, int));
+
+static void complete_command PARAMS ((char *, int));
+
+static void do_nothing PARAMS ((int));
+
+static int quit_cover PARAMS ((char *));
+
+static void disconnect PARAMS ((int));
+
+static void source_cleanup PARAMS ((FILE *));
 
 /* If this definition isn't overridden by the header files, assume
    that isatty and fileno exist on this system.  */
@@ -296,6 +293,9 @@ int baud_rate = -1;
 
 int remote_debug = 0;
 
+/* Level of control structure.  */
+static int control_level;
+
 /* Signal to catch ^Z typed while reading a command: SIGTSTP or SIGCONT.  */
 
 #ifndef STOP_SIGNAL
@@ -546,6 +546,221 @@ gdb_init ()
     init_ui_hook ();
 }
 
+/* Allocate, initialize a new command line structure for one of the
+   control commands (if/while).  */
+
+static struct command_line *
+build_command_line (type, args)
+     enum command_control_type type;
+     char *args;
+{
+  struct command_line *cmd;
+
+  cmd = (struct command_line *)xmalloc (sizeof (struct command_line));
+  cmd->next = NULL;
+  cmd->control_type = type;
+
+  cmd->body_count = 1;
+  cmd->body_list
+    = (struct command_line **)xmalloc (sizeof (struct command_line *)
+                                      * cmd->body_count);
+  memset (cmd->body_list, 0, sizeof (struct command_line *) * cmd->body_count);
+  cmd->line = savestring (args, strlen (args));
+  return cmd;
+}
+
+/* Build and return a new command structure for the control commands
+   such as "if" and "while".  */
+
+static struct command_line *
+get_command_line (type, arg)
+     enum command_control_type type;
+     char *arg;
+{
+  struct command_line *cmd;
+  struct cleanup *old_chain = NULL;
+
+  /* Allocate and build a new command line structure.  */
+  cmd = build_command_line (type, arg);
+
+  old_chain = make_cleanup (free_command_lines, &cmd);
+
+  /* Read in the body of this command.  */
+  if (recurse_read_control_structure (cmd) == invalid_control)
+    {
+      warning ("error reading in control structure\n");
+      do_cleanups (old_chain);
+      return NULL;
+    }
+
+  discard_cleanups (old_chain);
+  return cmd;
+}
+
+/* Execute the command in CMD.  */
+
+static enum command_control_type
+execute_control_command (cmd)
+     struct command_line *cmd;
+{
+  struct expression *expr;
+  struct command_line *current;
+  struct cleanup *old_chain = 0;
+  struct cleanup *tmp_chain;
+  value_ptr val;
+  int loop;
+  enum command_control_type ret;
+
+  switch (cmd->control_type)
+    {
+    case simple_control:
+      /* A simple command, execute it and return.  */
+      execute_command (cmd->line, 0);
+      return cmd->control_type;
+
+    case continue_control:
+    case break_control:
+      /* Return for "continue", and "break" so we can either
+        continue the loop at the top, or break out.  */
+      return cmd->control_type;
+
+    case while_control:
+      {
+       /* Parse the loop control expression for the while statement.  */
+       expr = parse_expression (cmd->line);
+       tmp_chain = make_cleanup (free_current_contents, &expr);
+       if (!old_chain)
+         old_chain = tmp_chain;
+
+       ret = simple_control;
+       loop = true;
+
+       /* Keep iterating so long as the expression is true.  */
+       while (loop == true)
+         {
+           /* Evaluate the expression.  */
+           val = evaluate_expression (expr);
+
+           /* If the value is false, then break out of the loop.  */
+           if (!value_true (val))
+             break;
+
+           /* Execute the body of the while statement.  */
+           current = *cmd->body_list;
+           while (current)
+             {
+               ret = execute_control_command (current);
+
+               /* If we got an error, or a "break" command, then stop
+                  looping.  */
+               if (ret == invalid_control || ret == break_control)
+                 {
+                   loop = false;
+                   break;
+                 }
+
+               /* If we got a "continue" command, then restart the loop
+                  at this point.  */
+               if (ret == continue_control)
+                 break;
+               
+               /* Get the next statement.  */
+               current = current->next; 
+             }
+         }
+
+       /* Reset RET so that we don't recurse the break all the way down.  */
+       if (ret == break_control)
+         ret = simple_control;
+
+       break;
+      }
+
+    case if_control:
+      {
+       /* Parse the conditional for the if statement.  */
+       expr = parse_expression (cmd->line);
+       old_chain = make_cleanup (free_current_contents, &expr);
+
+       current = NULL;
+       ret = simple_control;
+
+       /* Evaluate the conditional.  */
+       val = evaluate_expression (expr);
+
+       /* Choose which arm to take commands from based on the value of the
+          conditional expression.  */
+       if (value_true (val))
+         current = *cmd->body_list;
+       else if (cmd->body_count == 2)
+         current = *(cmd->body_list + 1);
+
+       /* Execute commands in the given arm.  */
+       while (current)
+         {
+           ret = execute_control_command (current);
+
+           /* If we got an error, get out.  */
+           if (ret != simple_control)
+             break;
+
+           /* Get the next statement in the body.  */
+           current = current->next;
+         }
+       break;
+      }
+
+    default:
+      warning ("Invalid control type in command structure.");
+      return invalid_control;
+    }
+
+  if (old_chain)
+    do_cleanups (old_chain);
+
+  return ret;
+}
+
+/* "while" command support.  Executes a body of statements while the
+   loop condition is nonzero.  */
+
+static void
+while_command (arg, from_tty)
+     char *arg;
+     int from_tty;
+{
+  struct command_line *command = NULL;
+
+  control_level = 1;
+  command = get_command_line (while_control, arg);
+
+  if (command == NULL)
+    return;
+
+  execute_control_command (command);
+  free_command_lines (&command);
+}
+
+/* "if" command support.  Execute either the true or false arm depending
+   on the value of the if conditional.  */
+
+static void
+if_command (arg, from_tty)
+     char *arg;
+     int from_tty;
+{
+  struct command_line *command = NULL;
+
+  control_level = 1;
+  command = get_command_line (if_control, arg);
+
+  if (command == NULL)
+    return;
+
+  execute_control_command (command);
+  free_command_lines (&command);
+}
+
 void
 execute_user_command (c, args)
      struct cmd_list_element *c;
@@ -553,7 +768,8 @@ execute_user_command (c, args)
 {
   register struct command_line *cmdlines;
   struct cleanup *old_chain;
-  
+  enum command_control_type ret;
+
   if (args)
     error ("User-defined commands cannot take arguments.");
 
@@ -568,7 +784,12 @@ execute_user_command (c, args)
   instream = (FILE *) 0;
   while (cmdlines)
     {
-      execute_command (cmdlines->line, 0);
+      ret = execute_control_command (cmdlines);
+      if (ret != simple_control && ret != break_control)
+       {
+         warning ("Error in control structure.\n");
+         break;
+       }
       cmdlines = cmdlines->next;
     }
   do_cleanups (old_chain);
@@ -591,12 +812,12 @@ execute_command (p, from_tty)
   /* This can happen when command_line_input hits end of file.  */
   if (p == NULL)
       return;
-  
+
   while (*p == ' ' || *p == '\t') p++;
   if (*p)
     {
       char *arg;
-      
+
       c = lookup_cmd (&p, cmdlist, "", 0, 1);
       /* Pass null arg rather than an empty one.  */
       arg = *p ? p : 0;
@@ -696,7 +917,7 @@ dont_repeat ()
 /* Read a line from the stream "instream" without command line editing.
 
    It prints PRROMPT once at the start.
-   Action is compatible with "readline", e.g. space for the result is 
+   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.  */
@@ -724,7 +945,7 @@ gdb_readline (prrompt)
 /* end-sanitize-mpw */
       gdb_flush (gdb_stdout);
     }
-  
+
   result = (char *) xmalloc (result_size);
 
   while (1)
@@ -1484,6 +1705,256 @@ command_line_input (prrompt, repeat, annotation_suffix)
   return linebuffer;
 }
 \f
+
+/* Expand the body_list of COMMAND so that it can hold NEW_LENGTH
+   code bodies.  This is typically used when we encounter an "else"
+   clause for an "if" command.  */
+
+static void
+realloc_body_list (command, new_length)
+     struct command_line *command;
+     int new_length;
+{
+  int n;
+  struct command_line **body_list;
+
+  n = command->body_count;
+
+  /* Nothing to do?  */
+  if (new_length <= n)
+    return;
+
+  body_list = (struct command_line **)
+    xmalloc (sizeof (struct command_line *) * new_length);
+
+  memcpy (body_list, command->body_list, sizeof (struct command_line *) * n);
+
+  free (command->body_list);
+  command->body_list = body_list;
+  command->body_count = new_length;
+}
+
+/* Read one line from the input stream.  If the command is an "else" or
+   "end", return such an indication to the caller.  */
+
+static enum misc_command_type
+read_next_line (command)
+     struct command_line **command;
+{
+  char *p, *p1, *prompt_ptr, control_prompt[256];
+  int i = 0;
+
+  if (control_level >= 254)
+    error ("Control nesting too deep!\n");
+
+  /* Set a prompt based on the nesting of the control commands.  */
+  if (instream == stdin)
+    {
+      for (i = 0; i < control_level; i++)
+       control_prompt[i] = ' ';
+      control_prompt[i] = '>';
+      control_prompt[i+1] = '\0';
+      prompt_ptr = (char *)&control_prompt[0];
+    }
+  else
+    prompt_ptr = NULL;
+
+  p = command_line_input (prompt_ptr, instream == stdin, NULL);
+
+  /* Not sure what to do here.  */
+  if (p == NULL)
+    return end_command;
+
+  /* Strip leading and trailing whitespace.  */
+  while (*p == ' ' || *p == '\t')
+    p++;
+
+  p1 = p + strlen (p);
+  while (p1 != p && (p1[-1] == ' ' || p1[-1] == '\t'))
+    p1--;
+
+  /* Blanks and comments don't really do anything, but we need to
+     distinguish them from else, end and other commands which can be
+     executed.  */
+  if (p1 == p || p[0] == '#')
+    return nop_command;
+      
+  /* Is this the end of a simple, while, or if control structure?  */
+  if (p1 - p == 3 && !strncmp (p, "end", 3))
+    return end_command;
+
+  /* Is the else clause of an if control structure?  */
+  if (p1 - p == 4 && !strncmp (p, "else", 4))
+    return else_command;
+
+  /* Check for while, if, break, continue, etc and build a new command
+     line structure for them.  */
+  if (p1 - p > 5 && !strncmp (p, "while", 5))
+    *command = build_command_line (while_control, p + 6);
+  else if (p1 - p > 2 && !strncmp (p, "if", 2))
+    *command = build_command_line (if_control, p + 3);
+  else if (p1 - p == 5 && !strncmp (p, "loop_break", 5))
+    {
+      *command = (struct command_line *)
+       xmalloc (sizeof (struct command_line));
+      (*command)->next = NULL;
+      (*command)->line = NULL;
+      (*command)->control_type = break_control;
+      (*command)->body_count = 0;
+      (*command)->body_list = NULL;
+    }
+  else if (p1 - p == 8 && !strncmp (p, "loop_continue", 8))
+    {
+      *command = (struct command_line *)
+       xmalloc (sizeof (struct command_line));
+      (*command)->next = NULL;
+      (*command)->line = NULL;
+      (*command)->control_type = continue_control;
+      (*command)->body_count = 0;
+      (*command)->body_list = NULL;
+    }
+  else
+    {
+      /* A normal command.  */
+      *command = (struct command_line *)
+       xmalloc (sizeof (struct command_line));
+      (*command)->next = NULL;
+      (*command)->line = savestring (p, p1 - p);
+      (*command)->control_type = simple_control;
+      (*command)->body_count = 0;
+      (*command)->body_list = NULL;
+  }
+
+  /* Nothing special.  */
+  return ok_command;
+}
+
+/* Recursively read in the control structures and create a command_line 
+   tructure from them.
+
+   The parent_control parameter is the control structure in which the
+   following commands are nested.  */
+
+static enum command_control_type
+recurse_read_control_structure (current_cmd)
+     struct command_line *current_cmd;
+{
+  int current_body, i;
+  enum misc_command_type val;
+  enum command_control_type ret;
+  struct command_line **body_ptr, *child_tail, *next;
+  struct cleanup *old_chains, *tmp_chains;
+
+  old_chains = NULL;
+  child_tail = NULL;
+  current_body = 1;
+
+  /* Sanity checks.  */
+  if (current_cmd->control_type == simple_control)
+    {
+      error ("Recursed on a simple control type\n");
+      return invalid_control;
+    }
+
+  if (current_body > current_cmd->body_count)
+    {
+      error ("Allocated body is smaller than this command type needs\n");
+      return invalid_control;
+    }
+
+  /* Read lines from the input stream and build control structures.  */
+  while (1)
+    {
+      dont_repeat ();
+
+      next = NULL;
+      val = read_next_line (&next);
+
+      /* Just skip blanks and comments.  */
+      if (val == nop_command)
+       continue;
+
+      if (val == end_command)
+       {
+         if (current_cmd->control_type == while_control
+             || current_cmd->control_type == if_control)
+           {
+             /* Success reading an entire control structure.  */
+             ret = simple_control;
+             break;
+           }
+         else
+           {
+             ret = invalid_control;
+             break;
+           }
+       }
+      
+      /* Not the end of a control structure.  */
+      if (val == else_command)
+       {
+         if (current_cmd->control_type == if_control
+             && current_body == 1)
+           {
+             realloc_body_list (current_cmd, 2);
+             current_body = 2;
+             child_tail = NULL;
+             continue;
+           }
+         else
+           {
+             ret = invalid_control;
+             break;
+           }
+       }
+
+      if (child_tail)
+       {
+         child_tail->next = next;
+       }
+      else
+       {
+         /* We have just read the first line of the child's control
+            structure.  From now on, arrange to throw away the line
+            we have if we quit or get an error.  */
+         body_ptr = current_cmd->body_list;
+         for (i = 1; i < current_body; i++)
+           body_ptr++;
+
+         *body_ptr = next;
+
+         tmp_chains = make_cleanup (free_command_lines, body_ptr);
+
+         if (!old_chains)
+           old_chains = tmp_chains;
+       }
+
+      child_tail = next;
+
+      /* If the latest line is another control structure, then recurse
+        on it.  */
+      if (next->control_type == while_control
+         || next->control_type == if_control)
+       {
+         control_level++;
+         ret = recurse_read_control_structure (next);
+         control_level--;
+
+         if (ret != simple_control)
+           break;
+       }
+    }
+
+  dont_repeat ();
+  if (ret == invalid_control && old_chains)
+    do_cleanups (old_chains);
+  else if (old_chains)
+    discard_cleanups (old_chains);
+
+  return ret;
+}
+
+
 /* Read lines from the input stream
    and accumulate them in a chain of struct command_line's
    which is then returned.  */
@@ -1491,54 +1962,71 @@ command_line_input (prrompt, repeat, annotation_suffix)
 struct command_line *
 read_command_lines ()
 {
-  struct command_line *first = 0;
-  register struct command_line *next, *tail = 0;
-  register char *p, *p1;
-  struct cleanup *old_chain = 0;
+  struct command_line *head, *tail, *next;
+  struct cleanup *old_chain;
+  enum command_control_type ret;
+  enum misc_command_type val;
+
+  head = tail = NULL;
+  old_chain = NULL;
 
   while (1)
     {
-      dont_repeat ();
-      p = command_line_input ((char *) NULL, instream == stdin, "commands");
-      if (p == NULL)
-       /* Treat end of file like "end".  */
-       break;
-      
-      /* Remove leading and trailing blanks.  */
-      while (*p == ' ' || *p == '\t') p++;
-      p1 = p + strlen (p);
-      while (p1 != p && (p1[-1] == ' ' || p1[-1] == '\t')) p1--;
+      val = read_next_line (&next);
 
-      /* Is this "end"?  */
-      if (p1 - p == 3 && !strncmp (p, "end", 3))
-       break;
+      /* Ignore blank lines or comments.  */
+      if (val == nop_command)
+       continue;
+
+      if (val == end_command)
+       {
+         ret = simple_control;
+         break;
+       }
+
+      if (val != ok_command)
+       {
+         ret = invalid_control;
+         break;
+       }
+
+      if (next->control_type == while_control
+         || next->control_type == if_control)
+       {
+         control_level++;
+         ret = recurse_read_control_structure (next);
+         control_level--;
 
-      /* No => add this line to the chain of command lines.  */
-      next = (struct command_line *) xmalloc (sizeof (struct command_line));
-      next->line = savestring (p, p1 - p);
-      next->next = 0;
+         if (ret == invalid_control)
+           break;
+       }
+      
       if (tail)
        {
          tail->next = next;
        }
       else
        {
-         /* We just read the first line.
-            From now on, arrange to throw away the lines we have
-            if we quit or get an error while inside this function.  */
-         first = next;
-         old_chain = make_cleanup (free_command_lines, &first);
+         head = next;
+         old_chain = make_cleanup (free_command_lines, &head);
        }
       tail = next;
     }
 
   dont_repeat ();
 
-  /* Now we are about to return the chain to our caller,
-     so freeing it becomes his responsibility.  */
-  if (first)
-    discard_cleanups (old_chain);
-  return first;
+  if (head)
+    {
+      if (ret != invalid_control)
+       {
+         discard_cleanups (old_chain);
+         return head;
+       }
+      else
+       do_cleanups (old_chain);
+    }
+
+  return NULL;
 }
 
 /* Free a chain of struct command_line's.  */
@@ -1549,9 +2037,17 @@ free_command_lines (lptr)
 {
   register struct command_line *l = *lptr;
   register struct command_line *next;
+  struct command_line **blist;
+  int i;
 
   while (l)
     {
+      if (l->body_count > 0)
+       {
+         blist = l->body_list;
+         for (i = 0; i < l->body_count; i++, blist++)
+           free_command_lines (blist);
+       }
       next = l->next;
       free (l->line);
       free ((PTR)l);
@@ -1718,7 +2214,7 @@ define_command (comname, from_tty)
   c = lookup_cmd (&tem, cmdlist, "", -1, 1);
   if (c && !STREQ (comname, c->name))
     c = 0;
-    
+
   if (c)
     {
       if (c->class == class_user || c->class == class_alias)
@@ -1751,7 +2247,7 @@ define_command (comname, from_tty)
 
   comname = savestring (comname, strlen (comname));
 
-  /* If the rest of the commands will be case insensitive, this one 
+  /* If the rest of the commands will be case insensitive, this one
      should behave in the same manner. */
   for (tem = comname; *tem; tem++)
     if (isupper(*tem)) *tem = tolower(*tem);
@@ -1763,6 +2259,7 @@ End with a line saying just \"end\".\n", comname);
       gdb_flush (gdb_stdout);
     }
 
+  control_level = 0;
   cmds = read_command_lines ();
 
   if (c && c->class == class_user)
@@ -2193,7 +2690,7 @@ show_commands (args, from_tty)
   /* The next command we want to display is the next one that we haven't
      displayed yet.  */
   num += Hist_print;
-  
+
   /* If the user repeats this command with return, it should do what
      "show commands +" does.  This is unnecessary if arg is null,
      because "show commands +" is not useful after "show commands".  */
@@ -2246,7 +2743,7 @@ int info_verbose = 0;             /* Default verbose msgs off */
 
 /* Called by do_setshow_command.  An elaborate joke.  */
 /* ARGSUSED */
-static void 
+static void
 set_verbose (args, from_tty, c)
      char *args;
      int from_tty;
@@ -2254,7 +2751,7 @@ set_verbose (args, from_tty, c)
 {
   char *cmdname = "verbose";
   struct cmd_list_element *showcmd;
-  
+
   showcmd = lookup_cmd_1 (&cmdname, showlist, NULL, 1);
 
   if (info_verbose)
@@ -2341,7 +2838,7 @@ static void
 init_main ()
 {
   struct cmd_list_element *c;
-  
+
 #ifdef DEFAULT_PROMPT
   prompt = savestring (DEFAULT_PROMPT, strlen(DEFAULT_PROMPT));
 #else
@@ -2352,7 +2849,7 @@ init_main ()
   command_editing_p = 1;
   history_expansion_p = 0;
   write_history_p = 0;
-  
+
   /* Setup important stuff for command line editing.  */
   rl_completion_entry_function = (int (*)()) symbol_completion_function;
   rl_completer_word_break_characters = gdb_completer_word_break_characters;
@@ -2401,7 +2898,7 @@ until the next time it is started.", &cmdlist);
           "Set gdb's prompt",
           &setlist),
      &showlist);
-  
+
   add_com ("echo", class_support, echo_command,
           "Print a constant string.  Give string as argument.\n\
 C escape sequences may be used in the argument.\n\
@@ -2447,7 +2944,7 @@ when gdb is started.", &cmdlist);
   add_show_from_set (c, &showlist);
   c->function.sfunc = set_verbose;
   set_verbose (NULL, 0, c);
-  
+
   add_show_from_set
     (add_set_cmd ("editing", class_support, var_boolean, (char *)&command_editing_p,
           "Set editing of command lines as they are typed.\n\
@@ -2518,6 +3015,19 @@ the previous command number shown.",
   add_cmd ("version", no_class, show_version,
           "Show what version of GDB this is.", &showlist);
 
+  add_com ("while", class_support, while_command,
+"Execute nested commands WHILE the conditional expression is non zero.\n\
+The conditional expression must follow the word `while' and must in turn be\
+followed by a new line.  The nested commands must be entered one per line,\
+and should be terminated by the word `end'.");
+
+  add_com ("if", class_support, if_command,
+"Execute nested commands once IF the conditional expression is non zero.\n\
+The conditional expression must follow the word `if' and must in turn be\
+followed by a new line.  The nested commands must be entered one per line,\
+and should be terminated by the word 'else' or `end'.  If an else clause\
+is used, the same rules apply to its nested commands as to the first ones.");
+
   /* If target is open when baud changes, it doesn't take effect until the
      next open (I think, not sure).  */
   add_show_from_set (add_set_cmd ("remotebaud", no_class,