386BSD 0.1 development
authorWilliam F. Jolitz <wjolitz@soda.berkeley.edu>
Wed, 8 May 1991 18:26:40 +0000 (10:26 -0800)
committerWilliam F. Jolitz <wjolitz@soda.berkeley.edu>
Wed, 8 May 1991 18:26:40 +0000 (10:26 -0800)
Work on file usr/src/usr.bin/gdb/core.c
Work on file usr/src/usr.bin/gdb/breakpoint.c
Work on file usr/src/usr.bin/gdb/dbxread.c
Work on file usr/src/usr.bin/gdb/environ.c
Work on file usr/src/usr.bin/gdb/eval.c
Work on file usr/src/usr.bin/gdb/infcmd.c
Work on file usr/src/usr.bin/gdb/inflow.c
Work on file usr/src/usr.bin/gdb/infrun.c
Work on file usr/src/usr.bin/gdb/printcmd.c
Work on file usr/src/usr.bin/gdb/remote.c
Work on file usr/src/usr.bin/gdb/source.c
Work on file usr/src/usr.bin/gdb/stack.c
Work on file usr/src/usr.bin/gdb/valarith.c
Work on file usr/src/usr.bin/gdb/utils.c
Work on file usr/src/usr.bin/gdb/symtab.c
Work on file usr/src/usr.bin/gdb/valprint.c
Work on file usr/src/usr.bin/gdb/valops.c
Work on file usr/src/usr.bin/gdb/values.c

Co-Authored-By: Lynne Greer Jolitz <ljolitz@cardio.ucsf.edu>
Synthesized-from: 386BSD-0.1

18 files changed:
usr/src/usr.bin/gdb/breakpoint.c [new file with mode: 0644]
usr/src/usr.bin/gdb/core.c [new file with mode: 0644]
usr/src/usr.bin/gdb/dbxread.c [new file with mode: 0644]
usr/src/usr.bin/gdb/environ.c [new file with mode: 0644]
usr/src/usr.bin/gdb/eval.c [new file with mode: 0644]
usr/src/usr.bin/gdb/infcmd.c [new file with mode: 0644]
usr/src/usr.bin/gdb/inflow.c [new file with mode: 0644]
usr/src/usr.bin/gdb/infrun.c [new file with mode: 0644]
usr/src/usr.bin/gdb/printcmd.c [new file with mode: 0644]
usr/src/usr.bin/gdb/remote.c [new file with mode: 0644]
usr/src/usr.bin/gdb/source.c [new file with mode: 0644]
usr/src/usr.bin/gdb/stack.c [new file with mode: 0644]
usr/src/usr.bin/gdb/symtab.c [new file with mode: 0644]
usr/src/usr.bin/gdb/utils.c [new file with mode: 0644]
usr/src/usr.bin/gdb/valarith.c [new file with mode: 0644]
usr/src/usr.bin/gdb/valops.c [new file with mode: 0644]
usr/src/usr.bin/gdb/valprint.c [new file with mode: 0644]
usr/src/usr.bin/gdb/values.c [new file with mode: 0644]

diff --git a/usr/src/usr.bin/gdb/breakpoint.c b/usr/src/usr.bin/gdb/breakpoint.c
new file mode 100644 (file)
index 0000000..b515ed3
--- /dev/null
@@ -0,0 +1,1383 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)breakpoint.c       6.3 (Berkeley) 5/8/91";
+#endif /* not lint */
+
+/* Everything about breakpoints, for GDB.
+   Copyright (C) 1986, 1987, 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING.  If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#include <stdio.h>
+#include "defs.h"
+#include "param.h"
+#include "symtab.h"
+#include "frame.h"
+
+/* This is the sequence of bytes we insert for a breakpoint.  */
+
+static char break_insn[] = BREAKPOINT;
+
+/* States of enablement of breakpoint.
+   `temporary' means disable when hit.
+   `delete' means delete when hit.  */
+
+enum enable { disabled, enabled, temporary, delete};
+
+/* Not that the ->silent field is not currently used by any commands
+   (though the code is in there if it was to be and set_raw_breakpoint
+   does set it to 0).  I implemented it because I thought it would be
+   useful for a hack I had to put in; I'm going to leave it in because
+   I can see how there might be times when it would indeed be useful */
+
+struct breakpoint
+{
+  struct breakpoint *next;
+  /* Number assigned to distinguish breakpoints.  */
+  int number;
+  /* Address to break at.  */
+  CORE_ADDR address;
+  /* Line number of this address.  Redundant.  */
+  int line_number;
+  /* Symtab of file of this address.  Redundant.  */
+  struct symtab *symtab;
+  /* Zero means disabled; remember the info but don't break here.  */
+  enum enable enable;
+  /* Non-zero means a silent breakpoint (don't print frame info
+     if we stop here). */
+  unsigned char silent;
+  /* Number of stops at this breakpoint that should
+     be continued automatically before really stopping.  */
+  int ignore_count;
+  /* "Real" contents of byte where breakpoint has been inserted.
+     Valid only when breakpoints are in the program.  */
+  char shadow_contents[sizeof break_insn];
+  /* Nonzero if this breakpoint is now inserted.  */
+  char inserted;
+  /* Nonzero if this is not the first breakpoint in the list
+     for the given address.  */
+  char duplicate;
+  /* Chain of command lines to execute when this breakpoint is hit.  */
+  struct command_line *commands;
+  /* Stack depth (address of frame).  If nonzero, break only if fp
+     equals this.  */
+  FRAME_ADDR frame;
+  /* Conditional.  Break only if this expression's value is nonzero.  */
+  struct expression *cond;
+};
+
+#define ALL_BREAKPOINTS(b)  for (b = breakpoint_chain; b; b = b->next)
+
+/* Chain of all breakpoints defined.  */
+
+struct breakpoint *breakpoint_chain;
+
+/* Number of last breakpoint made.  */
+
+static int breakpoint_count;
+
+/* Default address, symtab and line to put a breakpoint at
+   for "break" command with no arg.
+   if default_breakpoint_valid is zero, the other three are
+   not valid, and "break" with no arg is an error.
+
+   This set by print_stack_frame, which calls set_default_breakpoint.  */
+
+int default_breakpoint_valid;
+CORE_ADDR default_breakpoint_address;
+struct symtab *default_breakpoint_symtab;
+int default_breakpoint_line;
+
+/* Remaining commands (not yet executed)
+   of last breakpoint hit.  */
+
+struct command_line *breakpoint_commands;
+
+static void delete_breakpoint ();
+void clear_momentary_breakpoints ();
+void breakpoint_auto_delete ();
+
+/* Flag indicating extra verbosity for xgdb.  */
+extern int xgdb_verbose;
+\f
+/* condition N EXP -- set break condition of breakpoint N to EXP.  */
+
+static void
+condition_command (arg, from_tty)
+     char *arg;
+     int from_tty;
+{
+  register struct breakpoint *b;
+  register char *p;
+  register int bnum;
+  register struct expression *expr;
+
+  if (arg == 0)
+    error_no_arg ("breakpoint number");
+
+  p = arg;
+  while (*p >= '0' && *p <= '9') p++;
+  if (p == arg)
+    /* There is no number here.  (e.g. "cond a == b").  */
+    error_no_arg ("breakpoint number");
+  bnum = atoi (arg);
+
+  ALL_BREAKPOINTS (b)
+    if (b->number == bnum)
+      {
+       if (b->cond)
+         {
+           free (b->cond);
+           b->cond = 0;  /* parse_c_1 can leave this unchanged. */
+         }
+       if (*p == 0)
+         {
+           b->cond = 0;
+           if (from_tty)
+             printf ("Breakpoint %d now unconditional.\n", bnum);
+         }
+       else
+         {
+           if (*p != ' ' && *p != '\t')
+             error ("Arguments must be an integer (breakpoint number) and an expression.");
+
+           /* Find start of expression */
+           while (*p == ' ' || *p == '\t') p++;
+
+           arg = p;
+           b->cond = (struct expression *) parse_c_1 (&arg, block_for_pc (b->address), 0);
+           if (*arg)
+             error ("Junk at end of expression");
+         }
+       return;
+      }
+
+  error ("No breakpoint number %d.", bnum);
+}
+
+static void
+commands_command (arg, from_tty)
+     char *arg;
+     int from_tty;
+{
+  register struct breakpoint *b;
+  register char *p, *p1;
+  register int bnum;
+  struct command_line *l;
+
+  /* If we allowed this, we would have problems with when to
+     free the storage, if we change the commands currently
+     being read from.  */
+
+  if (breakpoint_commands)
+    error ("Can't use the \"commands\" command among a breakpoint's commands.");
+
+  /* Allow commands by itself to refer to the last breakpoint.  */
+  if (arg == 0)
+    bnum = breakpoint_count;
+  else
+    {
+      p = arg;
+      if (! (*p >= '0' && *p <= '9'))
+       error ("Argument must be integer (a breakpoint number).");
+      
+      while (*p >= '0' && *p <= '9') p++;
+      if (*p)
+       error ("Unexpected extra arguments following breakpoint number.");
+      
+      bnum = atoi (arg);
+    }
+
+  ALL_BREAKPOINTS (b)
+    if (b->number == bnum)
+      {
+       if (from_tty && input_from_terminal_p ())
+         {
+           printf ("Type commands for when breakpoint %d is hit, one per line.\n\
+End with a line saying just \"end\".\n", bnum);
+           fflush (stdout);
+         }
+       l = read_command_lines (from_tty);
+       free_command_lines (b->commands);
+       b->commands = l;
+       return;
+      }
+  error ("No breakpoint number %d.", bnum);
+}
+
+/* Called from command loop to execute the commands
+   associated with the breakpoint we just stopped at.  */
+
+void
+do_breakpoint_commands ()
+{
+  struct command_line *cmd;
+
+  while (cmd = breakpoint_commands)
+    {
+      breakpoint_commands = 0;
+      execute_command_lines(cmd);
+      /* If command was "cont", breakpoint_commands is now 0,
+        of if we stopped at yet another breakpoint which has commands,
+        it is now the commands for the new breakpoint.  */
+    }
+  clear_momentary_breakpoints ();
+}
+
+/* Used when the program is proceeded, to eliminate any remaining
+   commands attached to the previous breakpoint we stopped at.  */
+
+void
+clear_breakpoint_commands ()
+{
+  breakpoint_commands = 0;
+  breakpoint_auto_delete (0);
+}
+
+/* Functions to get and set the current list of pending
+   breakpoint commands.  These are used by run_stack_dummy
+   to preserve the commands around a function call.  */
+
+struct command_line *
+get_breakpoint_commands ()
+{
+  return breakpoint_commands;
+}
+
+void
+set_breakpoint_commands (cmds)
+     struct command_line *cmds;
+{
+  breakpoint_commands = cmds;
+}
+\f
+/* insert_breakpoints is used when starting or continuing the program.
+   remove_breakpoints is used when the program stops.
+   Both return zero if successful,
+   or an `errno' value if could not write the inferior.  */
+
+int
+insert_breakpoints ()
+{
+  register struct breakpoint *b;
+  int val;
+
+#ifdef BREAKPOINT_DEBUG
+  printf ("Inserting breakpoints.\n");
+#endif /* BREAKPOINT_DEBUG */
+
+  ALL_BREAKPOINTS (b)
+    if (b->enable != disabled && ! b->inserted && ! b->duplicate)
+      {
+       read_memory (b->address, b->shadow_contents, sizeof break_insn);
+       val = write_memory (b->address, break_insn, sizeof break_insn);
+       if (val)
+         return val;
+#ifdef BREAKPOINT_DEBUG
+       printf ("Inserted breakpoint at 0x%x, shadow 0x%x, 0x%x.\n",
+               b->address, b->shadow_contents[0], b->shadow_contents[1]);
+#endif /* BREAKPOINT_DEBUG */
+       b->inserted = 1;
+      }
+  return 0;
+}
+
+int
+remove_breakpoints ()
+{
+  register struct breakpoint *b;
+  int val;
+
+#ifdef BREAKPOINT_DEBUG
+  printf ("Removing breakpoints.\n");
+#endif /* BREAKPOINT_DEBUG */
+
+  ALL_BREAKPOINTS (b)
+    if (b->inserted)
+      {
+       val = write_memory (b->address, b->shadow_contents, sizeof break_insn);
+       if (val)
+         return val;
+       b->inserted = 0;
+#ifdef BREAKPOINT_DEBUG
+       printf ("Removed breakpoint at 0x%x, shadow 0x%x, 0x%x.\n",
+               b->address, b->shadow_contents[0], b->shadow_contents[1]);
+#endif /* BREAKPOINT_DEBUG */
+      }
+
+  return 0;
+}
+
+/* Clear the "inserted" flag in all breakpoints.
+   This is done when the inferior is loaded.  */
+
+void
+mark_breakpoints_out ()
+{
+  register struct breakpoint *b;
+
+  ALL_BREAKPOINTS (b)
+    b->inserted = 0;
+}
+
+/* breakpoint_here_p (PC) returns 1 if an enabled breakpoint exists at PC.
+   When continuing from a location with a breakpoint,
+   we actually single step once before calling insert_breakpoints.  */
+
+int
+breakpoint_here_p (pc)
+     CORE_ADDR pc;
+{
+  register struct breakpoint *b;
+
+  ALL_BREAKPOINTS (b)
+    if (b->enable != disabled && b->address == pc)
+      return 1;
+
+  return 0;
+}
+
+/* Evaluate the expression EXP and return 1 if value is zero.
+   This is used inside a catch_errors to evaluate the breakpoint condition.  */
+
+int
+breakpoint_cond_eval (exp)
+     struct expression *exp;
+{
+  return value_zerop (evaluate_expression (exp));
+}
+
+/* Return 0 if PC is not the address just after a breakpoint,
+   or -1 if breakpoint says do not stop now,
+   or -2 if breakpoint says it has deleted itself and don't stop,
+   or -3 if hit a breakpoint number -3 (delete when program stops),
+   or else the number of the breakpoint,
+   with 0x1000000 added (or subtracted, for a negative return value) for
+   a silent breakpoint.  */
+
+int
+breakpoint_stop_status (pc, frame_address)
+     CORE_ADDR pc;
+     FRAME_ADDR frame_address;
+{
+  register struct breakpoint *b;
+  register int cont = 0;
+
+  /* Get the address where the breakpoint would have been.  */
+  pc -= DECR_PC_AFTER_BREAK;
+
+  ALL_BREAKPOINTS (b)
+    if (b->enable != disabled && b->address == pc)
+      {
+       if (b->frame && b->frame != frame_address)
+         cont = -1;
+       else
+         {
+           int value_zero;
+           if (b->cond)
+             {
+               /* Need to select the frame, with all that implies
+                  so that the conditions will have the right context.  */
+               select_frame (get_current_frame (), 0);
+               value_zero
+                 = catch_errors (breakpoint_cond_eval, b->cond,
+                                 "Error occurred in testing breakpoint condition.");
+               free_all_values ();
+             }
+           if (b->cond && value_zero)
+             {
+               cont = -1;
+             }
+           else if (b->ignore_count > 0)
+             {
+               b->ignore_count--;
+               cont = -1;
+             }
+           else
+             {
+               if (b->enable == temporary)
+                 b->enable = disabled;
+               breakpoint_commands = b->commands;
+               if (b->silent
+                   || (breakpoint_commands
+                       && !strcmp ("silent", breakpoint_commands->line)))
+                 {
+                   if (breakpoint_commands)
+                     breakpoint_commands = breakpoint_commands->next;
+                   return (b->number > 0 ?
+                           0x1000000 + b->number :
+                           b->number - 0x1000000);
+                 }
+               return b->number;
+             }
+         }
+      }
+
+  return cont;
+}
+\f
+static void
+breakpoint_1 (bnum)
+     int bnum;
+{
+  register struct breakpoint *b;
+  register struct command_line *l;
+  register struct symbol *sym;
+  CORE_ADDR last_addr = (CORE_ADDR)-1;
+
+  ALL_BREAKPOINTS (b)
+    if (bnum == -1 || bnum == b->number)
+      {
+       printf_filtered ("#%-3d %c  0x%08x", b->number,
+               "nyod"[(int) b->enable],
+               b->address);
+       last_addr = b->address;
+       if (b->symtab)
+         {
+           sym = find_pc_function (b->address);
+           if (sym)
+             {
+               fputs_filtered (" in ", stdout);
+               fputs_demangled (SYMBOL_NAME (sym), stdout, 1);
+               fputs_filtered (" (", stdout);
+             }
+           fputs_filtered (b->symtab->filename, stdout);
+           printf_filtered (" line %d", b->line_number);
+           if (sym) fputs_filtered(")", stdout);
+         }
+       else
+             print_address_symbolic (b->address, stdout);
+             
+       printf_filtered ("\n");
+
+       if (b->ignore_count)
+         printf_filtered ("\tignore next %d hits\n", b->ignore_count);
+       if (b->frame)
+         printf_filtered ("\tstop only in stack frame at 0x%x\n", b->frame);
+       if (b->cond)
+         {
+           printf_filtered ("\tbreak only if ");
+           print_expression (b->cond, stdout);
+           printf_filtered ("\n");
+         }
+       if (l = b->commands)
+         while (l)
+           {
+             printf_filtered ("\t%s\n", l->line);
+             l = l->next;
+           }
+      }
+
+  /* Compare against (CORE_ADDR)-1 in case some compiler decides
+     that a comparison of an unsigned with -1 is always false.  */
+  if (last_addr != (CORE_ADDR)-1)
+    set_next_address (last_addr);
+}
+
+static void
+breakpoints_info (bnum_exp)
+     char *bnum_exp;
+{
+  int bnum = -1;
+
+  if (bnum_exp)
+    bnum = parse_and_eval_address (bnum_exp);
+  else if (breakpoint_chain == 0)
+    printf_filtered ("No breakpoints.\n");
+  else
+    printf_filtered ("Breakpoints:\n\
+Num Enb   Address    Where\n");
+
+  breakpoint_1 (bnum);
+}
+
+/* Print a message describing any breakpoints set at PC.  */
+
+static void
+describe_other_breakpoints (pc)
+     register CORE_ADDR pc;
+{
+  register int others = 0;
+  register struct breakpoint *b;
+
+  ALL_BREAKPOINTS (b)
+    if (b->address == pc)
+      others++;
+  if (others > 0)
+    {
+      printf ("Note: breakpoint%s ", (others > 1) ? "s" : "");
+      ALL_BREAKPOINTS (b)
+       if (b->address == pc)
+         {
+           others--;
+           printf ("%d%s%s ",
+                   b->number,
+                   (b->enable == disabled) ? " (disabled)" : "",
+                   (others > 1) ? "," : ((others == 1) ? " and" : ""));
+         }
+      printf ("also set at pc 0x%x.\n", pc);
+    }
+}
+\f
+/* Set the default place to put a breakpoint
+   for the `break' command with no arguments.  */
+
+void
+set_default_breakpoint (valid, addr, symtab, line)
+     int valid;
+     CORE_ADDR addr;
+     struct symtab *symtab;
+     int line;
+{
+  default_breakpoint_valid = valid;
+  default_breakpoint_address = addr;
+  default_breakpoint_symtab = symtab;
+  default_breakpoint_line = line;
+}
+
+/* Rescan breakpoints at address ADDRESS,
+   marking the first one as "first" and any others as "duplicates".
+   This is so that the bpt instruction is only inserted once.  */
+
+static void
+check_duplicates (address)
+     CORE_ADDR address;
+{
+  register struct breakpoint *b;
+  register int count = 0;
+
+  ALL_BREAKPOINTS (b)
+    if (b->enable != disabled && b->address == address)
+      {
+       count++;
+       b->duplicate = count > 1;
+      }
+}
+
+/* Low level routine to set a breakpoint.
+   Takes as args the three things that every breakpoint must have.
+   Returns the breakpoint object so caller can set other things.
+   Does not set the breakpoint number!
+   Does not print anything.  */
+
+static struct breakpoint *
+set_raw_breakpoint (sal)
+     struct symtab_and_line sal;
+{
+  register struct breakpoint *b, *b1;
+
+  b = (struct breakpoint *) xmalloc (sizeof (struct breakpoint));
+  bzero (b, sizeof *b);
+  b->address = sal.pc;
+  b->symtab = sal.symtab;
+  b->line_number = sal.line;
+  b->enable = enabled;
+  b->next = 0;
+  b->silent = 0;
+
+  /* Add this breakpoint to the end of the chain
+     so that a list of breakpoints will come out in order
+     of increasing numbers.  */
+
+  b1 = breakpoint_chain;
+  if (b1 == 0)
+    breakpoint_chain = b;
+  else
+    {
+      while (b1->next)
+       b1 = b1->next;
+      b1->next = b;
+    }
+
+  check_duplicates (sal.pc);
+
+  return b;
+}
+
+/* Set a breakpoint that will evaporate an end of command
+   at address specified by SAL.
+   Restrict it to frame FRAME if FRAME is nonzero.  */
+
+void
+set_momentary_breakpoint (sal, frame)
+     struct symtab_and_line sal;
+     FRAME frame;
+{
+  register struct breakpoint *b;
+  b = set_raw_breakpoint (sal);
+  b->number = -3;
+  b->enable = delete;
+  b->frame = (frame ? FRAME_FP (frame) : 0);
+}
+
+void
+clear_momentary_breakpoints ()
+{
+  register struct breakpoint *b;
+  ALL_BREAKPOINTS (b)
+    if (b->number == -3)
+      {
+       delete_breakpoint (b);
+       break;
+      }
+}
+\f
+/* Set a breakpoint from a symtab and line.
+   If TEMPFLAG is nonzero, it is a temporary breakpoint.
+   Print the same confirmation messages that the breakpoint command prints.  */
+
+void
+set_breakpoint (s, line, tempflag)
+     struct symtab *s;
+     int line;
+     int tempflag;
+{
+  register struct breakpoint *b;
+  struct symtab_and_line sal;
+  
+  sal.symtab = s;
+  sal.line = line;
+  sal.pc = find_line_pc (sal.symtab, sal.line);
+  if (sal.pc == 0)
+    error ("No line %d in file \"%s\".\n", sal.line, sal.symtab->filename);
+  else
+    {
+      describe_other_breakpoints (sal.pc);
+
+      b = set_raw_breakpoint (sal);
+      b->number = ++breakpoint_count;
+      b->cond = 0;
+      if (tempflag)
+       b->enable = temporary;
+
+      printf ("Breakpoint %d at 0x%x", b->number, b->address);
+      if (b->symtab)
+       printf (": file %s, line %d.", b->symtab->filename, b->line_number);
+      printf ("\n");
+    }
+}
+\f
+/* Set a breakpoint according to ARG (function, linenum or *address)
+   and make it temporary if TEMPFLAG is nonzero. */
+
+static void
+break_command_1 (arg, tempflag, from_tty)
+     char *arg;
+     int tempflag, from_tty;
+{
+  struct symtabs_and_lines sals;
+  struct symtab_and_line sal;
+  register struct expression *cond = 0;
+  register struct breakpoint *b;
+  char *save_arg;
+  int i;
+  CORE_ADDR pc;
+
+  sals.sals = NULL;
+  sals.nelts = 0;
+
+  sal.line = sal.pc = sal.end = 0;
+  sal.symtab = 0;
+
+  /* If no arg given, or if first arg is 'if ', use the default breakpoint. */
+
+  if (!arg || (arg[0] == 'i' && arg[1] == 'f' 
+              && (arg[2] == ' ' || arg[2] == '\t')))
+    {
+      if (default_breakpoint_valid)
+       {
+         sals.sals = (struct symtab_and_line *) 
+           malloc (sizeof (struct symtab_and_line));
+         sal.pc = default_breakpoint_address;
+         sal.line = default_breakpoint_line;
+         sal.symtab = default_breakpoint_symtab;
+         sals.sals[0] = sal;
+         sals.nelts = 1;
+       }
+      else
+       error ("No default breakpoint address now.");
+    }
+  else
+    /* Force almost all breakpoints to be in terms of the
+       current_source_symtab (which is decode_line_1's default).  This
+       should produce the results we want almost all of the time while
+       leaving default_breakpoint_* alone.  */
+    if (default_breakpoint_valid
+       && (!current_source_symtab
+           || (arg && (*arg == '+' || *arg == '-'))))
+      sals = decode_line_1 (&arg, 1, default_breakpoint_symtab,
+                           default_breakpoint_line);
+    else
+      sals = decode_line_1 (&arg, 1, 0, 0);
+  
+  if (! sals.nelts) 
+    return;
+
+  save_arg = arg;
+  for (i = 0; i < sals.nelts; i++)
+    {
+      sal = sals.sals[i];
+      if (sal.pc == 0 && sal.symtab != 0)
+       {
+         pc = find_line_pc (sal.symtab, sal.line);
+         if (pc == 0)
+           error ("No line %d in file \"%s\".",
+                  sal.line, sal.symtab->filename);
+       }
+      else 
+       pc = sal.pc;
+      
+      while (arg && *arg)
+       {
+         if (arg[0] == 'i' && arg[1] == 'f'
+             && (arg[2] == ' ' || arg[2] == '\t'))
+           cond = (struct expression *) parse_c_1 ((arg += 2, &arg),
+                                                   block_for_pc (pc), 0);
+         else
+           error ("Junk at end of arguments.");
+       }
+      arg = save_arg;
+      sals.sals[i].pc = pc;
+    }
+
+  for (i = 0; i < sals.nelts; i++)
+    {
+      sal = sals.sals[i];
+
+      if (from_tty)
+       describe_other_breakpoints (sal.pc);
+
+      b = set_raw_breakpoint (sal);
+      b->number = ++breakpoint_count;
+      b->cond = cond;
+      if (tempflag)
+       b->enable = temporary;
+
+      printf ("Breakpoint %d at 0x%x", b->number, b->address);
+      if (b->symtab)
+       printf (": file %s, line %d.", b->symtab->filename, b->line_number);
+      printf ("\n");
+    }
+
+  if (sals.nelts > 1)
+    {
+      printf ("Multiple breakpoints were set.\n");
+      printf ("Use the \"delete\" command to delete unwanted breakpoints.\n");
+    }
+  free (sals.sals);
+}
+
+static void
+break_command (arg, from_tty)
+     char *arg;
+     int from_tty;
+{
+  break_command_1 (arg, 0, from_tty);
+}
+
+static void
+tbreak_command (arg, from_tty)
+     char *arg;
+     int from_tty;
+{
+  break_command_1 (arg, 1, from_tty);
+}
+\f
+/*
+ * Helper routine for the until_command routine in infcmd.c.  Here
+ * because it uses the mechanisms of breakpoints.
+ */
+void
+until_break_command (arg, from_tty)
+     char *arg;
+     int from_tty;
+{
+  struct symtabs_and_lines sals;
+  struct symtab_and_line sal;
+  FRAME prev_frame = get_prev_frame (selected_frame);
+
+  clear_proceed_status ();
+
+  /* Set a breakpoint where the user wants it and at return from
+     this function */
+  
+  if (default_breakpoint_valid)
+    sals = decode_line_1 (&arg, 1, default_breakpoint_symtab,
+                         default_breakpoint_line);
+  else
+    sals = decode_line_1 (&arg, 1, 0, 0);
+  
+  if (sals.nelts != 1)
+    error ("Couldn't get information on specified line.");
+  
+  sal = sals.sals[0];
+  free (sals.sals);            /* malloc'd, so freed */
+  
+  if (*arg)
+    error ("Junk at end of arguments.");
+  
+  if (sal.pc == 0 && sal.symtab != 0)
+    sal.pc = find_line_pc (sal.symtab, sal.line);
+  
+  if (sal.pc == 0)
+    error ("No line %d in file \"%s\".", sal.line, sal.symtab->filename);
+  
+  set_momentary_breakpoint (sal, selected_frame);
+  
+  /* Keep within the current frame */
+  
+  if (prev_frame)
+    {
+      struct frame_info *fi;
+      
+      fi = get_frame_info (prev_frame);
+      sal = find_pc_line (fi->pc, 0);
+      sal.pc = fi->pc;
+      set_momentary_breakpoint (sal, prev_frame);
+    }
+  
+  proceed (-1, -1, 0);
+}
+\f
+static void
+clear_command (arg, from_tty)
+     char *arg;
+     int from_tty;
+{
+  register struct breakpoint *b, *b1;
+  struct symtabs_and_lines sals;
+  struct symtab_and_line sal;
+  register struct breakpoint *found;
+  int i;
+
+  if (arg)
+    {
+      sals = decode_line_spec (arg, 1);
+    }
+  else
+    {
+      sals.sals = (struct symtab_and_line *) malloc (sizeof (struct symtab_and_line));
+      sal.line = default_breakpoint_line;
+      sal.symtab = default_breakpoint_symtab;
+      sal.pc = 0;
+      if (sal.symtab == 0)
+       error ("No source file specified.");
+
+      sals.sals[0] = sal;
+      sals.nelts = 1;
+    }
+
+  for (i = 0; i < sals.nelts; i++)
+    {
+      /* If exact pc given, clear bpts at that pc.
+        But if sal.pc is zero, clear all bpts on specified line.  */
+      sal = sals.sals[i];
+      found = (struct breakpoint *) 0;
+      while (breakpoint_chain
+            && (sal.pc ? breakpoint_chain->address == sal.pc
+                : (breakpoint_chain->symtab == sal.symtab
+                   && breakpoint_chain->line_number == sal.line)))
+       {
+         b1 = breakpoint_chain;
+         breakpoint_chain = b1->next;
+         b1->next = found;
+         found = b1;
+       }
+
+      ALL_BREAKPOINTS (b)
+       while (b->next
+              && (sal.pc ? b->next->address == sal.pc
+                  : (b->next->symtab == sal.symtab
+                     && b->next->line_number == sal.line)))
+         {
+           b1 = b->next;
+           b->next = b1->next;
+           b1->next = found;
+           found = b1;
+         }
+
+      if (found == 0)
+       error ("No breakpoint at %s.", arg);
+
+      if (found->next) from_tty = 1; /* Always report if deleted more than one */
+      if (from_tty) printf ("Deleted breakpoint%s ", found->next ? "s" : "");
+      while (found)
+       {
+         if (from_tty) printf ("%d ", found->number);
+         b1 = found->next;
+         delete_breakpoint (found);
+         found = b1;
+       }
+      if (from_tty) putchar ('\n');
+    }
+  free (sals.sals);
+}
+\f
+/* Delete breakpoint number BNUM if it is a `delete' breakpoint.
+   This is called after breakpoint BNUM has been hit.
+   Also delete any breakpoint numbered -3 unless there are breakpoint
+   commands to be executed.  */
+
+void
+breakpoint_auto_delete (bnum)
+     int bnum;
+{
+  register struct breakpoint *b;
+  if (bnum != 0)
+    ALL_BREAKPOINTS (b)
+      if (b->number == bnum)
+       {
+         if (b->enable == delete)
+           delete_breakpoint (b);
+         break;
+       }
+  if (breakpoint_commands == 0)
+    clear_momentary_breakpoints ();
+}
+
+static void
+delete_breakpoint (bpt)
+     struct breakpoint *bpt;
+{
+  register struct breakpoint *b;
+
+  if (bpt->inserted)
+    write_memory (bpt->address, bpt->shadow_contents, sizeof break_insn);
+
+  if (breakpoint_chain == bpt)
+    breakpoint_chain = bpt->next;
+
+  ALL_BREAKPOINTS (b)
+    if (b->next == bpt)
+      {
+       b->next = bpt->next;
+       break;
+      }
+
+  check_duplicates (bpt->address);
+
+  free_command_lines (bpt->commands);
+  if (bpt->cond)
+    free (bpt->cond);
+
+  if (xgdb_verbose && bpt->number >=0)
+    printf ("breakpoint #%d deleted\n", bpt->number);
+
+  free (bpt);
+}
+
+static void map_breakpoint_numbers ();
+
+static void
+delete_command (arg, from_tty)
+     char *arg;
+     int from_tty;
+{
+  register struct breakpoint *b, *b1;
+
+  if (arg == 0)
+    {
+      /* Ask user only if there are some breakpoints to delete.  */
+      if (!from_tty
+         || breakpoint_chain && query ("Delete all breakpoints? "))
+       {
+         /* No arg; clear all breakpoints.  */
+         while (breakpoint_chain)
+           delete_breakpoint (breakpoint_chain);
+       }
+    }
+  else
+    map_breakpoint_numbers (arg, delete_breakpoint);
+}
+
+/* Delete all breakpoints.
+   Done when new symtabs are loaded, since the break condition expressions
+   may become invalid, and the breakpoints are probably wrong anyway.  */
+
+void
+clear_breakpoints ()
+{
+  delete_command (0, 0);
+}
+\f
+/* Set ignore-count of breakpoint number BPTNUM to COUNT.
+   If from_tty is nonzero, it prints a message to that effect,
+   which ends with a period (no newline).  */
+
+void
+set_ignore_count (bptnum, count, from_tty)
+     int bptnum, count, from_tty;
+{
+  register struct breakpoint *b;
+
+  if (count < 0)
+    count = 0;
+
+  ALL_BREAKPOINTS (b)
+    if (b->number == bptnum)
+      {
+       b->ignore_count = count;
+       if (!from_tty)
+         return;
+       else if (count == 0)
+         printf ("Will stop next time breakpoint %d is reached.", bptnum);
+       else if (count == 1)
+         printf ("Will ignore next crossing of breakpoint %d.", bptnum);
+       else
+         printf ("Will ignore next %d crossings of breakpoint %d.",
+                 count, bptnum);
+       return;
+      }
+
+  error ("No breakpoint number %d.", bptnum);
+}
+
+/* Clear the ignore counts of all breakpoints.  */
+void
+breakpoint_clear_ignore_counts ()
+{
+  struct breakpoint *b;
+
+  ALL_BREAKPOINTS (b)
+    b->ignore_count = 0;
+}
+
+/* Command to set ignore-count of breakpoint N to COUNT.  */
+
+static void
+ignore_command (args, from_tty)
+     char *args;
+     int from_tty;
+{
+  register char *p = args;
+  register int num;
+
+  if (p == 0)
+    error_no_arg ("a breakpoint number");
+  
+  while (*p >= '0' && *p <= '9') p++;
+  if (*p && *p != ' ' && *p != '\t')
+    error ("First argument must be a breakpoint number.");
+
+  num = atoi (args);
+
+  if (*p == 0)
+    error ("Second argument (specified ignore-count) is missing.");
+
+  set_ignore_count (num, parse_and_eval_address (p), from_tty);
+  printf ("\n");
+}
+\f
+/* Call FUNCTION on each of the breakpoints
+   whose numbers are given in ARGS.  */
+
+static void
+map_breakpoint_numbers (args, function)
+     char *args;
+     void (*function) ();
+{
+  register char *p = args;
+  register char *p1;
+  register int num;
+  register struct breakpoint *b;
+
+  if (p == 0)
+    error_no_arg ("one or more breakpoint numbers");
+
+  while (*p)
+    {
+      p1 = p;
+      while (*p1 >= '0' && *p1 <= '9') p1++;
+      if (*p1 && *p1 != ' ' && *p1 != '\t')
+       error ("Arguments must be breakpoint numbers.");
+
+      num = atoi (p);
+
+      ALL_BREAKPOINTS (b)
+       if (b->number == num)
+         {
+           function (b);
+           goto win;
+         }
+      printf ("No breakpoint number %d.\n", num);
+    win:
+      p = p1;
+      while (*p == ' ' || *p == '\t') p++;
+    }
+}
+
+static void
+enable_breakpoint (bpt)
+     struct breakpoint *bpt;
+{
+  bpt->enable = enabled;
+
+  if (xgdb_verbose && bpt->number >= 0)
+    printf ("breakpoint #%d enabled\n", bpt->number);
+
+  check_duplicates (bpt->address);
+}
+
+static void
+enable_command (args)
+     char *args;
+{
+  struct breakpoint *bpt;
+  if (args == 0)
+    ALL_BREAKPOINTS (bpt)
+      enable_breakpoint (bpt);
+  else
+    map_breakpoint_numbers (args, enable_breakpoint);
+}
+
+static void
+disable_breakpoint (bpt)
+     struct breakpoint *bpt;
+{
+  bpt->enable = disabled;
+
+  if (xgdb_verbose && bpt->number >= 0)
+    printf ("breakpoint #%d disabled\n", bpt->number);
+
+  check_duplicates (bpt->address);
+}
+
+static void
+disable_command (args)
+     char *args;
+{
+  register struct breakpoint *bpt;
+  if (args == 0)
+    ALL_BREAKPOINTS (bpt)
+      disable_breakpoint (bpt);
+  else
+    map_breakpoint_numbers (args, disable_breakpoint);
+}
+
+static void
+enable_once_breakpoint (bpt)
+     struct breakpoint *bpt;
+{
+  bpt->enable = temporary;
+
+  check_duplicates (bpt->address);
+}
+
+static void
+enable_once_command (args)
+     char *args;
+{
+  map_breakpoint_numbers (args, enable_once_breakpoint);
+}
+
+static void
+enable_delete_breakpoint (bpt)
+     struct breakpoint *bpt;
+{
+  bpt->enable = delete;
+
+  check_duplicates (bpt->address);
+}
+
+static void
+enable_delete_command (args)
+     char *args;
+{
+  map_breakpoint_numbers (args, enable_delete_breakpoint);
+}
+\f
+/*
+ * Use default_breakpoint_'s, or nothing if they aren't valid.
+ */
+struct symtabs_and_lines
+decode_line_spec_1 (string, funfirstline)
+     char *string;
+     int funfirstline;
+{
+  struct symtabs_and_lines sals;
+  if (string == 0)
+    error ("Empty line specification.");
+  if (default_breakpoint_valid)
+    sals = decode_line_1 (&string, funfirstline,
+                         default_breakpoint_symtab, default_breakpoint_line);
+  else
+    sals = decode_line_1 (&string, funfirstline, 0, 0);
+  if (*string)
+    error ("Junk at end of line specification: %s", string);
+  return sals;
+}
+\f
+
+/* Chain containing all defined enable commands.  */
+
+extern struct cmd_list_element 
+  *enablelist, *disablelist,
+  *deletelist, *enablebreaklist;
+
+extern struct cmd_list_element *cmdlist;
+
+void
+_initialize_breakpoint ()
+{
+  breakpoint_chain = 0;
+  breakpoint_count = 0;
+
+  add_com ("ignore", class_breakpoint, ignore_command,
+          "Set ignore-count of breakpoint number N to COUNT.");
+
+  add_com ("commands", class_breakpoint, commands_command,
+          "Set commands to be executed when a breakpoint is hit.\n\
+Give breakpoint number as argument after \"commands\".\n\
+With no argument, the targeted breakpoint is the last one set.\n\
+The commands themselves follow starting on the next line.\n\
+Type a line containing \"end\" to indicate the end of them.\n\
+Give \"silent\" as the first line to make the breakpoint silent;\n\
+then no output is printed when it is hit, except what the commands print.");
+
+  add_com ("condition", class_breakpoint, condition_command,
+          "Specify breakpoint number N to break only if COND is true.\n\
+N is an integer; COND is a C expression to be evaluated whenever\n\
+breakpoint N is reached.  Actually break only when COND is nonzero.");
+
+  add_com ("tbreak", class_breakpoint, tbreak_command,
+          "Set a temporary breakpoint.  Args like \"break\" command.\n\
+Like \"break\" except the breakpoint is only enabled temporarily,\n\
+so it will be disabled when hit.  Equivalent to \"break\" followed\n\
+by using \"enable once\" on the breakpoint number.");
+
+  add_prefix_cmd ("enable", class_breakpoint, enable_command,
+                 "Enable some breakpoints or auto-display expressions.\n\
+Give breakpoint numbers (separated by spaces) as arguments.\n\
+With no subcommand, breakpoints are enabled until you command otherwise.\n\
+This is used to cancel the effect of the \"disable\" command.\n\
+With a subcommand you can enable temporarily.\n\
+\n\
+The \"display\" subcommand applies to auto-displays instead of breakpoints.",
+                 &enablelist, "enable ", 1, &cmdlist);
+
+  add_abbrev_prefix_cmd ("breakpoints", class_breakpoint, enable_command,
+                 "Enable some breakpoints or auto-display expressions.\n\
+Give breakpoint numbers (separated by spaces) as arguments.\n\
+With no subcommand, breakpoints are enabled until you command otherwise.\n\
+This is used to cancel the effect of the \"disable\" command.\n\
+May be abbreviates to simply \"enable\".\n\
+With a subcommand you can enable temporarily.",
+                 &enablebreaklist, "enable breakpoints ", 1, &enablelist);
+
+  add_cmd ("once", no_class, enable_once_command,
+          "Enable breakpoints for one hit.  Give breakpoint numbers.\n\
+If a breakpoint is hit while enabled in this fashion, it becomes disabled.\n\
+See the \"tbreak\" command which sets a breakpoint and enables it once.",
+          &enablebreaklist);
+
+  add_cmd ("delete", no_class, enable_delete_command,
+          "Enable breakpoints and delete when hit.  Give breakpoint numbers.\n\
+If a breakpoint is hit while enabled in this fashion, it is deleted.",
+          &enablebreaklist);
+
+  add_cmd ("delete", no_class, enable_delete_command,
+          "Enable breakpoints and delete when hit.  Give breakpoint numbers.\n\
+If a breakpoint is hit while enabled in this fashion, it is deleted.",
+          &enablelist);
+
+  add_cmd ("once", no_class, enable_once_command,
+          "Enable breakpoints for one hit.  Give breakpoint numbers.\n\
+If a breakpoint is hit while enabled in this fashion, it becomes disabled.\n\
+See the \"tbreak\" command which sets a breakpoint and enables it once.",
+          &enablelist);
+
+  add_prefix_cmd ("disable", class_breakpoint, disable_command,
+          "Disable some breakpoints or auto-display expressions.\n\
+Arguments are breakpoint numbers with spaces in between.\n\
+To disable all breakpoints, give no argument.\n\
+A disabled breakpoint is not forgotten, but has no effect until reenabled.\n\
+\n\
+The \"display\" subcommand applies to auto-displays instead of breakpoints.",
+                 &disablelist, "disable ", 1, &cmdlist);
+  add_com_alias ("dis", "disable", class_breakpoint, 1);
+  add_com_alias ("disa", "disable", class_breakpoint, 1);
+
+  add_abbrev_cmd ("breakpoints", class_breakpoint, disable_command,
+          "Disable some breakpoints or auto-display expressions.\n\
+Arguments are breakpoint numbers with spaces in between.\n\
+To disable all breakpoints, give no argument.\n\
+A disabled breakpoint is not forgotten, but has no effect until reenabled.\n\
+This command may be abbreviated \"disable\".",
+          &disablelist);
+
+  add_prefix_cmd ("delete", class_breakpoint, delete_command,
+          "Delete some breakpoints or auto-display expressions.\n\
+Arguments are breakpoint numbers with spaces in between.\n\
+To delete all breakpoints, give no argument.\n\
+\n\
+Also a prefix command for deletion of other GDB objects.\n\
+The \"unset\" command is also an alias for \"delete\".",
+                 &deletelist, "delete ", 1, &cmdlist);
+  add_com_alias ("d", "delete", class_breakpoint, 1);
+  add_com_alias ("unset", "delete", class_alias, 1);
+
+  add_cmd ("breakpoints", class_alias, delete_command,
+          "Delete some breakpoints or auto-display expressions.\n\
+Arguments are breakpoint numbers with spaces in between.\n\
+To delete all breakpoints, give no argument.\n\
+This command may be abbreviated \"delete\".",
+          &deletelist);
+
+  add_com ("clear", class_breakpoint, clear_command,
+          "Clear breakpoint at specified line or function.\n\
+Argument may be line number, function name, or \"*\" and an address.\n\
+If line number is specified, all breakpoints in that line are cleared.\n\
+If function is specified, breakpoints at beginning of function are cleared.\n\
+If an address is specified, breakpoints at that address are cleared.\n\n\
+With no argument, clears all breakpoints in the line that the selected frame\n\
+is executing in.\n\
+\n\
+See also the \"delete\" command which clears breakpoints by number.");
+
+  add_com ("break", class_breakpoint, break_command,
+          "Set breakpoint at specified line or function.\n\
+Argument may be line number, function name, or \"*\" and an address.\n\
+If line number is specified, break at start of code for that line.\n\
+If function is specified, break at start of code for that function.\n\
+If an address is specified, break at that exact address.\n\
+With no arg, uses current execution address of selected stack frame.\n\
+This is useful for breaking on return to a stack frame.\n\
+\n\
+Multiple breakpoints at one place are permitted, and useful if conditional.\n\
+\n\
+Do \"help breakpoints\" for info on other commands dealing with breakpoints.");
+  add_com_alias ("b", "break", class_run, 1);
+  add_com_alias ("br", "break", class_run, 1);
+  add_com_alias ("bre", "break", class_run, 1);
+  add_com_alias ("brea", "break", class_run, 1);
+
+  add_info ("breakpoints", breakpoints_info,
+           "Status of all breakpoints, or breakpoint number NUMBER.\n\
+Second column is \"y\" for enabled breakpoint, \"n\" for disabled,\n\
+\"o\" for enabled once (disable when hit), \"d\" for enable but delete when hit.\n\
+Then come the address and the file/line number.\n\n\
+Convenience variable \"$_\" and default examine address for \"x\"\n\
+are set to the address of the last breakpoint listed.");
+}
+
diff --git a/usr/src/usr.bin/gdb/core.c b/usr/src/usr.bin/gdb/core.c
new file mode 100644 (file)
index 0000000..307addb
--- /dev/null
@@ -0,0 +1,581 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)core.c     6.3 (Berkeley) 5/8/91";
+#endif /* not lint */
+
+/* Work with core dump and executable files, for GDB.
+   Copyright (C) 1986, 1987, 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING.  If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#include <stdio.h>
+#include "defs.h"
+#include "param.h"
+#include "frame.h"  /* required by inferior.h */
+#include "inferior.h"
+
+#ifdef USG
+#include <sys/types.h>
+#include <fcntl.h>
+#endif
+
+#ifdef COFF_ENCAPSULATE
+#include "a.out.encap.h"
+#else
+#include <a.out.h>
+#endif
+#ifndef N_MAGIC
+#ifdef COFF_FORMAT
+#define N_MAGIC(exec) ((exec).magic)
+#else
+#define N_MAGIC(exec) ((exec).a_magic)
+#endif
+#endif
+#include <signal.h>
+#include <sys/param.h>
+#include <sys/dir.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+
+#ifdef UMAX_CORE
+#include <sys/ptrace.h>
+#else
+#include <sys/user.h>
+#endif
+
+#ifndef N_TXTADDR
+#define N_TXTADDR(hdr) 0
+#endif /* no N_TXTADDR */
+
+#ifndef N_DATADDR
+#define N_DATADDR(hdr) hdr.a_text
+#endif /* no N_DATADDR */
+
+#ifndef COFF_FORMAT
+#ifndef AOUTHDR
+#define AOUTHDR                struct exec
+#endif
+#endif
+
+extern char *sys_siglist[];
+
+extern core_file_command (), exec_file_command ();
+
+/* Hook for `exec_file_command' command to call.  */
+
+void (*exec_file_display_hook) ();
+   
+/* File names of core file and executable file.  */
+
+char *corefile;
+char *execfile;
+
+/* Descriptors on which core file and executable file are open.
+   Note that the execchan is closed when an inferior is created
+   and reopened if the inferior dies or is killed.  */
+
+int corechan;
+int execchan;
+
+/* Last modification time of executable file.
+   Also used in source.c to compare against mtime of a source file.  */
+
+int exec_mtime;
+
+/* Virtual addresses of bounds of the two areas of memory in the core file.  */
+
+CORE_ADDR data_start;
+CORE_ADDR data_end;
+CORE_ADDR stack_start;
+CORE_ADDR stack_end;
+
+#if defined (REG_STACK_SEGMENT)
+/* Start and end of the register stack segment.  */
+CORE_ADDR reg_stack_start;
+CORE_ADDR reg_stack_end;
+#endif /* REG_STACK_SEGMENT */
+
+/* Virtual addresses of bounds of two areas of memory in the exec file.
+   Note that the data area in the exec file is used only when there is no core file.  */
+
+CORE_ADDR text_start;
+CORE_ADDR text_end;
+
+CORE_ADDR exec_data_start;
+CORE_ADDR exec_data_end;
+
+/* Offset within executable file of start of text area data.  */
+
+int text_offset;
+
+/* Offset within executable file of start of data area data.  */
+
+int exec_data_offset;
+
+/* Offset within core file of start of data area data.  */
+
+int data_offset;
+
+/* Offset within core file of start of stack area data.  */
+
+int stack_offset;
+  
+#ifdef COFF_FORMAT
+/* various coff data structures */
+
+FILHDR file_hdr;
+SCNHDR text_hdr;
+SCNHDR data_hdr;
+
+#endif /* not COFF_FORMAT */
+
+/* a.out header saved in core file.  */
+  
+AOUTHDR core_aouthdr;
+
+/* a.out header of exec file.  */
+
+AOUTHDR exec_aouthdr;
+
+void validate_files ();
+unsigned int register_addr ();
+\f
+/* Call this to specify the hook for exec_file_command to call back.
+   This is called from the x-window display code.  */
+
+void
+specify_exec_file_hook (hook)
+     void (*hook) ();
+{
+  exec_file_display_hook = hook;
+}
+
+/* The exec file must be closed before running an inferior.
+   If it is needed again after the inferior dies, it must
+   be reopened.  */
+
+void
+close_exec_file ()
+{
+  if (execchan >= 0)
+    close (execchan);
+  execchan = -1;
+}
+
+void
+reopen_exec_file ()
+{
+  if (execchan < 0 && execfile != 0)
+    {
+      char *filename = concat (execfile, "", "");
+      exec_file_command (filename, 0);
+      free (filename);
+    }
+}
+\f
+/* If we have both a core file and an exec file,
+   print a warning if they don't go together.
+   This should really check that the core file came
+   from that exec file, but I don't know how to do it.  */
+
+void
+validate_files ()
+{
+  if (execfile != 0 && corefile != 0)
+    {
+      struct stat st_core;
+
+      if (fstat (corechan, &st_core) < 0)
+       /* It might be a good idea to print an error message.
+          On the other hand, if the user tries to *do* anything with
+          the core file, (s)he'll find out soon enough.  */
+       return;
+
+      if (N_MAGIC (core_aouthdr) != 0
+         && bcmp (&core_aouthdr, &exec_aouthdr, sizeof core_aouthdr))
+       printf ("Warning: core file does not match specified executable file.\n");
+      else if (exec_mtime > st_core.st_mtime) {
+#ifdef KERNELDEBUG
+       extern int kernel_debugging;
+        if (!kernel_debugging)
+#endif
+       printf ("Warning: exec file is newer than core file.\n");
+      }
+    }
+}
+
+/* Return the name of the executable file as a string.
+   ERR nonzero means get error if there is none specified;
+   otherwise return 0 in that case.  */
+
+char *
+get_exec_file (err)
+     int err;
+{
+  if (err && execfile == 0)
+    error ("No executable file specified.\n\
+Use the \"exec-file\" and \"symbol-file\" commands.");
+  return execfile;
+}
+
+int
+have_core_file_p ()
+{
+  return corefile != 0;
+}
+
+static void
+files_info ()
+{
+  char *symfile;
+  extern char *get_sym_file ();
+
+  if (execfile)
+    printf ("Executable file \"%s\".\n", execfile);
+  else
+    printf ("No executable file\n");
+  if (corefile == 0)
+    printf ("No core dump file\n");
+  else
+    printf ("Core dump file \"%s\".\n", corefile);
+
+  if (have_inferior_p ())
+    printf ("Using the running image of the program, rather than these files.\n");
+
+  symfile = get_sym_file ();
+  if (symfile != 0)
+    printf ("Symbols from \"%s\".\n", symfile);
+
+#ifdef FILES_INFO_HOOK
+  if (FILES_INFO_HOOK ())
+    return;
+#endif
+
+  if (! have_inferior_p ())
+    {
+      if (execfile)
+       {
+         printf ("Text segment in executable from 0x%x to 0x%x.\n",
+                 text_start, text_end);
+         printf ("Data segment in executable from 0x%x to 0x%x.\n",
+                 exec_data_start, exec_data_end);
+         if (corefile)
+           printf ("(But since we have a core file, we're using...)\n");
+       }
+      if (corefile)
+       {
+         printf ("Data segment in core file from 0x%x to 0x%x.\n",
+                 data_start, data_end);
+         printf ("Stack segment in core file from 0x%x to 0x%x.\n",
+                 stack_start, stack_end);
+       }
+    }
+}
+\f
+/* Read "memory data" from core file and/or executable file.
+   Returns zero if successful, 1 if xfer_core_file failed, errno value if
+   ptrace failed. */
+
+int
+read_memory (memaddr, myaddr, len)
+     CORE_ADDR memaddr;
+     char *myaddr;
+     int len;
+{
+  if (len == 0)
+    return 0;
+
+  if (have_inferior_p ())
+    {
+      if (remote_debugging)
+       return remote_read_inferior_memory (memaddr, myaddr, len);
+      else
+       return read_inferior_memory (memaddr, myaddr, len);
+    }
+  else
+      return xfer_core_file (memaddr, myaddr, len);
+}
+
+/* Write LEN bytes of data starting at address MYADDR
+   into debugged program memory at address MEMADDR.
+   Returns zero if successful, or an errno value if ptrace failed.  */
+
+int
+write_memory (memaddr, myaddr, len)
+     CORE_ADDR memaddr;
+     char *myaddr;
+     int len;
+{
+  if (have_inferior_p ())
+    {
+      if (remote_debugging)
+       return remote_write_inferior_memory (memaddr, myaddr, len);
+      else
+       return write_inferior_memory (memaddr, myaddr, len);
+    }
+  else
+    error ("Can write memory only when program being debugged is running.");
+}
+
+#ifndef XFER_CORE_FILE
+int (*core_file_hook)();      /* hook to handle special core files like
+                                like /dev/mem and crash dumps */
+
+/* Read from the program's memory (except for inferior processes).
+   This function is misnamed, since it only reads, never writes; and
+   since it will use the core file and/or executable file as necessary.
+
+   It should be extended to write as well as read, FIXME, for patching files.
+
+   Return 0 if address could be read, 1 if not. */
+
+int
+xfer_core_file (memaddr, myaddr, len)
+     CORE_ADDR memaddr;
+     char *myaddr;
+     int len;
+{
+  register int i;
+  register int val;
+  int xferchan;
+  char **xferfile;
+  int fileptr;
+  int returnval = 0;
+
+  if (core_file_hook)
+      return ((*core_file_hook)(memaddr, myaddr, len));
+
+  while (len > 0)
+    {
+      xferfile = 0;
+      xferchan = 0;
+
+      /* Determine which file the next bunch of addresses reside in,
+        and where in the file.  Set the file's read/write pointer
+        to point at the proper place for the desired address
+        and set xferfile and xferchan for the correct file.
+
+        If desired address is nonexistent, leave them zero.
+
+        i is set to the number of bytes that can be handled
+        along with the next address.
+
+        We put the most likely tests first for efficiency.  */
+
+      /* Note that if there is no core file
+        data_start and data_end are equal.  */
+      if (memaddr >= data_start && memaddr < data_end)
+       {
+         i = min (len, data_end - memaddr);
+         fileptr = memaddr - data_start + data_offset;
+         xferfile = &corefile;
+         xferchan = corechan;
+       }
+      /* Note that if there is no core file
+        stack_start and stack_end are equal.  */
+      else if (memaddr >= stack_start && memaddr < stack_end)
+       {
+         i = min (len, stack_end - memaddr);
+         fileptr = memaddr - stack_start + stack_offset;
+         xferfile = &corefile;
+         xferchan = corechan;
+       }
+#ifdef REG_STACK_SEGMENT
+      /* Pyramids have an extra segment in the virtual address space
+         for the (control) stack of register-window frames */
+      else if (memaddr >= reg_stack_start && memaddr < reg_stack_end)
+       {
+         i = min (len, reg_stack_end - memaddr);
+         fileptr = memaddr - reg_stack_start + reg_stack_offset;
+         xferfile = &corefile;
+         xferchan = corechan;
+       }
+#endif /* REG_STACK_SEGMENT */
+
+      else if (corechan < 0
+              && memaddr >= exec_data_start && memaddr < exec_data_end)
+       {
+         i = min (len, exec_data_end - memaddr);
+         fileptr = memaddr - exec_data_start + exec_data_offset;
+         xferfile = &execfile;
+         xferchan = execchan;
+       }
+      else if (memaddr >= text_start && memaddr < text_end)
+       {
+         i = min (len, text_end - memaddr);
+         fileptr = memaddr - text_start + text_offset;
+         xferfile = &execfile;
+         xferchan = execchan;
+       }
+      else if (memaddr < text_start)
+       {
+         i = min (len, text_start - memaddr);
+       }
+      else if (memaddr >= text_end
+              && memaddr < (corechan >= 0? data_start : exec_data_start))
+       {
+         i = min (len, data_start - memaddr);
+       }
+      else if (corechan >= 0
+              && memaddr >= data_end && memaddr < stack_start)
+       {
+         i = min (len, stack_start - memaddr);
+       }
+      else if (corechan < 0 && memaddr >= exec_data_end)
+       {
+         /* Since there is nothing at higher addresses than data
+            (without a core file or an inferior, there is no
+            stack, set i to do the rest of the operation now.  */
+         i = len;
+       }
+#ifdef REG_STACK_SEGMENT
+      else if (memaddr >= reg_stack_end && reg_stack_end != 0)
+       {
+         i = min (len, reg_stack_start - memaddr);
+       }
+      else if (memaddr >= stack_end && memaddr < reg_stack_start)
+#else /* no REG_STACK_SEGMENT.  */
+      else if (memaddr >= stack_end && stack_end != 0)
+#endif /* no REG_STACK_SEGMENT.  */
+       {
+         /* Since there is nothing at higher addresses than
+            the stack, set i to do the rest of the operation now.  */
+         i = len;
+       }
+      else
+       {
+         /* Address did not classify into one of the known ranges.
+            This shouldn't happen; we catch the endpoints.  */
+         fatal ("Internal: Bad case logic in xfer_core_file.");
+       }
+
+      /* Now we know which file to use.
+        Set up its pointer and transfer the data.  */
+      if (xferfile)
+       {
+         if (*xferfile == 0)
+           if (xferfile == &execfile)
+             error ("No program file to examine.");
+           else
+             error ("No core dump file or running program to examine.");
+         val = lseek (xferchan, fileptr, 0);
+         if (val == -1)
+           perror_with_name (*xferfile);
+         val = myread (xferchan, myaddr, i);
+         if (val < 0)
+           perror_with_name (*xferfile);
+       }
+      /* If this address is for nonexistent memory,
+        read zeros if reading, or do nothing if writing.
+        Actually, we never right.  */
+      else
+       {
+         bzero (myaddr, i);
+         returnval = 1;
+       }
+
+      memaddr += i;
+      myaddr += i;
+      len -= i;
+    }
+  return returnval;
+}
+#endif /* XFER_CORE_FILE */
+\f
+/* My replacement for the read system call.
+   Used like `read' but keeps going if `read' returns too soon.  */
+
+int
+myread (desc, addr, len)
+     int desc;
+     char *addr;
+     int len;
+{
+  register int val;
+  int orglen = len;
+
+  while (len > 0)
+    {
+      val = read (desc, addr, len);
+      if (val < 0)
+       return val;
+      if (val == 0)
+       return orglen - len;
+      len -= val;
+      addr += val;
+    }
+  return orglen;
+}
+\f
+#ifdef REGISTER_U_ADDR
+
+/* Return the address in the core dump or inferior of register REGNO.
+   BLOCKEND is the address of the end of the user structure.  */
+
+unsigned int
+register_addr (regno, blockend)
+     int regno;
+     int blockend;
+{
+  int addr;
+
+  if (regno < 0 || regno >= NUM_REGS)
+    error ("Invalid register number %d.", regno);
+
+  REGISTER_U_ADDR (addr, blockend, regno);
+
+  return addr;
+}
+
+#endif /* REGISTER_U_ADDR */
+\f
+void
+_initialize_core()
+{
+  corechan = -1;
+  execchan = -1;
+  corefile = 0;
+  execfile = 0;
+  exec_file_display_hook = 0;
+
+  text_start = 0;
+  text_end = 0;
+  data_start = 0;
+  data_end = 0;
+  exec_data_start = 0;
+  exec_data_end = 0;
+  stack_start = STACK_END_ADDR;
+  stack_end = STACK_END_ADDR;
+
+  add_com ("core-file", class_files, core_file_command,
+          "Use FILE as core dump for examining memory and registers.\n\
+No arg means have no core file.");
+  add_com ("exec-file", class_files, exec_file_command,
+          "Use FILE as program for getting contents of pure memory.\n\
+If FILE cannot be found as specified, your execution directory path\n\
+is searched for a command of that name.\n\
+No arg means have no executable file.");
+  add_info ("files", files_info, "Names of files being debugged.");
+}
+
diff --git a/usr/src/usr.bin/gdb/dbxread.c b/usr/src/usr.bin/gdb/dbxread.c
new file mode 100644 (file)
index 0000000..1e95d96
--- /dev/null
@@ -0,0 +1,5587 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)dbxread.c  6.3 (Berkeley) 5/8/91";
+#endif /* not lint */
+
+/* Read dbx symbol tables and convert to internal format, for GDB.
+   Copyright (C) 1986, 1987, 1988, 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING.  If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+\f
+/* Symbol read-in occurs in two phases:
+   1.  A scan (read_dbx_symtab()) of the entire executable, whose sole
+       purpose is to make a list of symbols (partial symbol table)
+       which will cause symbols
+       to be read in if referenced.  This scan happens when the
+       "symbol-file" command is given (symbol_file_command()).
+   2.  Full read-in of symbols.  (psymtab_to_symtab()).  This happens
+       when a symbol in a file for which symbols have not yet been
+       read in is referenced.
+   2a.  The "add-file" command.  Similar to #2.  */
+
+#include <stdio.h>
+#include "defs.h"
+#include "param.h"
+
+#ifdef READ_DBX_FORMAT
+
+#ifdef USG
+#include <sys/types.h>
+#include <fcntl.h>
+#define L_SET 0
+#define L_INCR 1
+#endif
+
+#ifdef COFF_ENCAPSULATE
+#include "a.out.encap.h"
+#include "stab.gnu.h"
+#else
+#include <a.out.h>
+#include <stab.h>
+#endif
+#include <ctype.h>
+
+#ifndef NO_GNU_STABS
+/*
+ * Define specifically gnu symbols here.
+ */
+
+/* The following type indicates the definition of a symbol as being
+   an indirect reference to another symbol.  The other symbol
+   appears as an undefined reference, immediately following this symbol.
+
+   Indirection is asymmetrical.  The other symbol's value will be used
+   to satisfy requests for the indirect symbol, but not vice versa.
+   If the other symbol does not have a definition, libraries will
+   be searched to find a definition.  */
+#ifndef N_INDR
+#define N_INDR 0xa
+#endif
+
+/* The following symbols refer to set elements.
+   All the N_SET[ATDB] symbols with the same name form one set.
+   Space is allocated for the set in the text section, and each set
+   element's value is stored into one word of the space.
+   The first word of the space is the length of the set (number of elements).
+
+   The address of the set is made into an N_SETV symbol
+   whose name is the same as the name of the set.
+   This symbol acts like a N_DATA global symbol
+   in that it can satisfy undefined external references.  */
+
+#ifndef N_SETA
+#define        N_SETA  0x14            /* Absolute set element symbol */
+#endif                         /* This is input to LD, in a .o file.  */
+
+#ifndef N_SETT
+#define        N_SETT  0x16            /* Text set element symbol */
+#endif                         /* This is input to LD, in a .o file.  */
+
+#ifndef N_SETD
+#define        N_SETD  0x18            /* Data set element symbol */
+#endif                         /* This is input to LD, in a .o file.  */
+
+#ifndef N_SETB
+#define        N_SETB  0x1A            /* Bss set element symbol */
+#endif                         /* This is input to LD, in a .o file.  */
+
+/* Macros dealing with the set element symbols defined in a.out.h */
+#define        SET_ELEMENT_P(x)        ((x)>=N_SETA&&(x)<=(N_SETB|N_EXT))
+#define TYPE_OF_SET_ELEMENT(x) ((x)-N_SETA+N_ABS)
+
+#ifndef N_SETV
+#define N_SETV 0x1C            /* Pointer to set vector in data area.  */
+#endif                         /* This is output from LD.  */
+
+#ifndef N_WARNING
+#define N_WARNING 0x1E         /* Warning message to print if file included */
+#endif                         /* This is input to ld */
+
+#ifndef __GNU_STAB__
+
+/* Line number for the data section.  This is to be used to describe
+   the source location of a variable declaration.  */
+#ifndef N_DSLINE
+#define N_DSLINE (N_SLINE+N_DATA-N_TEXT)
+#endif
+
+/* Line number for the bss section.  This is to be used to describe
+   the source location of a variable declaration.  */
+#ifndef N_BSLINE
+#define N_BSLINE (N_SLINE+N_BSS-N_TEXT)
+#endif
+
+#endif /* not __GNU_STAB__ */
+#endif /* NO_GNU_STABS */
+
+#include <obstack.h>
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+
+#include "symtab.h"
+
+#ifndef COFF_FORMAT
+#ifndef AOUTHDR
+#define AOUTHDR                struct exec
+#endif
+#endif
+
+static void add_symbol_to_list ();
+static void read_dbx_symtab ();
+static void process_one_symbol ();
+static void free_all_psymbols ();
+static struct type *read_type ();
+static struct type *read_range_type ();
+static struct type *read_enum_type ();
+static struct type *read_struct_type ();
+static struct type *read_array_type ();
+static long read_number ();
+static void finish_block ();
+static struct blockvector *make_blockvector ();
+static struct symbol *define_symbol ();
+static void start_subfile ();
+static int hashname ();
+static void hash_symsegs ();
+static struct pending *copy_pending ();
+static void fix_common_block ();
+
+static void add_undefined_type ();
+static void cleanup_undefined_types ();
+
+extern char *index();
+
+extern struct symtab *read_symsegs ();
+extern void free_all_symtabs ();
+extern void free_all_psymtabs ();
+extern void free_inclink_symtabs ();
+
+/* C++ */
+static struct type **read_args ();
+
+/* Macro to determine which symbols to ignore when reading the first symbol
+   of a file.  Some machines override this definition. */
+#ifdef N_NSYMS
+#ifndef IGNORE_SYMBOL
+/* This code is used on Ultrix systems.  Ignore it */
+#define IGNORE_SYMBOL(type)  (type == N_NSYMS)
+#endif
+#else
+#ifndef IGNORE_SYMBOL
+/* Don't ignore any symbols. */
+#define IGNORE_SYMBOL(type) (0)
+#endif
+#endif /* not N_NSYMS */
+
+/* Macro for number of symbol table entries (in usual a.out format).
+   Some machines override this definition.  */
+#ifndef NUMBER_OF_SYMBOLS
+#ifdef COFF_HEADER
+#define NUMBER_OF_SYMBOLS \
+  ((COFF_HEADER(hdr) ? hdr.coffhdr.filehdr.f_nsyms : hdr.a_syms) /     \
+   sizeof (struct nlist))
+#else
+#define NUMBER_OF_SYMBOLS (hdr.a_syms / sizeof (struct nlist))
+#endif
+#endif
+
+/* Macro for file-offset of symbol table (in usual a.out format).  */
+#ifndef SYMBOL_TABLE_OFFSET
+#define SYMBOL_TABLE_OFFSET N_SYMOFF (hdr)
+#endif
+
+/* Macro for file-offset of string table (in usual a.out format).  */
+#ifndef STRING_TABLE_OFFSET
+#define STRING_TABLE_OFFSET (N_SYMOFF (hdr) + hdr.a_syms)
+#endif
+
+/* Macro to store the length of the string table data in INTO.  */
+#ifndef READ_STRING_TABLE_SIZE
+#define READ_STRING_TABLE_SIZE(INTO)           \
+{ val = myread (desc, &INTO, sizeof INTO);     \
+  if (val < 0) perror_with_name (name); }
+#endif
+
+/* Macro to declare variables to hold the file's header data.  */
+#ifndef DECLARE_FILE_HEADERS
+#define DECLARE_FILE_HEADERS  AOUTHDR hdr
+#endif
+
+/* Macro to read the header data from descriptor DESC and validate it.
+   NAME is the file name, for error messages.  */
+#ifndef READ_FILE_HEADERS
+#ifdef HEADER_SEEK_FD
+#define READ_FILE_HEADERS(DESC, NAME)          \
+{ HEADER_SEEK_FD (DESC);                       \
+  val = myread (DESC, &hdr, sizeof hdr);       \
+  if (val < 0) perror_with_name (NAME);                \
+  if (N_BADMAG (hdr))                          \
+    error ("File \"%s\" not in executable format.", NAME); }
+#else
+#define READ_FILE_HEADERS(DESC, NAME)          \
+{ val = myread (DESC, &hdr, sizeof hdr);       \
+  if (val < 0) perror_with_name (NAME);                \
+  if (N_BADMAG (hdr))                          \
+    error ("File \"%s\" not in executable format.", NAME); }
+#endif
+#endif
+
+/* Non-zero if this is an object (.o) file, rather than an executable.
+   Distinguishing between the two is rarely necessary (and seems like
+   a hack, but there is no other way to do ADDR_OF_TEXT_SEGMENT
+   right for SunOS).  */
+#if !defined (IS_OBJECT_FILE)
+/* This will not work
+   if someone decides to make ld preserve relocation info.  */
+#define IS_OBJECT_FILE (hdr.a_trsize != 0)
+#endif
+
+/* Macro for size of text segment */
+#ifndef SIZE_OF_TEXT_SEGMENT
+#define SIZE_OF_TEXT_SEGMENT hdr.a_text
+#endif
+
+/* Get the address in debugged memory of the start
+   of the text segment.  */
+#if !defined (ADDR_OF_TEXT_SEGMENT)
+#if defined (N_TXTADDR)
+#define ADDR_OF_TEXT_SEGMENT (IS_OBJECT_FILE ? 0 : N_TXTADDR (hdr))
+#else /* no N_TXTADDR */
+#define ADDR_OF_TEXT_SEGMENT 0
+#endif /* no N_TXTADDR */
+#endif /* no ADDR_OF_TEXT_SEGMENT */
+
+/* Macro to get entry point from headers.  */
+#ifndef ENTRY_POINT
+#define ENTRY_POINT hdr.a_entry
+#endif
+
+/* Macro for name of symbol to indicate a file compiled with gcc. */
+#ifndef GCC_COMPILED_FLAG_SYMBOL
+#define GCC_COMPILED_FLAG_SYMBOL "gcc_compiled."
+#endif
+
+/* Convert stab register number (from `r' declaration) to a gdb REGNUM.  */
+
+#ifndef STAB_REG_TO_REGNUM
+#define STAB_REG_TO_REGNUM(VALUE) (VALUE)
+#endif
+
+/* Define this as 1 if a pcc declaration of a char or short argument
+   gives the correct address.  Otherwise assume pcc gives the
+   address of the corresponding int, which is not the same on a
+   big-endian machine.  */
+
+#ifndef BELIEVE_PCC_PROMOTION
+#define BELIEVE_PCC_PROMOTION 0
+#endif
+\f
+/* Nonzero means give verbose info on gdb action.  From main.c.  */
+extern int info_verbose;
+
+/* Chain of symtabs made from reading the file's symsegs.
+   These symtabs do not go into symtab_list themselves,
+   but the information is copied from them when appropriate
+   to make the symtabs that will exist permanently.  */
+
+static struct symtab *symseg_chain;
+
+/* Symseg symbol table for the file whose data we are now processing.
+   It is one of those in symseg_chain.  Or 0, for a compilation that
+   has no symseg.  */
+
+static struct symtab *current_symseg;
+
+/* Name of source file whose symbol data we are now processing.
+   This comes from a symbol of type N_SO.  */
+
+static char *last_source_file;
+
+/* Core address of start of text of current source file.
+   This too comes from the N_SO symbol.  */
+
+static CORE_ADDR last_source_start_addr;
+
+/* End of the text segment of the executable file,
+   as found in the symbol _etext.  */
+
+static CORE_ADDR end_of_text_addr;
+
+/* The list of sub-source-files within the current individual compilation.
+   Each file gets its own symtab with its own linetable and associated info,
+   but they all share one blockvector.  */
+
+struct subfile
+{
+  struct subfile *next;
+  char *name;
+  struct linetable *line_vector;
+  int line_vector_length;
+  int line_vector_index;
+  int prev_line_number;
+};
+
+static struct subfile *subfiles;
+
+static struct subfile *current_subfile;
+
+/* Count symbols as they are processed, for error messages.  */
+
+static int symnum;
+
+/* Vector of types defined so far, indexed by their dbx type numbers.
+   (In newer sun systems, dbx uses a pair of numbers in parens,
+    as in "(SUBFILENUM,NUMWITHINSUBFILE)".  Then these numbers must be
+    translated through the type_translations hash table to get
+    the index into the type vector.)  */
+
+static struct typevector *type_vector;
+
+/* Number of elements allocated for type_vector currently.  */
+
+static int type_vector_length;
+
+/* Vector of line number information.  */
+
+static struct linetable *line_vector;
+
+/* Index of next entry to go in line_vector_index.  */
+
+static int line_vector_index;
+
+/* Last line number recorded in the line vector.  */
+
+static int prev_line_number;
+
+/* Number of elements allocated for line_vector currently.  */
+
+static int line_vector_length;
+
+/* Hash table of global symbols whose values are not known yet.
+   They are chained thru the SYMBOL_VALUE, since we don't
+   have the correct data for that slot yet.  */
+/* The use of the LOC_BLOCK code in this chain is nonstandard--
+   it refers to a FORTRAN common block rather than the usual meaning.  */
+
+#define HASHSIZE 127
+static struct symbol *global_sym_chain[HASHSIZE];
+
+/* Record the symbols defined for each context in a list.
+   We don't create a struct block for the context until we
+   know how long to make it.  */
+
+#define PENDINGSIZE 100
+
+struct pending
+{
+  struct pending *next;
+  int nsyms;
+  struct symbol *symbol[PENDINGSIZE];
+};
+
+/* List of free `struct pending' structures for reuse.  */
+struct pending *free_pendings;
+
+/* Here are the three lists that symbols are put on.  */
+
+struct pending *file_symbols;  /* static at top level, and types */
+
+struct pending *global_symbols;        /* global functions and variables */
+
+struct pending *local_symbols; /* everything local to lexical context */
+
+/* List of symbols declared since the last BCOMM.  This list is a tail
+   of local_symbols.  When ECOMM is seen, the symbols on the list
+   are noted so their proper addresses can be filled in later,
+   using the common block base address gotten from the assembler
+   stabs.  */
+
+struct pending *common_block;
+int common_block_i;
+
+/* Stack representing unclosed lexical contexts
+   (that will become blocks, eventually).  */
+
+struct context_stack
+{
+  struct pending *locals;
+  struct pending_block *old_blocks;
+  struct symbol *name;
+  CORE_ADDR start_addr;
+  int depth;
+};
+
+struct context_stack *context_stack;
+
+/* Index of first unused entry in context stack.  */
+int context_stack_depth;
+
+/* Currently allocated size of context stack.  */
+
+int context_stack_size;
+
+/* Nonzero if within a function (so symbols should be local,
+   if nothing says specifically).  */
+
+int within_function;
+
+/* List of blocks already made (lexical contexts already closed).
+   This is used at the end to make the blockvector.  */
+
+struct pending_block
+{
+  struct pending_block *next;
+  struct block *block;
+};
+
+struct pending_block *pending_blocks;
+
+extern CORE_ADDR startup_file_start;   /* From blockframe.c */
+extern CORE_ADDR startup_file_end;     /* From blockframe.c */
+
+/* File name symbols were loaded from.  */
+
+static char *symfile;
+
+/* Low and high symbol values (inclusive) for the global variable
+   entries in the symbol file. */
+
+static int first_global_sym, last_global_sym;
+
+/* Structures with which to manage partial symbol allocation.  */
+
+struct psymbol_allocation_list global_psymbols, static_psymbols;
+
+/* Global variable which, when set, indicates that we are processing a
+   .o file compiled with gcc */
+
+static unsigned char processing_gcc_compilation;
+
+/* Make a list of forward references which haven't been defined.  */
+static struct type **undef_types;
+static int undef_types_allocated, undef_types_length;
+
+  /* Setup a define to deal cleanly with the underscore problem */
+
+#ifdef NAMES_HAVE_UNDERSCORE
+#define HASH_OFFSET 1
+#else
+#define HASH_OFFSET 0
+#endif
+
+#if 0
+/* I'm not sure why this is here.  To debug bugs which cause
+   an infinite loop of allocations, I suppose.  In any event,
+   dumping core when out of memory isn't usually right.  */
+static int
+xxmalloc (n)
+{
+  int v = malloc (n);
+  if (v == 0)
+    {
+      fprintf (stderr, "Virtual memory exhausted.\n");
+      abort ();
+    }
+  return v;
+}
+#else /* not 0 */
+#define xxmalloc xmalloc
+#endif /* not 0 */
+
+/* Make a copy of the string at PTR with SIZE characters in the symbol obstack
+   (and add a null character at the end in the copy).
+   Returns the address of the copy.  */
+
+static char *
+obsavestring (ptr, size)
+     char *ptr;
+     int size;
+{
+  register char *p = (char *) obstack_alloc (symbol_obstack, size + 1);
+  /* Open-coded bcopy--saves function call time.
+     These strings are usually short.  */
+  {
+    register char *p1 = ptr;
+    register char *p2 = p;
+    char *end = ptr + size;
+    while (p1 != end)
+      *p2++ = *p1++;
+  }
+  p[size] = 0;
+  return p;
+}
+
+/* Concatenate strings S1, S2 and S3; return the new string.
+   Space is found in the symbol_obstack.  */
+
+static char *
+obconcat (s1, s2, s3)
+     char *s1, *s2, *s3;
+{
+  register int len = strlen (s1) + strlen (s2) + strlen (s3) + 1;
+  register char *val = (char *) obstack_alloc (symbol_obstack, len);
+  strcpy (val, s1);
+  strcat (val, s2);
+  strcat (val, s3);
+  return val;
+}
+\f
+/* Support for Sun changes to dbx symbol format */
+
+/* For each identified header file, we have a table of types defined
+   in that header file.
+
+   header_files maps header file names to their type tables.
+   It is a vector of n_header_files elements.
+   Each element describes one header file.
+   It contains a vector of types.
+
+   Sometimes it can happen that the same header file produces
+   different results when included in different places.
+   This can result from conditionals or from different
+   things done before including the file.
+   When this happens, there are multiple entries for the file in this table,
+   one entry for each distinct set of results.
+   The entries are distinguished by the INSTANCE field.
+   The INSTANCE field appears in the N_BINCL and N_EXCL symbol table and is
+   used to match header-file references to their corresponding data.  */
+
+struct header_file
+{
+  char *name;                  /* Name of header file */
+  int instance;                        /* Numeric code distinguishing instances
+                                  of one header file that produced
+                                  different results when included.
+                                  It comes from the N_BINCL or N_EXCL.  */
+  struct type **vector;                /* Pointer to vector of types */
+  int length;                  /* Allocated length (# elts) of that vector */
+};
+
+static struct header_file *header_files;
+
+static int n_header_files;
+
+static int n_allocated_header_files;
+
+/* During initial symbol readin, we need to have a structure to keep
+   track of which psymtabs have which bincls in them.  This structure
+   is used during readin to setup the list of dependencies within each
+   partial symbol table. */
+
+struct header_file_location
+{
+  char *name;                  /* Name of header file */
+  int instance;                        /* See above */
+  struct partial_symtab *pst;  /* Partial symtab that has the
+                                  BINCL/EINCL defs for this file */
+};
+
+/* The actual list and controling variables */
+static struct header_file_location *bincl_list, *next_bincl;
+static int bincls_allocated;
+
+/* Within each object file, various header files are assigned numbers.
+   A type is defined or referred to with a pair of numbers
+   (FILENUM,TYPENUM) where FILENUM is the number of the header file
+   and TYPENUM is the number within that header file.
+   TYPENUM is the index within the vector of types for that header file.
+
+   FILENUM == 1 is special; it refers to the main source of the object file,
+   and not to any header file.  FILENUM != 1 is interpreted by looking it up
+   in the following table, which contains indices in header_files.  */
+
+static int *this_object_header_files;
+
+static int n_this_object_header_files;
+
+static int n_allocated_this_object_header_files;
+
+/* When a header file is getting special overriding definitions
+   for one source file, record here the header_files index
+   of its normal definition vector.
+   At other times, this is -1.  */
+
+static int header_file_prev_index;
+
+/* At the start of reading dbx symbols, allocate our tables.  */
+
+static void
+init_header_files ()
+{
+  n_allocated_header_files = 10;
+  header_files = (struct header_file *) xxmalloc (10 * sizeof (struct header_file));
+  n_header_files = 0;
+
+  n_allocated_this_object_header_files = 10;
+  this_object_header_files = (int *) xxmalloc (10 * sizeof (int));
+}
+
+/* At the end of reading dbx symbols, free our tables.  */
+
+static void
+free_header_files ()
+{
+  register int i;
+  for (i = 0; i < n_header_files; i++)
+    free (header_files[i].name);
+  if (header_files) free (header_files);
+  if (this_object_header_files)
+    free (this_object_header_files);
+}
+
+/* Called at the start of each object file's symbols.
+   Clear out the mapping of header file numbers to header files.  */
+
+static void
+new_object_header_files ()
+{
+  /* Leave FILENUM of 0 free for builtin types and this file's types.  */
+  n_this_object_header_files = 1;
+  header_file_prev_index = -1;
+}
+
+/* Add header file number I for this object file
+   at the next successive FILENUM.  */
+
+static void
+add_this_object_header_file (i)
+     int i;
+{
+  if (n_this_object_header_files == n_allocated_this_object_header_files)
+    {
+      n_allocated_this_object_header_files *= 2;
+      this_object_header_files
+       = (int *) xrealloc (this_object_header_files,
+                           n_allocated_this_object_header_files * sizeof (int));
+    }
+
+  this_object_header_files[n_this_object_header_files++] = i;
+}
+
+/* Add to this file an "old" header file, one already seen in
+   a previous object file.  NAME is the header file's name.
+   INSTANCE is its instance code, to select among multiple
+   symbol tables for the same header file.  */
+
+static void
+add_old_header_file (name, instance)
+     char *name;
+     int instance;
+{
+  register struct header_file *p = header_files;
+  register int i;
+
+  for (i = 0; i < n_header_files; i++)
+    if (!strcmp (p[i].name, name) && instance == p[i].instance)
+      {
+       add_this_object_header_file (i);
+       return;
+      }
+  error ("Invalid symbol data: \"repeated\" header file that hasn't been seen before, at symtab pos %d.",
+        symnum);
+}
+
+/* Add to this file a "new" header file: definitions for its types follow.
+   NAME is the header file's name.
+   Most often this happens only once for each distinct header file,
+   but not necessarily.  If it happens more than once, INSTANCE has
+   a different value each time, and references to the header file
+   use INSTANCE values to select among them.
+
+   dbx output contains "begin" and "end" markers for each new header file,
+   but at this level we just need to know which files there have been;
+   so we record the file when its "begin" is seen and ignore the "end".  */
+
+static void
+add_new_header_file (name, instance)
+     char *name;
+     int instance;
+{
+  register int i;
+  register struct header_file *p = header_files;
+  header_file_prev_index = -1;
+
+#if 0
+  /* This code was used before I knew about the instance codes.
+     My first hypothesis is that it is not necessary now
+     that instance codes are handled.  */
+
+  /* Has this header file a previous definition?
+     If so, make a new entry anyway so that this use in this source file
+     gets a separate entry.  Later source files get the old entry.
+     Record here the index of the old entry, so that any type indices
+     not previously defined can get defined in the old entry as
+     well as in the new one.  */
+
+  for (i = 0; i < n_header_files; i++)
+    if (!strcmp (p[i].name, name))
+      {
+       header_file_prev_index = i;
+      }
+
+#endif
+
+  /* Make sure there is room for one more header file.  */
+
+  if (n_header_files == n_allocated_header_files)
+    {
+      n_allocated_header_files *= 2;
+      header_files = (struct header_file *)
+       xrealloc (header_files,
+                 (n_allocated_header_files
+                  * sizeof (struct header_file)));
+    }
+
+  /* Create an entry for this header file.  */
+
+  i = n_header_files++;
+  header_files[i].name = savestring (name, strlen(name));
+  header_files[i].instance = instance;
+  header_files[i].length = 10;
+  header_files[i].vector
+    = (struct type **) xxmalloc (10 * sizeof (struct type *));
+  bzero (header_files[i].vector, 10 * sizeof (struct type *));
+
+  add_this_object_header_file (i);
+}
+
+/* Look up a dbx type-number pair.  Return the address of the slot
+   where the type for that number-pair is stored.
+   The number-pair is in TYPENUMS.
+
+   This can be used for finding the type associated with that pair
+   or for associating a new type with the pair.  */
+
+static struct type **
+dbx_lookup_type (typenums)
+     int typenums[2];
+{
+  register int filenum = typenums[0], index = typenums[1];
+
+  if (filenum < 0 || filenum >= n_this_object_header_files)
+    error ("Invalid symbol data: type number (%d,%d) out of range at symtab pos %d.",
+          filenum, index, symnum);
+
+  if (filenum == 0)
+    {
+      /* Type is defined outside of header files.
+        Find it in this object file's type vector.  */
+      if (index >= type_vector_length)
+       {
+         type_vector_length *= 2;
+         type_vector = (struct typevector *)
+           xrealloc (type_vector,
+                     (sizeof (struct typevector)
+                      + type_vector_length * sizeof (struct type *)));
+         bzero (&type_vector->type[type_vector_length / 2],
+                type_vector_length * sizeof (struct type *) / 2);
+       }
+      return &type_vector->type[index];
+    }
+  else
+    {
+      register int real_filenum = this_object_header_files[filenum];
+      register struct header_file *f;
+
+      if (real_filenum >= n_header_files)
+       abort ();
+
+      f = &header_files[real_filenum];
+
+      if (index >= f->length)
+       {
+         f->length *= 2;
+         f->vector = (struct type **)
+           xrealloc (f->vector, f->length * sizeof (struct type *));
+         bzero (&f->vector[f->length / 2],
+                f->length * sizeof (struct type *) / 2);
+       }
+      return &f->vector[index];
+    }
+}
+
+/* Create a type object.  Occaisionally used when you need a type
+   which isn't going to be given a type number.  */
+
+static struct type *
+dbx_create_type ()
+{
+  register struct type *type =
+    (struct type *) obstack_alloc (symbol_obstack, sizeof (struct type));
+
+  bzero (type, sizeof (struct type));
+  TYPE_VPTR_FIELDNO (type) = -1;
+  return type;
+}
+
+/* Make sure there is a type allocated for type numbers TYPENUMS
+   and return the type object.
+   This can create an empty (zeroed) type object.
+   TYPENUMS may be (-1, -1) to return a new type object that is not
+   put into the type vector, and so may not be referred to by number. */
+
+static struct type *
+dbx_alloc_type (typenums)
+     int typenums[2];
+{
+  register struct type **type_addr;
+  register struct type *type;
+
+  if (typenums[1] != -1)
+    {
+      type_addr = dbx_lookup_type (typenums);
+      type = *type_addr;
+    }
+  else
+    {
+      type_addr = 0;
+      type = 0;
+    }
+
+  /* If we are referring to a type not known at all yet,
+     allocate an empty type for it.
+     We will fill it in later if we find out how.  */
+  if (type == 0)
+    {
+      type = dbx_create_type ();
+      if (type_addr)
+       *type_addr = type;
+    }
+  
+  return type;
+}
+
+#if 0
+static struct type **
+explicit_lookup_type (real_filenum, index)
+     int real_filenum, index;
+{
+  register struct header_file *f = &header_files[real_filenum];
+
+  if (index >= f->length)
+    {
+      f->length *= 2;
+      f->vector = (struct type **)
+       xrealloc (f->vector, f->length * sizeof (struct type *));
+      bzero (&f->vector[f->length / 2],
+            f->length * sizeof (struct type *) / 2);
+    }
+  return &f->vector[index];
+}
+#endif
+\f
+/* maintain the lists of symbols and blocks */
+
+/* Add a symbol to one of the lists of symbols.  */
+static void
+add_symbol_to_list (symbol, listhead)
+     struct symbol *symbol;
+     struct pending **listhead;
+{
+  /* We keep PENDINGSIZE symbols in each link of the list.
+     If we don't have a link with room in it, add a new link.  */
+  if (*listhead == 0 || (*listhead)->nsyms == PENDINGSIZE)
+    {
+      register struct pending *link;
+      if (free_pendings)
+       {
+         link = free_pendings;
+         free_pendings = link->next;
+       }
+      else
+       link = (struct pending *) xxmalloc (sizeof (struct pending));
+
+      link->next = *listhead;
+      *listhead = link;
+      link->nsyms = 0;
+    }
+
+  (*listhead)->symbol[(*listhead)->nsyms++] = symbol;
+}
+
+/* At end of reading syms, or in case of quit,
+   really free as many `struct pending's as we can easily find.  */
+
+static void
+really_free_pendings ()
+{
+  struct pending *next, *next1;
+  struct pending_block *bnext, *bnext1;
+
+  for (next = free_pendings; next; next = next1)
+    {
+      next1 = next->next;
+      free (next);
+    }
+  free_pendings = 0;
+
+  for (bnext = pending_blocks; bnext; bnext = bnext1)
+    {
+      bnext1 = bnext->next;
+      free (bnext);
+    }
+  pending_blocks = 0;
+
+  for (next = file_symbols; next; next = next1)
+    {
+      next1 = next->next;
+      free (next);
+    }
+  for (next = global_symbols; next; next = next1)
+    {
+      next1 = next->next;
+      free (next);
+    }
+}
+
+/* Take one of the lists of symbols and make a block from it.
+   Keep the order the symbols have in the list (reversed from the input file).
+   Put the block on the list of pending blocks.  */
+
+static void
+finish_block (symbol, listhead, old_blocks, start, end)
+     struct symbol *symbol;
+     struct pending **listhead;
+     struct pending_block *old_blocks;
+     CORE_ADDR start, end;
+{
+  register struct pending *next, *next1;
+  register struct block *block;
+  register struct pending_block *pblock;
+  struct pending_block *opblock;
+  register int i;
+
+  /* Count the length of the list of symbols.  */
+
+  for (next = *listhead, i = 0; next; i += next->nsyms, next = next->next);
+
+  block = (struct block *) obstack_alloc (symbol_obstack,
+                                         (sizeof (struct block)
+                                          + ((i - 1)
+                                             * sizeof (struct symbol *))));
+
+  /* Copy the symbols into the block.  */
+
+  BLOCK_NSYMS (block) = i;
+  for (next = *listhead; next; next = next->next)
+    {
+      register int j;
+      for (j = next->nsyms - 1; j >= 0; j--)
+       BLOCK_SYM (block, --i) = next->symbol[j];
+    }
+
+  BLOCK_START (block) = start;
+  BLOCK_END (block) = end;
+  BLOCK_SUPERBLOCK (block) = 0;        /* Filled in when containing block is made */
+  BLOCK_GCC_COMPILED (block) = processing_gcc_compilation;
+
+  /* Put the block in as the value of the symbol that names it.  */
+
+  if (symbol)
+    {
+      SYMBOL_BLOCK_VALUE (symbol) = block;
+      BLOCK_FUNCTION (block) = symbol;
+    }
+  else
+    BLOCK_FUNCTION (block) = 0;
+
+  /* Now "free" the links of the list, and empty the list.  */
+
+  for (next = *listhead; next; next = next1)
+    {
+      next1 = next->next;
+      next->next = free_pendings;
+      free_pendings = next;
+    }
+  *listhead = 0;
+
+  /* Install this block as the superblock
+     of all blocks made since the start of this scope
+     that don't have superblocks yet.  */
+
+  opblock = 0;
+  for (pblock = pending_blocks; pblock != old_blocks; pblock = pblock->next)
+    {
+      if (BLOCK_SUPERBLOCK (pblock->block) == 0)
+       BLOCK_SUPERBLOCK (pblock->block) = block;
+      opblock = pblock;
+    }
+
+  /* Record this block on the list of all blocks in the file.
+     Put it after opblock, or at the beginning if opblock is 0.
+     This puts the block in the list after all its subblocks.  */
+
+  /* Allocate in the symbol_obstack to save time.
+     It wastes a little space.  */
+  pblock = (struct pending_block *)
+    obstack_alloc (symbol_obstack,
+                  sizeof (struct pending_block));
+  pblock->block = block;
+  if (opblock)
+    {
+      pblock->next = opblock->next;
+      opblock->next = pblock;
+    }
+  else
+    {
+      pblock->next = pending_blocks;
+      pending_blocks = pblock;
+    }
+}
+
+static struct blockvector *
+make_blockvector ()
+{
+  register struct pending_block *next, *next1;
+  register struct blockvector *blockvector;
+  register int i;
+
+  /* Count the length of the list of blocks.  */
+
+  for (next = pending_blocks, i = 0; next; next = next->next, i++);
+
+  blockvector = (struct blockvector *)
+    obstack_alloc (symbol_obstack,
+                  (sizeof (struct blockvector)
+                   + (i - 1) * sizeof (struct block *)));
+
+  /* Copy the blocks into the blockvector.
+     This is done in reverse order, which happens to put
+     the blocks into the proper order (ascending starting address).
+     finish_block has hair to insert each block into the list
+     after its subblocks in order to make sure this is true.  */
+
+  BLOCKVECTOR_NBLOCKS (blockvector) = i;
+  for (next = pending_blocks; next; next = next->next)
+    BLOCKVECTOR_BLOCK (blockvector, --i) = next->block;
+
+#if 0 /* Now we make the links in the obstack, so don't free them.  */
+  /* Now free the links of the list, and empty the list.  */
+
+  for (next = pending_blocks; next; next = next1)
+    {
+      next1 = next->next;
+      free (next);
+    }
+#endif
+  pending_blocks = 0;
+
+  return blockvector;
+}
+\f
+/* Manage the vector of line numbers.  */
+
+static void
+record_line (line, pc)
+     int line;
+     CORE_ADDR pc;
+{
+  struct linetable_entry *e;
+  /* Ignore the dummy line number in libg.o */
+
+  if (line == 0xffff)
+    return;
+
+  /* Make sure line vector is big enough.  */
+
+  if (line_vector_index + 1 >= line_vector_length)
+    {
+      line_vector_length *= 2;
+      line_vector = (struct linetable *)
+       xrealloc (line_vector,
+                 (sizeof (struct linetable)
+                  + line_vector_length * sizeof (struct linetable_entry)));
+      current_subfile->line_vector = line_vector;
+    }
+
+  e = line_vector->item + line_vector_index++;
+  e->line = line; e->pc = pc;
+}
+\f
+/* Start a new symtab for a new source file.
+   This is called when a dbx symbol of type N_SO is seen;
+   it indicates the start of data for one original source file.  */
+
+static void
+start_symtab (name, start_addr)
+     char *name;
+     CORE_ADDR start_addr;
+{
+  register struct symtab *s;
+
+  last_source_file = name;
+  last_source_start_addr = start_addr;
+  file_symbols = 0;
+  global_symbols = 0;
+  within_function = 0;
+
+  /* Context stack is initially empty, with room for 10 levels.  */
+  context_stack
+    = (struct context_stack *) xxmalloc (10 * sizeof (struct context_stack));
+  context_stack_size = 10;
+  context_stack_depth = 0;
+
+  new_object_header_files ();
+
+  for (s = symseg_chain; s; s = s->next)
+    if (s->ldsymoff == symnum * sizeof (struct nlist))
+      break;
+  current_symseg = s;
+  if (s != 0)
+    return;
+
+  type_vector_length = 160;
+  type_vector = (struct typevector *)
+    xxmalloc (sizeof (struct typevector)
+             + type_vector_length * sizeof (struct type *));
+  bzero (type_vector->type, type_vector_length * sizeof (struct type *));
+
+  /* Initialize the list of sub source files with one entry
+     for this file (the top-level source file).  */
+
+  subfiles = 0;
+  current_subfile = 0;
+  start_subfile (name);
+
+#if 0 /* This is now set at the beginning of read_ofile_symtab */
+  /* Set default for compiler to pcc; assume that we aren't processing
+     a gcc compiled file until proved otherwise.  */
+
+  processing_gcc_compilation = 0;
+#endif
+}
+
+/* Handle an N_SOL symbol, which indicates the start of
+   code that came from an included (or otherwise merged-in)
+   source file with a different name.  */
+
+static void
+start_subfile (name)
+     char *name;
+{
+  register struct subfile *subfile;
+
+  /* Save the current subfile's line vector data.  */
+
+  if (current_subfile)
+    {
+      current_subfile->line_vector_index = line_vector_index;
+      current_subfile->line_vector_length = line_vector_length;
+      current_subfile->prev_line_number = prev_line_number;
+    }
+
+  /* See if this subfile is already known as a subfile of the
+     current main source file.  */
+
+  for (subfile = subfiles; subfile; subfile = subfile->next)
+    {
+      if (!strcmp (subfile->name, name))
+       {
+         line_vector = subfile->line_vector;
+         line_vector_index = subfile->line_vector_index;
+         line_vector_length = subfile->line_vector_length;
+         prev_line_number = subfile->prev_line_number;
+         current_subfile = subfile;
+         return;
+       }
+    }
+
+  /* This subfile is not known.  Add an entry for it.  */
+
+  line_vector_index = 0;
+  line_vector_length = 1000;
+  prev_line_number = -2;       /* Force first line number to be explicit */
+  line_vector = (struct linetable *)
+    xxmalloc (sizeof (struct linetable)
+             + line_vector_length * sizeof (struct linetable_entry));
+
+  /* Make an entry for this subfile in the list of all subfiles
+     of the current main source file.  */
+
+  subfile = (struct subfile *) xxmalloc (sizeof (struct subfile));
+  subfile->next = subfiles;
+  subfile->name = savestring (name, strlen (name));
+  subfile->line_vector = line_vector;
+  subfiles = subfile;
+  current_subfile = subfile;
+}
+
+/* Finish the symbol definitions for one main source file,
+   close off all the lexical contexts for that file
+   (creating struct block's for them), then make the struct symtab
+   for that file and put it in the list of all such.
+
+   END_ADDR is the address of the end of the file's text.  */
+
+static void
+end_symtab (end_addr)
+     CORE_ADDR end_addr;
+{
+  register struct symtab *symtab;
+  register struct blockvector *blockvector;
+  register struct subfile *subfile;
+  register struct linetable *lv;
+  struct subfile *nextsub;
+
+  if (current_symseg != 0)
+    {
+      last_source_file = 0;
+      current_symseg = 0;
+      return;
+    }
+
+  /* Finish the lexical context of the last function in the file;
+     pop the context stack.  */
+
+  if (context_stack_depth > 0)
+    {
+      register struct context_stack *cstk;
+      context_stack_depth--;
+      cstk = &context_stack[context_stack_depth];
+      /* Make a block for the local symbols within.  */
+      finish_block (cstk->name, &local_symbols, cstk->old_blocks,
+                   cstk->start_addr, end_addr);
+    }
+
+  /* Cleanup any undefined types that have been left hanging around
+     (this needs to be done before the finish_blocks so that
+     file_symbols is still good).  */
+  cleanup_undefined_types ();
+
+  /* Finish defining all the blocks of this symtab.  */
+  finish_block (0, &file_symbols, 0, last_source_start_addr, end_addr);
+  finish_block (0, &global_symbols, 0, last_source_start_addr, end_addr);
+  blockvector = make_blockvector ();
+
+  current_subfile->line_vector_index = line_vector_index;
+
+  /* Now create the symtab objects proper, one for each subfile.  */
+  /* (The main file is one of them.)  */
+
+  for (subfile = subfiles; subfile; subfile = nextsub)
+    {
+      symtab = (struct symtab *) xxmalloc (sizeof (struct symtab));
+      symtab->free_ptr = 0;
+
+      /* Fill in its components.  */
+      symtab->blockvector = blockvector;
+      type_vector->length = type_vector_length;
+      symtab->typevector = type_vector;
+      symtab->free_code = free_linetable;
+      if (subfile->next == 0)
+       symtab->free_ptr = (char *) type_vector;
+
+      symtab->filename = subfile->name;
+      lv = subfile->line_vector;
+      lv->nitems = subfile->line_vector_index;
+      symtab->linetable = (struct linetable *)
+       xrealloc (lv, (sizeof (struct linetable)
+                      + lv->nitems * sizeof (struct linetable_entry)));
+      symtab->nlines = 0;
+      symtab->line_charpos = 0;
+
+      /* Link the new symtab into the list of such.  */
+      symtab->next = symtab_list;
+      symtab_list = symtab;
+
+      nextsub = subfile->next;
+      free (subfile);
+    }
+
+  type_vector = 0;
+  type_vector_length = -1;
+  line_vector = 0;
+  line_vector_length = -1;
+  last_source_file = 0;
+}
+\f
+#ifdef N_BINCL
+
+/* Handle the N_BINCL and N_EINCL symbol types
+   that act like N_SOL for switching source files
+   (different subfiles, as we call them) within one object file,
+   but using a stack rather than in an arbitrary order.  */
+
+struct subfile_stack
+{
+  struct subfile_stack *next;
+  char *name;
+  int prev_index;
+};
+
+struct subfile_stack *subfile_stack;
+
+static void
+push_subfile ()
+{
+  register struct subfile_stack *tem
+    = (struct subfile_stack *) xxmalloc (sizeof (struct subfile_stack));
+
+  tem->next = subfile_stack;
+  subfile_stack = tem;
+  if (current_subfile == 0 || current_subfile->name == 0)
+    abort ();
+  tem->name = current_subfile->name;
+  tem->prev_index = header_file_prev_index;
+}
+
+static char *
+pop_subfile ()
+{
+  register char *name;
+  register struct subfile_stack *link = subfile_stack;
+
+  if (link == 0)
+    abort ();
+
+  name = link->name;
+  subfile_stack = link->next;
+  header_file_prev_index = link->prev_index;
+  free (link);
+
+  return name;
+}
+#endif /* Have N_BINCL */
+\f
+/* Accumulate the misc functions in bunches of 127.
+   At the end, copy them all into one newly allocated structure.  */
+
+#define MISC_BUNCH_SIZE 127
+
+struct misc_bunch
+{
+  struct misc_bunch *next;
+  struct misc_function contents[MISC_BUNCH_SIZE];
+};
+
+/* Bunch currently being filled up.
+   The next field points to chain of filled bunches.  */
+
+static struct misc_bunch *misc_bunch;
+
+/* Number of slots filled in current bunch.  */
+
+static int misc_bunch_index;
+
+/* Total number of misc functions recorded so far.  */
+
+static int misc_count;
+
+static void
+init_misc_functions ()
+{
+  misc_count = 0;
+  misc_bunch = 0;
+  misc_bunch_index = MISC_BUNCH_SIZE;
+}
+
+static void
+record_misc_function (name, address, type)
+     char *name;
+     CORE_ADDR address;
+     int type;
+{
+  register struct misc_bunch *new;
+  register unsigned char mtype;
+
+  if (misc_bunch_index == MISC_BUNCH_SIZE)
+    {
+      new = (struct misc_bunch *) xxmalloc (sizeof (struct misc_bunch));
+      misc_bunch_index = 0;
+      new->next = misc_bunch;
+      misc_bunch = new;
+    }
+  misc_bunch->contents[misc_bunch_index].name = name;
+  misc_bunch->contents[misc_bunch_index].address = address;
+  switch (type &~ N_EXT)
+    {
+    case N_TEXT:  mtype = mf_text; break;
+    case N_DATA:  mtype = mf_data; break;
+    case N_BSS:   mtype = mf_bss;  break;
+    case N_ABS:   mtype = mf_abs;  break;
+#ifdef N_SETV
+    case N_SETV:  mtype = mf_data; break;
+#endif
+    default:      mtype = mf_unknown; break;
+    }
+  misc_bunch->contents[misc_bunch_index].type = mtype;
+  misc_bunch_index++;
+  misc_count++;
+}
+
+static int
+compare_misc_functions (fn1, fn2)
+     struct misc_function *fn1, *fn2;
+{
+  /* Return a signed result based on unsigned comparisons
+     so that we sort into unsigned numeric order.  */
+  if (fn1->address < fn2->address)
+    return -1;
+  if (fn1->address > fn2->address)
+    return 1;
+  return 0;
+}
+
+static void
+discard_misc_bunches ()
+{
+  register struct misc_bunch *next;
+
+  while (misc_bunch)
+    {
+      next = misc_bunch->next;
+      free (misc_bunch);
+      misc_bunch = next;
+    }
+}
+
+/* INCLINK nonzero means bunches are from an incrementally-linked file.
+   Add them to the existing bunches.
+   Otherwise INCLINK is zero, and we start from scratch. */
+static void
+condense_misc_bunches (inclink)
+     int inclink;
+{
+  register int i, j;
+  register struct misc_bunch *bunch;
+#ifdef NAMES_HAVE_UNDERSCORE
+  int offset = 1;
+#else
+  int offset = 0;
+#endif
+
+  if (inclink)
+    {
+      misc_function_vector
+       = (struct misc_function *)
+         xrealloc (misc_function_vector, (misc_count + misc_function_count)
+                   * sizeof (struct misc_function));
+      j = misc_function_count;
+    }
+  else
+    {
+      misc_function_vector
+       = (struct misc_function *)
+         xxmalloc (misc_count * sizeof (struct misc_function));
+      j = 0;
+    }
+
+  bunch = misc_bunch;
+  while (bunch)
+    {
+      for (i = 0; i < misc_bunch_index; i++)
+       {
+         misc_function_vector[j] = bunch->contents[i];
+         misc_function_vector[j].name
+           = obconcat (misc_function_vector[j].name
+                       + (misc_function_vector[j].name[0] == '_' ? offset : 0),
+                       "", "");
+         j++;
+       }
+      bunch = bunch->next;
+      misc_bunch_index = MISC_BUNCH_SIZE;
+    }
+
+  if (inclink)
+    misc_function_count += misc_count;
+  else
+    misc_function_count = j;
+
+  /* Sort the misc functions by address.  */
+
+  qsort (misc_function_vector, misc_function_count,
+        sizeof (struct misc_function),
+        compare_misc_functions);
+
+  /* (re)build the hash table (positions changed during the sort) */
+
+  for (i = 0; i < MISC_FUNC_HASH_SIZE; ++i)
+    misc_function_hash_tab[i] = -1;
+  for (i = 0; i < misc_function_count; ++i)
+    {
+      j = hash_symbol(misc_function_vector[i].name) & (MISC_FUNC_HASH_SIZE - 1);
+      misc_function_vector[i].next = misc_function_hash_tab[j];
+      misc_function_hash_tab[j] = i;
+    }
+}
+\f
+/* Call sort_syms to sort alphabetically
+   the symbols of each block of each symtab.  */
+
+static int
+compare_symbols (s1, s2)
+     struct symbol **s1, **s2;
+{
+  register int namediff;
+
+  /* Compare the initial characters.  */
+  namediff = SYMBOL_NAME (*s1)[0] - SYMBOL_NAME (*s2)[0];
+  if (namediff != 0) return namediff;
+
+  /* If they match, compare the rest of the names.  */
+  namediff = strcmp (SYMBOL_NAME (*s1), SYMBOL_NAME (*s2));
+  if (namediff != 0) return namediff;
+
+  /* For symbols of the same name, registers should come first.  */
+  return ((SYMBOL_CLASS (*s2) == LOC_REGISTER)
+         - (SYMBOL_CLASS (*s1) == LOC_REGISTER));
+}
+
+static void sort_symtab_syms ();
+
+static void
+sort_syms ()
+{
+  register struct symtab *s;
+
+  for (s = symtab_list; s; s = s->next)
+    sort_symtab_syms (s);
+}
+
+static void
+sort_symtab_syms (s)
+     register struct symtab *s;
+{
+  register struct blockvector *bv = BLOCKVECTOR (s);
+  int nbl = BLOCKVECTOR_NBLOCKS (bv);
+  int i;
+  register struct block *b;
+
+  /* Note that in the following sort, we always make sure that
+     register debug symbol declarations always come before regular
+     debug symbol declarations (as might happen when parameters are
+     then put into registers by the compiler).  We do this by a
+     correct compare in compare_symbols, and by the reversal of the
+     symbols if we don't sort.  This works as long as a register debug
+     symbol always comes after a parameter debug symbol. */
+
+  /* This is no longer necessary; lookup_block_symbol now always
+     prefers some other declaration over a parameter declaration.  We
+     still sort the thing (that is necessary), but we don't reverse it
+     if we shouldn't sort it.  */
+
+  for (i = 0; i < nbl; i++)
+    {
+      b = BLOCKVECTOR_BLOCK (bv, i);
+      if (BLOCK_SHOULD_SORT (b))
+       qsort (&BLOCK_SYM (b, 0), BLOCK_NSYMS (b),
+              sizeof (struct symbol *), compare_symbols);
+    }
+}
+
+\f
+extern struct symtab *psymtab_to_symtab ();
+
+/* The entry point.  */
+static CORE_ADDR entry_point;
+
+static char *symfile_string_table;
+static int symfile_string_table_size;
+
+/* This is the symbol-file command.  Read the file, analyze its symbols,
+   and add a struct symtab to symtab_list.  */
+
+void
+symbol_file_command (name, from_tty)
+     char *name;
+     int from_tty;
+{
+  register int desc;
+  DECLARE_FILE_HEADERS;
+  struct nlist *nlist;
+
+  /* The string table.  */
+  char *stringtab;
+  
+  /* The size of the string table (buffer is a bizarre name...).  */
+  long buffer;
+  
+  register int val;
+  extern void close ();
+  struct cleanup *old_chain;
+  struct symtab *symseg;
+  struct stat statbuf;
+
+  dont_repeat ();
+
+  if (name == 0)
+    {
+      if ((symtab_list || partial_symtab_list)
+         && from_tty
+         && !query ("Discard symbol table? ", 0))
+       error ("Not confirmed.");
+      if (symfile)
+       free (symfile);
+      symfile = 0;
+      free_all_symtabs ();
+      free_all_psymtabs ();
+      return;
+    }
+
+  name = tilde_expand (name);
+  make_cleanup (free, name);
+
+  if ((symtab_list || partial_symtab_list)
+      && !query ("Load new symbol table from \"%s\"? ", name))
+    error ("Not confirmed.");
+
+  {
+    char *absolute_name;
+    desc = openp (getenv ("PATH"), 1, name, O_RDONLY, 0, &absolute_name);
+    if (desc < 0)
+      perror_with_name (name);
+    else
+      name = absolute_name;
+  }
+
+  old_chain = make_cleanup (close, desc);
+  make_cleanup (free_current_contents, &name);
+
+  READ_FILE_HEADERS (desc, name);
+
+  entry_point = ENTRY_POINT;
+
+  if (NUMBER_OF_SYMBOLS == 0)
+    {
+      if (symfile)
+       free (symfile);
+      symfile = 0;
+      free_all_symtabs ();
+      free_all_psymtabs ();
+      printf ("%s has no symbol-table; symbols discarded.\n", name);
+      fflush (stdout);
+      do_cleanups (old_chain);
+      return;
+    }
+
+  printf ("Reading symbol data from %s...", name);
+  fflush (stdout);
+
+  /* Now read the string table, all at once.  */
+  val = lseek (desc, STRING_TABLE_OFFSET, 0);
+  if (val < 0)
+    perror_with_name (name);
+  if (stat (name, &statbuf) == -1)
+    perror_with_name (name);
+  READ_STRING_TABLE_SIZE (buffer);
+  if (buffer >= 0 && buffer < statbuf.st_size)
+    {
+      /* This should speed things up without consuming much
+        extra memory (because probably little of the space is going
+        to be reused anyway, whether in data or stack space).
+
+        A quick test (running GDB on itself and setting 9 breakpoints
+        in different files) showed that memory usage was almost
+        identical for the two cases.  */
+#if 0
+#ifdef BROKEN_LARGE_ALLOCA
+      stringtab = (char *) xmalloc (buffer);
+      make_cleanup (free, stringtab);
+#else
+      stringtab = (char *) alloca (buffer);
+#endif
+#endif
+      stringtab = (char *) xmalloc (buffer);
+      symfile_string_table = stringtab;
+      symfile_string_table_size = buffer;
+    }
+  else
+    stringtab = NULL;
+  if (stringtab == NULL)
+    error ("ridiculous string table size: %d bytes", buffer);
+
+  /* Usually READ_STRING_TABLE_SIZE will have shifted the file pointer.
+     Occaisionally, it won't.  */
+  val = lseek (desc, STRING_TABLE_OFFSET, L_SET);
+  if (val < 0)
+    perror_with_name (name);
+  val = myread (desc, stringtab, buffer);
+  if (val < 0)
+    perror_with_name (name);
+  
+  /* Throw away the old symbol table.  */
+
+  if (symfile)
+    free (symfile);
+  symfile = 0;
+  free_all_symtabs ();
+  free_all_psymtabs ();
+
+  /* Empty the hash table of global syms looking for values.  */
+  bzero (global_sym_chain, sizeof global_sym_chain);
+
+  /* Symsegs are no longer supported by GDB.  Setting symseg_chain to
+     0 is easier than finding all the symseg code and eliminating it.  */
+  symseg_chain = 0;
+
+  /* Position to read the symbol table.  Do not read it all at once. */
+  val = lseek (desc, SYMBOL_TABLE_OFFSET, 0);
+  if (val < 0)
+    perror_with_name (name);
+
+  /* Don't put these on the cleanup chain; they need to stick around
+     until the next call to symbol_file_command.  *Then* we'll free
+     them. */
+  free_header_files ();
+  init_header_files ();
+
+  init_misc_functions ();
+  make_cleanup (discard_misc_bunches, 0);
+
+  free_pendings = 0;
+  pending_blocks = 0;
+  file_symbols = 0;
+  global_symbols = 0;
+  make_cleanup (really_free_pendings, 0);
+
+  /* Now that the symbol table data of the executable file are all in core,
+     process them and define symbols accordingly.  Closes desc.  */
+
+  read_dbx_symtab (desc, stringtab, buffer, NUMBER_OF_SYMBOLS, 0,
+                  ADDR_OF_TEXT_SEGMENT, SIZE_OF_TEXT_SEGMENT);
+
+  /* Go over the misc functions and install them in vector.  */
+
+  condense_misc_bunches (0);
+
+  /* Don't allow char * to have a typename (else would get caddr_t.)  */
+
+  TYPE_NAME (lookup_pointer_type (builtin_type_char)) = 0;
+
+  /* Make a default for file to list.  */
+
+  symfile = savestring (name, strlen (name));
+
+  /* Call to select_source_symtab used to be here; it was using too
+     much time.  I'll make sure that list_sources can handle the lack
+     of current_source_symtab */
+
+  do_cleanups (old_chain);     /* Descriptor closed here */
+
+  /* Free the symtabs made by read_symsegs, but not their contents,
+     which have been copied into symtabs on symtab_list.  */
+  while (symseg_chain)
+    {
+      register struct symtab *s = symseg_chain->next;
+      free (symseg_chain);
+      symseg_chain = s;
+    }
+
+  if (!partial_symtab_list)
+    printf ("\n(no debugging symbols found)...");
+
+  printf ("done.\n");
+  fflush (stdout);
+}
+
+/* Return name of file symbols were loaded from, or 0 if none..  */
+
+char *
+get_sym_file ()
+{
+  return symfile;
+}
+\f
+/* Buffer for reading the symbol table entries.  */
+static struct nlist symbuf[4096];
+static int symbuf_idx;
+static int symbuf_end;
+
+/* I/O descriptor for reading the symbol table.  */
+static int symtab_input_desc;
+
+/* The address of the string table
+   of the object file we are reading (as copied into core).  */
+static char *stringtab_global;
+
+/* Refill the symbol table input buffer
+   and set the variables that control fetching entries from it.
+   Reports an error if no data available.
+   This function can read past the end of the symbol table
+   (into the string table) but this does no harm.  */
+
+static int
+fill_symbuf ()
+{
+  int nbytes = myread (symtab_input_desc, symbuf, sizeof (symbuf));
+  if (nbytes <= 0)
+    error ("error or end of file reading symbol table");
+  symbuf_end = nbytes / sizeof (struct nlist);
+  symbuf_idx = 0;
+  return 1;
+}
+
+/* dbx allows the text of a symbol name to be continued into the
+   next symbol name!  When such a continuation is encountered
+   (a \ at the end of the text of a name)
+   call this function to get the continuation.  */
+
+static char *
+next_symbol_text ()
+{
+  if (symbuf_idx == symbuf_end)
+    fill_symbuf ();
+  symnum++;
+  return symbuf[symbuf_idx++].n_un.n_strx + stringtab_global;
+}
+\f
+/*
+ * Initializes storage for all of the partial symbols that will be
+ * created by read_dbx_symtab and subsidiaries.
+ */
+void
+init_psymbol_list (total_symbols)
+     int total_symbols;
+{
+  /* Current best guess is that there are approximately a twentieth
+     of the total symbols (in a debugging file) are global or static
+     oriented symbols */
+  global_psymbols.size = total_symbols / 10;
+  static_psymbols.size = total_symbols / 10;
+  global_psymbols.next = global_psymbols.list = (struct partial_symbol *)
+    xmalloc (global_psymbols.size * sizeof (struct partial_symbol));
+  static_psymbols.next = static_psymbols.list = (struct partial_symbol *)
+    xmalloc (static_psymbols.size * sizeof (struct partial_symbol));
+}
+
+/*
+ * Initialize the list of bincls to contain none and have some
+ * allocated.
+ */
+static void
+init_bincl_list (number)
+     int number;
+{
+  bincls_allocated = number;
+  next_bincl = bincl_list = (struct header_file_location *)
+      xmalloc (bincls_allocated * sizeof(struct header_file_location));
+}
+
+/*
+ * Add a bincl to the list.
+ */
+static void
+add_bincl_to_list (pst, name, instance)
+     struct partial_symtab *pst;
+     char *name;
+     int instance;
+{
+  if (next_bincl >= bincl_list + bincls_allocated)
+    {
+      int offset = next_bincl - bincl_list;
+      bincls_allocated *= 2;
+      bincl_list = (struct header_file_location *)
+       xrealloc (bincl_list,
+                 bincls_allocated * sizeof (struct header_file_location));
+      next_bincl = bincl_list + offset;
+    }
+  next_bincl->pst = pst;
+  next_bincl->instance = instance;
+  next_bincl++->name = name;
+}
+
+/*
+ * Given a name, value pair, find the corresponding
+ * bincl in the list.  Return the partial symtab associated
+ * with that header_file_location.
+ */
+struct partial_symtab *
+find_corresponding_bincl_psymtab (name, instance)
+     char *name;
+     int instance;
+{
+  struct header_file_location *bincl;
+
+  for (bincl = bincl_list; bincl < next_bincl; bincl++)
+    if (bincl->instance == instance
+       && !strcmp (name, bincl->name))
+      return bincl->pst;
+
+  return (struct partial_symtab *) 0;
+}
+
+/*
+ * Free the storage allocated for the bincl list.
+ */
+static void
+free_bincl_list ()
+{
+  free (bincl_list);
+  bincls_allocated = 0;
+}
+
+static struct partial_symtab *start_psymtab ();
+static void add_psymtab_dependency ();
+static void end_psymtab();
+
+static int
+compare_psymbols (s1, s2)
+     register struct partial_symbol *s1, *s2;
+{
+  register char
+    *st1 = SYMBOL_NAME (s1),
+    *st2 = SYMBOL_NAME (s2);
+  register int i;
+
+  if (st1[0] - st2[0])
+    return (st1[0] - st2[0]);
+  if (st1[1] - st2[1])
+    return (st1[1] - st2[1]);
+  if (i = strcmp(st1, st2))
+    return (i);
+  /* Next comparison implements policy that used to be in lookup_symbol:
+   * it would search psymtabs in psymtab_list order (reverse order of
+   * declaration) & take first occurance of symbol it found.  So, we
+   * collate duplicate names in reverse psymtab order. */
+  return (s2->pst - s1->pst);
+}
+
+/* Given pointers to an a.out symbol table in core containing dbx
+   style data, setup partial_symtab's describing each source file for
+   which debugging information is available.  NLISTLEN is the number
+   of symbols in the symbol table.  All symbol names are given as
+   offsets relative to STRINGTAB.  STRINGTAB_SIZE is the size of
+   STRINGTAB.
+
+   I have no idea whether or not this routine should be setup to deal
+   with inclinks.  It seems reasonable to me that they be dealt with
+   standardly, so I am not going to make a strong effort to deal with
+   them here.
+   */
+
+static void
+read_dbx_symtab (desc, stringtab, stringtab_size, nlistlen, inclink,
+                text_addr, text_size)
+     int desc;
+     register char *stringtab;
+     register long stringtab_size;
+     register int nlistlen;
+     int inclink;
+     unsigned text_addr;
+     int text_size;
+{
+  register struct nlist *bufp;
+  register char *namestring;
+  register struct partial_symbol *psym;
+  register struct psymbol_allocation_list *psymbol_struct;
+
+  int nsl;
+  int past_first_source_file = 0;
+  CORE_ADDR last_o_file_start = 0;
+  char *last_o_file_name = "*bogus*";
+  struct cleanup *old_chain;
+  char *p;
+  enum namespace ns;
+  enum address_class class;
+
+#ifdef PROFILE_TYPES
+  int i;
+  int profile_types [256];
+  int strcmp_called = 0;
+  int autovars = 0;
+  int global_funs = 0;
+#endif
+
+  /* Current partial symtab */
+  struct partial_symtab *pst;
+
+  /* List of current psymtab's include files */
+  char **psymtab_include_list;
+  int includes_allocated;
+  int includes_used;
+
+  /* Index within current psymtab dependency list */
+  struct partial_symtab **dependency_list;
+  int dependencies_used, dependencies_allocated;
+
+#ifdef PROFILE_TYPES
+  for (i = 0; i < 256; i++)
+    profile_types[i] = 0;
+#endif
+
+  stringtab_global = stringtab;
+  
+  pst = (struct partial_symtab *) 0;
+
+  includes_allocated = 30;
+  includes_used = 0;
+  psymtab_include_list = (char **) alloca (includes_allocated *
+                                          sizeof (char *));
+
+  dependencies_allocated = 30;
+  dependencies_used = 0;
+  dependency_list =
+    (struct partial_symtab **) alloca (dependencies_allocated *
+                                      sizeof (struct partial_symtab *));
+
+  old_chain = make_cleanup (free_all_psymtabs, 0);
+
+  /* Init bincl list */
+  init_bincl_list (20);
+  make_cleanup (free_bincl_list, 0);
+
+  /* Setup global partial symbol list */
+  init_psymbol_list (nlistlen);
+
+  last_source_file = 0;
+
+#ifdef END_OF_TEXT_DEFAULT
+  end_of_text_addr = END_OF_TEXT_DEFAULT;
+#else
+  end_of_text_addr = text_addr + text_size;
+#endif
+
+  symtab_input_desc = desc;    /* This is needed for fill_symbuf below */
+  symbuf_end = symbuf_idx = 0;
+
+  for (symnum = 0; symnum < nlistlen; symnum++)
+    {
+      /* Get the symbol for this run and pull out some info */
+      QUIT;    /* allow this to be interruptable */
+      if (symbuf_idx == symbuf_end)
+       fill_symbuf ();
+      bufp = &symbuf[symbuf_idx++];
+
+#ifdef PROFILE_TYPES
+      profile_types[bufp->n_type]++;
+#endif
+
+      /*
+       * Special case to speed up readin.
+       */
+      if (bufp->n_type == N_SLINE) continue;
+
+      /* Ok.  There is a lot of code duplicated in the rest of this
+         switch statiement (for efficiency reasons).  Since I don't
+         like duplicating code, I will do my penance here, and
+         describe the code which is duplicated:
+
+        *) The assignment to namestring.
+        *) The call to index.
+        *) The addition of a partial symbol the the two partial
+           symbol lists.  This last is a large section of code, so
+           I've imbedded it in the following macro.
+        */
+      
+/* Set namestring based on bufp.  */
+#define SET_NAMESTRING()\
+  if (bufp->n_un.n_strx < 0 || bufp->n_un.n_strx >= stringtab_size)  \
+    error ("Invalid symbol data: bad string table offset: %d",       \
+          bufp->n_un.n_strx);                                       \
+  namestring = bufp->n_un.n_strx + stringtab
+
+#define        ADD_PSYMBOL_TO_LIST(NAME, NAMELENGTH, NAMESPACE, CLASS, LIST, VALUE)\
+  do {                                                                 \
+    if ((LIST).next >=                                                 \
+       (LIST).list + (LIST).size)                                      \
+      {                                                                        \
+       (LIST).list = (struct partial_symbol *)                         \
+         xrealloc ((LIST).list,                                        \
+                   ((LIST).size * 2                                    \
+                    * sizeof (struct partial_symbol)));                \
+       /* Next assumes we only went one over.  Should be good if       \
+          program works correctly */                                   \
+       (LIST).next =                                                   \
+         (LIST).list + (LIST).size;                                    \
+       (LIST).size *= 2;                                               \
+      }                                                                        \
+    psym = (LIST).next++;                                              \
+                                                                       \
+    SYMBOL_NAME (psym) = (char *) obstack_alloc (psymbol_obstack,      \
+                                                (NAMELENGTH) + 1);     \
+    strncpy (SYMBOL_NAME (psym), (NAME), (NAMELENGTH));                        \
+    SYMBOL_NAME (psym)[(NAMELENGTH)] = '\0';                           \
+    SYMBOL_NAMESPACE (psym) = (NAMESPACE);                             \
+    SYMBOL_CLASS (psym) = (CLASS);                                     \
+    SYMBOL_VALUE (psym) = (VALUE);                                     \
+  } while (0);
+
+
+      switch (bufp->n_type)
+       {
+         /*
+          * Standard, non-debugger, symbols
+          */
+
+       case N_TEXT | N_EXT:
+         /* Catch etext */
+
+         SET_NAMESTRING();
+
+         if (namestring[6] == '\0' && namestring[5] == 't'
+             && namestring[4] == 'x' && namestring[3] == 'e'
+             && namestring[2] == 't' && namestring[1] == 'e'
+             && namestring[0] == '_')
+           end_of_text_addr = bufp->n_value;
+
+         /* Figure out beginning and end of global linker symbol
+            section and put non-debugger specified symbols on
+            tmp_symchain */
+
+         last_global_sym = symnum;
+         if (!first_global_sym) first_global_sym = symnum;
+
+         record_misc_function (namestring, bufp->n_value,
+                               bufp->n_type); /* Always */
+
+         continue;
+
+#ifdef N_NBTEXT
+       case N_NBTEXT | N_EXT:
+#endif
+#ifdef N_NBDATA
+       case N_NBDATA | N_EXT:
+#endif
+#ifdef N_NBBSS
+       case N_NBBSS | N_EXT:
+#endif
+#ifdef N_SETV
+       case N_SETV | N_EXT:
+#endif
+       case N_ABS | N_EXT:
+       case N_DATA | N_EXT:
+       case N_BSS | N_EXT:
+         /* Figure out beginning and end of global linker symbol
+            section and put non-debugger specified symbols on
+            tmp_symchain */
+
+         SET_NAMESTRING();
+
+         last_global_sym = symnum;
+         if (!first_global_sym) first_global_sym = symnum;
+
+         /* Not really a function here, but... */
+         record_misc_function (namestring, bufp->n_value,
+                               bufp->n_type); /* Always */
+
+         continue;
+
+#ifdef N_NBTEXT
+       case N_NBTEXT:
+#endif
+
+         /* We need to be able to deal with both N_FN or N_TEXT,
+            because we have no way of knowing whether the sys-supplied ld
+            or GNU ld was used to make the executable.  */
+#if ! (N_FN & N_EXT)
+       case N_FN:
+#endif
+       case N_FN | N_EXT:
+       case N_TEXT:
+         SET_NAMESTRING();
+         if ((namestring[0] == '-' && namestring[1] == 'l')
+             || (namestring [(nsl = strlen (namestring)) - 1] == 'o'
+                 && namestring [nsl - 2] == '.'))
+           {
+             if (entry_point < bufp->n_value
+                 && entry_point >= last_o_file_start)
+               {
+                 startup_file_start = last_o_file_start;
+                 startup_file_end = bufp->n_value;
+               }
+             if (past_first_source_file && pst)
+               {
+                 end_psymtab (pst, psymtab_include_list, includes_used,
+                              symnum * sizeof (struct nlist), bufp->n_value,
+                              dependency_list, dependencies_used,
+                              global_psymbols.next, static_psymbols.next);
+                 pst = (struct partial_symtab *) 0;
+                 includes_used = 0;
+                 dependencies_used = 0;
+               }
+             else
+               past_first_source_file = 1;
+             last_o_file_start = bufp->n_value;
+             last_o_file_name = namestring;
+             nsl = strlen(namestring);
+             if (namestring[nsl-2] == '.' && namestring[nsl-1] == 'o')
+               namestring[nsl-2] = 0;
+           }
+         else if (strcmp(namestring, "gcc_compiled."))
+           {
+             if (*namestring == '_')
+               ++namestring;
+             namestring = obconcat(last_o_file_name, ":", namestring);
+             last_global_sym = symnum;
+             if (!first_global_sym)
+               first_global_sym = symnum;
+             record_misc_function(namestring, bufp->n_value, bufp->n_type);
+           }
+         continue;
+
+       case N_ABS:
+       case N_DATA:
+       case N_BSS:
+           SET_NAMESTRING();
+           if (*namestring == '_')
+             ++namestring;
+           namestring = obconcat(last_o_file_name, ":", namestring);
+           last_global_sym = symnum;
+           if (!first_global_sym)
+             first_global_sym = symnum;
+           record_misc_function(namestring, bufp->n_value, bufp->n_type);
+           continue;
+
+       case N_UNDF:
+       case N_UNDF | N_EXT:
+#ifdef N_NBDATA
+       case N_NBDATA:
+#endif
+#ifdef N_NBBSS
+       case N_NBBSS:
+#endif
+
+         /* Keep going . . .*/
+
+         /*
+          * Special symbol types for GNU
+          */
+#ifdef N_INDR
+       case N_INDR:
+       case N_INDR | N_EXT:
+#endif
+#ifdef N_SETA
+       case N_SETA:
+       case N_SETA | N_EXT:
+       case N_SETT:
+       case N_SETT | N_EXT:
+       case N_SETD:
+       case N_SETD | N_EXT:
+       case N_SETB:
+       case N_SETB | N_EXT:
+       case N_SETV:
+#endif
+         continue;
+
+         /*
+          * Debugger symbols
+          */
+
+       case N_SO:
+         /* End the current partial symtab and start a new one */
+
+         SET_NAMESTRING();
+
+         if (pst && past_first_source_file)
+           {
+             end_psymtab (pst, psymtab_include_list, includes_used,
+                          symnum * sizeof (struct nlist), bufp->n_value,
+                          dependency_list, dependencies_used,
+                          global_psymbols.next, static_psymbols.next);
+             pst = (struct partial_symtab *) 0;
+             includes_used = 0;
+             dependencies_used = 0;
+           }
+         else
+           past_first_source_file = 1;
+
+         pst = start_psymtab (namestring, bufp->n_value,
+                              symnum * sizeof (struct nlist),
+                              global_psymbols.next, static_psymbols.next);
+
+         continue;
+
+#ifdef N_BINCL
+       case N_BINCL:
+         /* Add this bincl to the bincl_list for future EXCLs.  No
+            need to save the string; it'll be around until
+            read_dbx_symtab function return */
+
+         SET_NAMESTRING();
+
+         add_bincl_to_list (pst, namestring, bufp->n_value);
+
+         /* Mark down an include file in the current psymtab */
+
+         psymtab_include_list[includes_used++] = namestring;
+         if (includes_used >= includes_allocated)
+           {
+             char **orig = psymtab_include_list;
+
+             psymtab_include_list = (char **)
+               alloca ((includes_allocated *= 2) *
+                       sizeof (char *));
+             bcopy (orig, psymtab_include_list,
+                    includes_used * sizeof (char *));
+           }
+
+         continue;
+#endif
+
+       case N_SOL:
+         /* Mark down an include file in the current psymtab */
+
+         SET_NAMESTRING();
+
+         /* In C++, one may expect the same filename to come round many
+            times, when code is coming alternately from the main file
+            and from inline functions in other files. So I check to see
+            if this is a file we've seen before.
+
+            This seems to be a lot of time to be spending on N_SOL, but
+            things like "break expread.y:435" need to work (I
+            suppose the psymtab_include_list could be hashed or put
+            in a binary tree, if profiling shows this is a major hog).  */
+         {
+           register int i;
+           for (i = 0; i < includes_used; i++)
+             if (!strcmp (namestring, psymtab_include_list[i]))
+               {
+                 i = -1; 
+                 break;
+               }
+           if (i == -1)
+             continue;
+         }
+
+         psymtab_include_list[includes_used++] = namestring;
+         if (includes_used >= includes_allocated)
+           {
+             char **orig = psymtab_include_list;
+
+             psymtab_include_list = (char **)
+               alloca ((includes_allocated *= 2) *
+                       sizeof (char *));
+             bcopy (orig, psymtab_include_list,
+                    includes_used * sizeof (char *));
+           }
+         continue;
+
+       case N_LSYM:            /* Typedef or automatic variable. */
+         SET_NAMESTRING();
+
+         p = (char *) index (namestring, ':');
+
+         /* Skip if there is no :.  */
+         if (!p) continue;
+
+         switch (p[1])
+           {
+           case 'T':
+             ADD_PSYMBOL_TO_LIST (namestring, p - namestring,
+                                  STRUCT_NAMESPACE, LOC_TYPEDEF,
+                                  static_psymbols, bufp->n_value);
+             goto check_enum;
+           case 't':
+             ADD_PSYMBOL_TO_LIST (namestring, p - namestring,
+                                  VAR_NAMESPACE, LOC_TYPEDEF,
+                                  static_psymbols, bufp->n_value);
+           check_enum:
+             /* If this is an enumerated type, we need to
+                add all the enum constants to the partial symbol
+                table.  This does not cover enums without names, e.g.
+                "enum {a, b} c;" in C, but fortunately those are
+                rare.  There is no way for GDB to find those from the
+                enum type without spending too much time on it.  Thus
+                to solve this problem, the compiler needs to put out separate
+                constant symbols ('c' N_LSYMS) for enum constants in
+                enums without names.  */
+
+             /* We are looking for something of the form
+                <name> ":" ("t" | "T") [<number> "="] "e"
+                {<constant> ":" <value> ","} ";".  */
+
+             /* Skip over the colon and the 't' or 'T'.  */
+             p += 2;
+             /* This type may be given a number.  Skip over it.  */
+             while ((*p >= '0' && *p <= '9')
+                    || *p == '=')
+               p++;
+
+             if (*p++ == 'e')
+               {
+                 /* We have found an enumerated type.  */
+                 /* According to comments in read_enum_type
+                    a comma could end it instead of a semicolon.
+                    I don't know where that happens.
+                    Accept either.  */
+                 while (*p && *p != ';' && *p != ',')
+                   {
+                     char *q;
+
+                     /* Check for and handle cretinous dbx symbol name
+                        continuation!  */
+                     if (*p == '\\')
+                       p = next_symbol_text ();
+
+                     /* Point to the character after the name
+                        of the enum constant.  */
+                     for (q = p; *q && *q != ':'; q++)
+                       ;
+                     /* Note that the value doesn't matter for
+                        enum constants in psymtabs, just in symtabs.  */
+                     ADD_PSYMBOL_TO_LIST (p, q - p,
+                                          VAR_NAMESPACE, LOC_CONST,
+                                          static_psymbols, 0);
+                     /* Point past the name.  */
+                     p = q;
+                     /* Skip over the value.  */
+                     while (*p && *p != ',')
+                       p++;
+                     /* Advance past the comma.  */
+                     if (*p)
+                       p++;
+                   }
+               }
+
+             continue;
+           case 'c':
+             /* Constant, e.g. from "const" in Pascal.  */
+             ADD_PSYMBOL_TO_LIST (namestring, p - namestring,
+                                  VAR_NAMESPACE, LOC_CONST,
+                                  static_psymbols, bufp->n_value);
+             continue;
+           default:
+#ifdef PROFILE_TYPES
+             if (isalpha(p[1]))
+                 printf ("Funny...LSYM with a letter that isn't a type\n");
+             autovars++;
+#endif
+             /* Skip if the thing following the : is
+                not a letter (which indicates declaration of a local
+                variable, which we aren't interested in).  */
+             continue;
+           }
+
+       case N_FUN:
+#if 0
+         /* This special-casing of N_FUN is just wrong; N_FUN
+            does not mean "function"; it means "text segment".
+            So N_FUN can go with 'V', etc. as well as 'f' or 'F'.  */
+
+         SET_NAMESTRING();
+
+         p = (char *) index (namestring, ':');
+
+         if (!p || p[1] == 'F') continue;
+
+#ifdef PROFILE_TYPES
+         if (p[1] != 'f')
+           printf ("Funny...FUN with a letter that isn't 'F' or 'f'.\n");
+         global_funs++;
+#endif
+
+         ADD_PSYMBOL_TO_LIST (namestring, p - namestring,
+                              VAR_NAMESPACE, LOC_BLOCK,
+                              static_psymbols, bufp->n_value);
+
+         continue;
+#endif /* 0 */
+       case N_GSYM:            /* Global (extern) variable; can be
+                                  data or bss (sigh).  */
+       case N_STSYM:           /* Data seg var -- static  */
+       case N_LCSYM:           /* BSS      "  */
+
+       /* Following may probably be ignored; I'll leave them here
+          for now (until I do Pascal and Modula 2 extensions).  */
+
+       case N_PC:              /* I may or may not need this; I
+                                  suspect not.  */
+#ifdef N_M2C
+       case N_M2C:             /* I suspect that I can ignore this here. */
+       case N_SCOPE:           /* Same.   */
+#endif
+
+         SET_NAMESTRING();
+
+         p = (char *) index (namestring, ':');
+         if (!p)
+           continue;           /* Not a debugging symbol.   */
+
+         process_symbol_for_psymtab:
+
+         /* Main processing section for debugging symbols which
+            the initial read through the symbol tables needs to worry
+            about.  If we reach this point, the symbol which we are
+            considering is definitely one we are interested in.
+            p must also contain the (valid) index into the namestring
+            which indicates the debugging type symbol.  */
+
+         switch (p[1])
+           {
+           case 'c':
+             ADD_PSYMBOL_TO_LIST (namestring, p - namestring,
+                                  VAR_NAMESPACE, LOC_CONST,
+                                  static_psymbols, bufp->n_value);
+             continue;
+           case 'S':
+             ADD_PSYMBOL_TO_LIST (namestring, p - namestring,
+                                  VAR_NAMESPACE, LOC_STATIC,
+                                  static_psymbols, bufp->n_value);
+             continue;
+           case 'G':
+             ADD_PSYMBOL_TO_LIST (namestring, p - namestring,
+                                  VAR_NAMESPACE, LOC_EXTERNAL,
+                                  global_psymbols, bufp->n_value);
+             continue;
+
+           case 't':
+             ADD_PSYMBOL_TO_LIST (namestring, p - namestring,
+                                  VAR_NAMESPACE, LOC_TYPEDEF,
+                                  global_psymbols, bufp->n_value);
+             continue;
+
+           case 'f':
+             ADD_PSYMBOL_TO_LIST (namestring, p - namestring,
+                                  VAR_NAMESPACE, LOC_BLOCK,
+                                  static_psymbols, bufp->n_value);
+             continue;
+
+             /* Two things show up here (hopefully); static symbols of
+                local scope (static used inside braces) or extensions
+                of structure symbols.  We can ignore both.  */
+           case 'V':
+           case '(':
+           case '0':
+           case '1':
+           case '2':
+           case '3':
+           case '4':
+           case '5':
+           case '6':
+           case '7':
+           case '8':
+           case '9':
+             /* Global functions are ignored here.  I'm not
+                sure what psymtab they go into (or just the misc
+                function vector).  */
+           case 'F':
+             continue;
+
+           default:
+             fatal ("Internal error: Unexpected debugging symbol type '%c' at symnum %d.\n",
+                    p[1], symnum);
+           }
+
+#ifdef N_BINCL
+       case N_EXCL:
+
+         SET_NAMESTRING();
+
+         /* Find the corresponding bincl and mark that psymtab on the
+            psymtab dependency list */
+         {
+           struct partial_symtab *needed_pst =
+             find_corresponding_bincl_psymtab (namestring, bufp->n_value);
+
+           /* If this include file was defined earlier in this file,
+              leave it alone.  */
+           if (needed_pst == pst) continue;
+
+           if (needed_pst)
+             {
+               int i;
+               int found = 0;
+
+               for (i = 0; i < dependencies_used; i++)
+                 if (dependency_list[i] == needed_pst)
+                   {
+                     found = 1;
+                     break;
+                   }
+
+               /* If it's already in the list, skip the rest.  */
+               if (found) continue;
+
+               dependency_list[dependencies_used++] = needed_pst;
+               if (dependencies_used >= dependencies_allocated)
+                 {
+                   struct partial_symtab **orig = dependency_list;
+                   dependency_list =
+                     (struct partial_symtab **)
+                       alloca ((dependencies_allocated *= 2)
+                               * sizeof (struct partial_symtab *));
+                   bcopy (orig, dependency_list,
+                          (dependencies_used
+                           * sizeof (struct partial_symtab *)));
+#ifdef DEBUG_INFO
+                   fprintf (stderr, "Had to reallocate dependency list.\n");
+                   fprintf (stderr, "New dependencies allocated: %d\n",
+                            dependencies_allocated);
+#endif
+                 }
+             }
+           else
+             error ("Invalid symbol data: \"repeated\" header file not previously seen, at symtab pos %d.",
+                    symnum);
+         }
+         continue;
+
+       case N_EINCL:
+#endif
+#ifdef N_DSLINE
+       case N_DSLINE:
+#endif
+#ifdef N_BSLINE
+       case N_BSLINE:
+#endif
+       case N_SSYM:            /* Claim: Structure or union element.
+                                  Hopefully, I can ignore this.  */
+       case N_ENTRY:           /* Alternate entry point; can ignore. */
+#ifdef N_MAIN
+       case N_MAIN:            /* Can definitely ignore this.   */
+#endif
+       case N_LENG:
+       case N_BCOMM:
+       case N_ECOMM:
+       case N_ECOML:
+       case N_FNAME:
+       case N_SLINE:
+       case N_RSYM:
+       case N_PSYM:
+       case N_LBRAC:
+       case N_RBRAC:
+         /* These symbols aren't interesting; don't worry about them */
+
+         continue;
+
+       default:
+         /* If we haven't found it yet, we've got problems */
+
+         if (IGNORE_SYMBOL (bufp->n_type))
+           continue;
+
+         fatal ("Bad symbol type 0x%x encountered in gdb scan", bufp->n_type);
+       }
+    }
+
+  /* If there's stuff to be cleaned up, clean it up.  */
+  if (entry_point < bufp->n_value
+      && entry_point >= last_o_file_start)
+    {
+      startup_file_start = last_o_file_start;
+      startup_file_end = bufp->n_value;
+    }
+
+  if (pst)
+    {
+      end_psymtab (pst, psymtab_include_list, includes_used,
+                  symnum * sizeof (struct nlist), end_of_text_addr,
+                  dependency_list, dependencies_used,
+                  global_psymbols.next, static_psymbols.next);
+      includes_used = 0;
+      dependencies_used = 0;
+      pst = (struct partial_symtab *) 0;
+    }
+
+  /* sort the global & static symtab list so we can binary search them */
+  qsort (global_psymbols.list, global_psymbols.next - global_psymbols.list,
+        sizeof (struct partial_symbol), compare_psymbols);
+  qsort (static_psymbols.list, static_psymbols.next - static_psymbols.list,
+        sizeof (struct partial_symbol), compare_psymbols);
+  free_bincl_list ();
+  discard_cleanups (old_chain);
+#ifdef PROFILE_TYPES
+  {
+    int i, j;
+#define __define_stab(SYM, NUMBER, NAME)       {NUMBER, NAME},
+  static struct xyzzy {
+    unsigned char symnum;
+    char *name;
+  } tmp_list[] = {
+#include "stab.def"
+    {0x1, "eREF"},
+    {0x2, "ABS"},
+    {0x3, "eABS"},
+    {0x4, "TEXT"},
+    {0x5, "eTEXT"},
+    {0x6, "DATA"},
+    {0x7, "eDATA"},
+    {0x8, "BSS"},
+    {0x9, "eBSS"},
+    {0x12, "COMM"},
+    {0x13, "eCOMM"},
+    {0x1f, "FN"},
+    {0, "Unknown"},
+};
+    for (i = 0; i < 256; i++)
+      {
+       for (j = 0; j < (sizeof (tmp_list) / sizeof (struct xyzzy)) - 1; j++)
+         if (tmp_list[j].symnum == i)
+           break;
+       printf ("Symbol \"%s\" (0x%x) occured %d times.\n",
+               tmp_list[j].name, i, profile_types[i]);
+      }
+    printf ("Auto vars (under LSYM): %d\n", autovars);
+    printf ("Global funs (under FUN): %d\n", global_funs);
+  }
+#endif
+}
+
+/*
+ * Allocate and partially fill a partial symtab.  It will be
+ * completely filled at the end of the symbol list.
+ */
+static struct partial_symtab *
+start_psymtab (filename, textlow, ldsymoff, global_syms, static_syms)
+     char *filename;
+     int textlow;
+     int ldsymoff;
+     struct partial_symbol *global_syms;
+     struct partial_symbol *static_syms;
+{
+  struct partial_symtab *result =
+    (struct partial_symtab *) obstack_alloc (psymbol_obstack,
+                                            sizeof (struct partial_symtab));
+
+  result->filename =
+    (char *) obstack_alloc (psymbol_obstack,
+                           strlen (filename) + 1);
+  strcpy (result->filename, filename);
+
+  result->textlow = textlow;
+  result->ldsymoff = ldsymoff;
+
+  result->readin = 0;
+
+  result->globals_offset = global_syms - global_psymbols.list;
+  result->statics_offset = static_syms - static_psymbols.list;
+
+  result->n_global_syms = 0;
+  result->n_static_syms = 0;
+
+  return result;
+}
+
+
+/* Close off the current usage of a partial_symbol table entry.  This
+   involves setting the correct number of includes (with a realloc),
+   setting the high text mark, setting the symbol length in the
+   executable, and setting the length of the global and static lists
+   of psymbols.
+
+   The global symbols and static symbols are then seperately sorted.
+
+   Then the partial symtab is put on the global list.
+   *** List variables and peculiarities of same. ***
+   */
+static void
+end_psymtab (pst, include_list, num_includes, capping_symbol_offset,
+            capping_text, dependency_list, number_dependencies,
+            capping_global, capping_static)
+     struct partial_symtab *pst;
+     char **include_list;
+     int num_includes;
+     int capping_symbol_offset;
+     int capping_text;
+     struct partial_symtab **dependency_list;
+     int number_dependencies;
+     struct partial_symbol *capping_global, *capping_static;
+{
+  int i;
+  register struct partial_symbol *ps;
+
+  pst->ldsymlen = capping_symbol_offset - pst->ldsymoff;
+  pst->texthigh = capping_text;
+
+  pst->n_global_syms =
+    capping_global - (global_psymbols.list + pst->globals_offset);
+  pst->n_static_syms =
+    capping_static - (static_psymbols.list + pst->statics_offset);
+
+  pst->dependencies = (struct partial_symtab **)
+    obstack_alloc (psymbol_obstack,
+                  number_dependencies * sizeof (struct partial_symtab *));
+  bcopy (dependency_list, pst->dependencies,
+        number_dependencies * sizeof (struct partial_symtab *));
+  pst->number_of_dependencies = number_dependencies;
+
+  for (i = 0; i < num_includes; i++)
+    {
+      /* Eventually, put this on obstack */
+      struct partial_symtab *subpst =
+       (struct partial_symtab *)
+         obstack_alloc (psymbol_obstack,
+                        sizeof (struct partial_symtab));
+
+      subpst->filename =
+       (char *) obstack_alloc (psymbol_obstack,
+                               strlen (include_list[i]) + 1);
+      strcpy (subpst->filename, include_list[i]);
+
+      subpst->ldsymoff =
+       subpst->ldsymlen =
+         subpst->textlow =
+           subpst->texthigh = 0;
+      subpst->readin = 0;
+
+      subpst->dependencies = (struct partial_symtab **)
+       obstack_alloc (psymbol_obstack,
+                      sizeof (struct partial_symtab *));
+      subpst->dependencies[0] = pst;
+      subpst->number_of_dependencies = 1;
+
+      subpst->globals_offset =
+       subpst->n_global_syms =
+         subpst->statics_offset =
+           subpst->n_static_syms = 0;
+
+      subpst->next = partial_symtab_list;
+      partial_symtab_list = subpst;
+    }
+
+  for (ps = global_psymbols.list + pst->globals_offset;
+       ps < capping_global; ++ps)
+    ps->pst = pst;
+  for (ps = static_psymbols.list + pst->statics_offset;
+       ps < capping_static; ++ps)
+    ps->pst = pst;
+
+  /* Put the psymtab on the psymtab list */
+  pst->next = partial_symtab_list;
+  partial_symtab_list = pst;
+}
+\f
+
+/* Helper routines for psymtab_to_symtab.  */
+static void scan_file_globals ();
+static void read_ofile_symtab ();
+
+static void
+psymtab_to_symtab_1 (pst, desc, stringtab, stringtab_size, sym_offset)
+     struct partial_symtab *pst;
+     int desc;
+     char *stringtab;
+     int stringtab_size;
+     int sym_offset;
+{
+  struct cleanup *old_chain;
+  int i;
+  
+  if (!pst)
+    return;
+
+  if (pst->readin)
+    {
+      fprintf (stderr, "Psymtab for %s already read in.  Shouldn't happen.\n",
+              pst->filename);
+      return;
+    }
+
+  /* Read in all partial symbtabs on which this one is dependent */
+  for (i = 0; i < pst->number_of_dependencies; i++)
+    if (!pst->dependencies[i]->readin)
+      {
+       /* Inform about additional files that need to be read in.  */
+       if (info_verbose)
+         {
+           printf_filtered (" and %s...", pst->dependencies[i]->filename);
+           fflush (stdout);
+         }
+       psymtab_to_symtab_1 (pst->dependencies[i], desc,
+                            stringtab, stringtab_size, sym_offset);
+      }
+
+  if (pst->ldsymlen)           /* Otherwise it's a dummy */
+    {
+      /* Init stuff necessary for reading in symbols */
+      free_pendings = 0;
+      pending_blocks = 0;
+      file_symbols = 0;
+      global_symbols = 0;
+      old_chain = make_cleanup (really_free_pendings, 0);
+
+      /* Read in this files symbols */
+      lseek (desc, sym_offset, L_SET);
+      read_ofile_symtab (desc, stringtab, stringtab_size,
+                        pst->ldsymoff,
+                        pst->ldsymlen, pst->textlow,
+                        pst->texthigh - pst->textlow, 0);
+      sort_symtab_syms (symtab_list); /* At beginning since just added */
+
+      do_cleanups (old_chain);
+    }
+
+  pst->readin = 1;
+}
+
+/*
+ * Read in all of the symbols for a given psymtab for real.  Return
+ * the value of the symtab you create.  Do not free the storage
+ * allocated to the psymtab; it may have pointers to it.
+ */
+struct symtab *
+psymtab_to_symtab(pst)
+     struct partial_symtab *pst;
+{
+  int desc;
+  DECLARE_FILE_HEADERS;
+  char *stringtab;
+  struct partial_symtab **list_patch;
+  int stsize, val;
+  struct stat statbuf;
+  struct cleanup *old_chain;
+  extern void close ();
+  int i;
+  struct symtab *result;
+  char *name = symfile;                /* Some of the macros require the */
+                               /* variable "name" to be defined in */
+                               /* the context in which they execute */
+                               /* (Yech!)  */
+
+  if (!pst)
+    return 0;
+
+  if (pst->readin)
+    {
+      fprintf (stderr, "Psymtab for %s already read in.  Shouldn't happen.\n",
+              pst->filename);
+      return 0;
+    }
+
+  if (!name)
+    error("No symbol file currently specified; use command symbol-file");
+
+  if (pst->ldsymlen || pst->number_of_dependencies)
+    {
+      /* Print the message now, before reading the string table,
+        to avoid disconcerting pauses.  */
+      if (info_verbose)
+       {
+         printf_filtered ("Reading in symbols for %s...", pst->filename);
+         fflush (stdout);
+       }
+
+      /* Open symbol file and read in string table */
+      if (stat (name, &statbuf) < 0)
+       perror_with_name (name);
+      desc = open(name, O_RDONLY, 0); /* symbol_file_command
+                                        guarrantees that the symbol file name
+                                        will be absolute, so there is no
+                                        need for openp */
+
+      old_chain = make_cleanup (close, desc);
+
+      if (desc < 0)
+       error("Symbol file not readable");
+
+      READ_FILE_HEADERS (desc, name);
+
+#if 0
+      /* Read in the string table */
+      lseek (desc, STRING_TABLE_OFFSET, L_SET);
+      READ_STRING_TABLE_SIZE (stsize);
+      if (stsize >= 0 && stsize < statbuf.st_size)
+       {
+#ifdef BROKEN_LARGE_ALLOCA
+         stringtab = (char *) xmalloc (stsize);
+         make_cleanup (free, stringtab);
+#else
+         stringtab = (char *) alloca (stsize);
+#endif
+       }
+      else
+       stringtab = NULL;
+      if (stringtab == NULL)
+       error ("ridiculous string table size: %d bytes", stsize);
+
+      /* Usually READ_STRING_TABLE_SIZE will have shifted the file pointer.
+        Occaisionally, it won't.  */
+      val = lseek (desc, STRING_TABLE_OFFSET, L_SET);
+      if (val < 0)
+       perror_with_name (name);
+      val = myread (desc, stringtab, stsize);
+      if (val < 0)
+       perror_with_name (name);
+#endif /* 0 */
+      stringtab = symfile_string_table;
+      stsize = symfile_string_table_size;
+
+      psymtab_to_symtab_1 (pst, desc, stringtab, stsize,
+                          SYMBOL_TABLE_OFFSET);
+
+      /* Match with global symbols.  This  only needs to be done once,
+         after all of the symtabs and dependencies have been read in.   */
+      scan_file_globals ();
+
+      do_cleanups (old_chain);
+
+      /* Finish up the debug error message.  */
+      if (info_verbose)
+       printf_filtered ("done.\n");
+    }
+
+  /* Search through list for correct name. */
+  for (result = symtab_list; result; result = result->next)
+    if (!strcmp (result->filename, pst->filename))
+      return result;
+
+  return 0;
+}
+
+/*
+ * Scan through all of the global symbols defined in the object file,
+ * assigning values to the debugging symbols that need to be assigned
+ * to.  Get these symbols from the misc function list.
+ */
+static void
+scan_file_globals ()
+{
+  int hash;
+  int mf;
+
+  for (mf = 0; mf < misc_function_count; mf++)
+    {
+      char *namestring = misc_function_vector[mf].name;
+      struct symbol *sym, *prev;
+
+      QUIT;
+
+      prev = (struct symbol *) 0;
+
+      /* Get the hash index and check all the symbols
+        under that hash index. */
+
+      hash = hashname (namestring);
+
+      for (sym = global_sym_chain[hash]; sym;)
+       {
+         if (*namestring == SYMBOL_NAME (sym)[0]
+             && !strcmp(namestring + 1, SYMBOL_NAME (sym) + 1))
+           {
+             /* Splice this symbol out of the hash chain and
+                assign the value we have to it. */
+             if (prev)
+               SYMBOL_VALUE (prev) = SYMBOL_VALUE (sym);
+             else
+               global_sym_chain[hash]
+                 = (struct symbol *) SYMBOL_VALUE (sym);
+             
+             /* Check to see whether we need to fix up a common block.  */
+             /* Note: this code might be executed several times for
+                the same symbol if there are multiple references.  */
+             if (SYMBOL_CLASS (sym) == LOC_BLOCK)
+               fix_common_block (sym, misc_function_vector[mf].address);
+             else
+               SYMBOL_VALUE (sym) = misc_function_vector[mf].address;
+             
+             if (prev)
+               sym = (struct symbol *) SYMBOL_VALUE (prev);
+             else
+               sym = global_sym_chain[hash];
+           }
+         else
+           {
+             prev = sym;
+             sym = (struct symbol *) SYMBOL_VALUE (sym);
+           }
+       }
+    }
+}
+
+/*
+ * Read in a defined section of a specific object file's symbols.
+ *
+ * DESC is the file descriptor for the file, positioned at the
+ * beginning of the symtab
+ * STRINGTAB is a pointer to the files string
+ * table, already read in
+ * SYM_OFFSET is the offset within the file of
+ * the beginning of the symbols we want to read, NUM_SUMBOLS is the
+ * number of symbols to read
+ * TEXT_OFFSET is the offset to be added to
+ * all values of symbols coming in and
+ * TEXT_SIZE is the size of the text segment read in.
+ * OFFSET is a flag which indicates that the value of all of the
+ * symbols should be offset by TEXT_OFFSET (for the purposes of
+ * incremental linking).
+ */
+
+static void
+read_ofile_symtab (desc, stringtab, stringtab_size, sym_offset,
+                  sym_size, text_offset, text_size, offset)
+     int desc;
+     register char *stringtab;
+     int sym_offset;
+     int sym_size;
+     int text_offset;
+     int text_size;
+     int offset;
+{
+  register char *namestring;
+  register struct symbol *sym, *prev;
+  int hash;
+  struct cleanup *old_chain;
+  struct nlist *bufp;
+  unsigned char type;
+#ifdef N_BINCL
+  subfile_stack = 0;
+#endif
+
+  stringtab_global = stringtab;
+  last_source_file = 0;
+
+  symtab_input_desc = desc;
+  symbuf_end = symbuf_idx = 0;
+
+  /* It is necessary to actually read one symbol *before* the start
+     of this symtab's symbols, because the GCC_COMPILED_FLAG_SYMBOL
+     occurs before the N_SO symbol.
+
+     Detecting this in read_dbx_symtab
+     would slow down initial readin, so we look for it here instead.  */
+  if (sym_offset >= sizeof (struct nlist))
+    {
+      lseek (desc, sym_offset - sizeof (struct nlist), L_INCR);
+      fill_symbuf ();
+      bufp = &symbuf[symbuf_idx++];
+
+      if (bufp->n_un.n_strx < 0 || bufp->n_un.n_strx >= stringtab_size)
+       error ("Invalid symbol data: bad string table offset: %d",
+              bufp->n_un.n_strx);
+      namestring = bufp->n_un.n_strx + stringtab;
+
+      processing_gcc_compilation =
+       (bufp->n_type == N_TEXT
+        && !strcmp (namestring, GCC_COMPILED_FLAG_SYMBOL));
+    }
+  else
+    {
+      /* The N_SO starting this symtab is the first symbol, so we
+        better not check the symbol before it.  I'm not this can
+        happen, but it doesn't hurt to check for it.  */
+      lseek(desc, sym_offset, L_INCR);
+      processing_gcc_compilation = 0;
+    }
+
+  if (symbuf_idx == symbuf_end)
+    fill_symbuf();
+  bufp = &symbuf[symbuf_idx];
+  if ((unsigned char) bufp->n_type != N_SO)
+    fatal("First symbol in segment of executable not a source symbol");
+
+  for (symnum = 0;
+       symnum < sym_size / sizeof(struct nlist);
+       symnum++)
+    {
+      QUIT;                    /* Allow this to be interruptable */
+      if (symbuf_idx == symbuf_end)
+       fill_symbuf();
+      bufp = &symbuf[symbuf_idx++];
+      type = bufp->n_type;
+
+      if (offset &&
+         (type == N_TEXT || type == N_DATA || type == N_BSS))
+       bufp->n_value += text_offset;
+
+      if (bufp->n_un.n_strx < 0 || bufp->n_un.n_strx >= stringtab_size)
+       error ("Invalid symbol data: bad string table offset: %d",
+              bufp->n_un.n_strx);
+      namestring = bufp->n_un.n_strx + stringtab;
+
+      if (type & N_STAB)
+       process_one_symbol(type, bufp->n_desc,
+                          bufp->n_value, namestring);
+      /* We skip checking for a new .o or -l file; that should never
+         happen in this routine. */
+      else if (type == N_TEXT
+              && !strcmp (namestring, GCC_COMPILED_FLAG_SYMBOL))
+       /* I don't think this code will ever be executed, because
+          the GCC_COMPILED_FLAG_SYMBOL usually is right before
+          the N_SO symbol which starts this source file.
+          However, there is no reason not to accept
+          the GCC_COMPILED_FLAG_SYMBOL anywhere.  */
+       processing_gcc_compilation = 1;
+      else if (type & N_EXT || type == N_TEXT
+#ifdef N_NBTEXT
+              || type == N_NBTEXT
+#endif
+              )
+         /* Global symbol: see if we came across a dbx defintion for
+            a corresponding symbol.  If so, store the value.  Remove
+            syms from the chain when their values are stored, but
+            search the whole chain, as there may be several syms from
+            different files with the same name. */
+         /* This is probably not true.  Since the files will be read
+            in one at a time, each reference to a global symbol will
+            be satisfied in each file as it appears. So we skip this
+            section. */
+       &stringtab_global;      /* For debugger; am I right? */
+    }
+  end_symtab (text_offset + text_size);
+}
+\f
+static int
+hashname (name)
+     char *name;
+{
+  register char *p = name;
+  register int total = p[0];
+  register int c;
+
+  c = p[1];
+  total += c << 2;
+  if (c)
+    {
+      c = p[2];
+      total += c << 4;
+      if (c)
+       total += p[3] << 6;
+    }
+
+  /* Ensure result is positive.  */
+  if (total < 0) total += (1000 << 6);
+  return total % HASHSIZE;
+}
+
+/* Put all appropriate global symbols in the symseg data
+   onto the hash chains so that their addresses will be stored
+   when seen later in loader global symbols.  */
+
+static void
+hash_symsegs ()
+{
+  /* Look at each symbol in each block in each symseg symtab.  */
+  struct symtab *s;
+  for (s = symseg_chain; s; s = s->next)
+    {
+      register int n;
+      for (n = BLOCKVECTOR_NBLOCKS (BLOCKVECTOR (s)) - 1; n >= 0; n--)
+       {
+         register struct block *b = BLOCKVECTOR_BLOCK (BLOCKVECTOR (s), n);
+         register int i;
+         for (i = BLOCK_NSYMS (b) - 1; i >= 0; i--)
+           {
+             register struct symbol *sym = BLOCK_SYM (b, i);
+
+             /* Put the symbol on a chain if its value is an address
+                that is figured out by the loader.  */
+
+             if (SYMBOL_CLASS (sym) == LOC_EXTERNAL)
+               {
+                 register int hash = hashname (SYMBOL_NAME (sym));
+                 SYMBOL_VALUE (sym) = (int) global_sym_chain[hash];
+                 global_sym_chain[hash] = sym;
+                 SYMBOL_CLASS (sym) = LOC_STATIC;
+               }
+           }
+       }
+    }
+}
+\f
+static void
+process_one_symbol (type, desc, value, name)
+     int type, desc;
+     CORE_ADDR value;
+     char *name;
+{
+  register struct context_stack *new;
+  char *colon_pos;
+
+  /* Something is wrong if we see real data before
+     seeing a source file name.  */
+
+  if (last_source_file == 0 && type != N_SO)
+    {
+      /* Currently this ignores N_ENTRY on Gould machines, N_NSYM on machines
+        where that code is defined.  */
+      if (IGNORE_SYMBOL (type))
+       return;
+
+      error ("Invalid symbol data: does not start by identifying a source file.");
+    }
+
+  switch (type)
+    {
+    case N_FUN:
+    case N_FNAME:
+      /* Either of these types of symbols indicates the start of
+        a new function.  We must process its "name" normally for dbx,
+        but also record the start of a new lexical context, and possibly
+        also the end of the lexical context for the previous function.  */
+      /* This is not always true.  This type of symbol may indicate a
+         text segment variable.  */
+
+      colon_pos = index (name, ':');
+      if (!colon_pos++
+         || (*colon_pos != 'f' && *colon_pos != 'F'))
+       {
+         define_symbol (value, name, desc);
+         break;
+       }
+
+      within_function = 1;
+      if (context_stack_depth > 0)
+       {
+         new = &context_stack[--context_stack_depth];
+         /* Make a block for the local symbols within.  */
+         finish_block (new->name, &local_symbols, new->old_blocks,
+                       new->start_addr, value);
+       }
+      /* Stack must be empty now.  */
+      if (context_stack_depth != 0)
+       error ("Invalid symbol data: unmatched N_LBRAC before symtab pos %d.",
+              symnum);
+
+      new = &context_stack[context_stack_depth++];
+      new->old_blocks = pending_blocks;
+      new->start_addr = value;
+      new->name = define_symbol (value, name, desc);
+      local_symbols = 0;
+      break;
+
+    case N_LBRAC:
+      /* This "symbol" just indicates the start of an inner lexical
+        context within a function.  */
+
+      if (context_stack_depth == context_stack_size)
+       {
+         context_stack_size *= 2;
+         context_stack = (struct context_stack *)
+           xrealloc (context_stack,
+                     (context_stack_size
+                      * sizeof (struct context_stack)));
+       }
+
+      new = &context_stack[context_stack_depth++];
+      new->depth = desc;
+      new->locals = local_symbols;
+      new->old_blocks = pending_blocks;
+      new->start_addr = value;
+      new->name = 0;
+      local_symbols = 0;
+      break;
+
+    case N_RBRAC:
+      /* This "symbol" just indicates the end of an inner lexical
+        context that was started with N_LBRAC.  */
+      new = &context_stack[--context_stack_depth];
+      if (desc != new->depth)
+       error ("Invalid symbol data: N_LBRAC/N_RBRAC symbol mismatch, symtab pos %d.", symnum);
+
+      /* Some native compilers put the variable decls inside of an
+         LBRAC/RBRAC block.  This macro should be nonzero if this
+        is true.  DESC is N_DESC from the N_RBRAC symbol.  */
+#if !defined (VARIABLES_INSIDE_BLOCK)
+#define VARIABLES_INSIDE_BLOCK(desc) 0
+#endif
+
+      /* Can only use new->locals as local symbols here if we're in
+         gcc or on a machine that puts them before the lbrack.  */
+      if (!VARIABLES_INSIDE_BLOCK(desc))
+       local_symbols = new->locals;
+
+      /* If this is not the outermost LBRAC...RBRAC pair in the
+        function, its local symbols preceded it, and are the ones
+        just recovered from the context stack.  Defined the block for them.
+
+        If this is the outermost LBRAC...RBRAC pair, there is no
+        need to do anything; leave the symbols that preceded it
+        to be attached to the function's own block.  However, if
+        it is so, we need to indicate that we just moved outside
+        of the function.  */
+      if (local_symbols
+         && context_stack_depth > !VARIABLES_INSIDE_BLOCK(desc))
+       {
+         /* Muzzle a compiler bug that makes end < start.  */
+         if (new->start_addr > value)
+           new->start_addr = value;
+         /* Make a block for the local symbols within.  */
+         finish_block (0, &local_symbols, new->old_blocks,
+                       new->start_addr + last_source_start_addr,
+                       value + last_source_start_addr);
+       }
+      else
+       {
+         within_function = 0;
+       }
+      if (VARIABLES_INSIDE_BLOCK(desc))
+       /* Now pop locals of block just finished.  */
+       local_symbols = new->locals;
+      break;
+
+    case N_FN | N_EXT:
+      /* This kind of symbol supposedly indicates the start
+        of an object file.  In fact this type does not appear.  */
+      break;
+
+    case N_SO:
+      /* This type of symbol indicates the start of data
+        for one source file.
+        Finish the symbol table of the previous source file
+        (if any) and start accumulating a new symbol table.  */
+#ifdef PCC_SOL_BROKEN
+      /* pcc bug, occasionally puts out SO for SOL.  */
+      if (context_stack_depth > 0)
+       {
+         start_subfile (name);
+         break;
+       }
+#endif
+      if (last_source_file)
+       end_symtab (value);
+      start_symtab (name, value);
+      break;
+
+    case N_SOL:
+      /* This type of symbol indicates the start of data for
+        a sub-source-file, one whose contents were copied or
+        included in the compilation of the main source file
+        (whose name was given in the N_SO symbol.)  */
+      start_subfile (name);
+      break;
+
+#ifdef N_BINCL
+    case N_BINCL:
+      push_subfile ();
+      add_new_header_file (name, value);
+      start_subfile (name);
+      break;
+
+    case N_EINCL:
+      start_subfile (pop_subfile ());
+      break;
+
+    case N_EXCL:
+      add_old_header_file (name, value);
+      break;
+#endif /* have N_BINCL */
+
+    case N_SLINE:
+      /* This type of "symbol" really just records
+        one line-number -- core-address correspondence.
+        Enter it in the line list for this symbol table.  */
+      record_line (desc, value);
+      break;
+
+    case N_BCOMM:
+      if (common_block)
+       error ("Invalid symbol data: common within common at symtab pos %d",
+              symnum);
+      common_block = local_symbols;
+      common_block_i = local_symbols ? local_symbols->nsyms : 0;
+      break;
+
+    case N_ECOMM:
+      /* Symbols declared since the BCOMM are to have the common block
+        start address added in when we know it.  common_block points to
+        the first symbol after the BCOMM in the local_symbols list;
+        copy the list and hang it off the symbol for the common block name
+        for later fixup.  */
+      {
+       int i;
+       struct pending *link = local_symbols;
+       struct symbol *sym =
+         (struct symbol *) xmalloc (sizeof (struct symbol));
+       bzero (sym, sizeof *sym);
+       SYMBOL_NAME (sym) = savestring (name, strlen (name));
+       SYMBOL_CLASS (sym) = LOC_BLOCK;
+       SYMBOL_NAMESPACE (sym) = (enum namespace)((long)
+         copy_pending (local_symbols, common_block_i, common_block));
+       i = hashname (SYMBOL_NAME (sym));
+       SYMBOL_VALUE (sym) = (int) global_sym_chain[i];
+       global_sym_chain[i] = sym;
+       common_block = 0;
+       break;
+      }
+
+    case N_ECOML:
+    case N_LENG:
+      break;
+
+    default:
+      if (name)
+       define_symbol (value, name, desc);
+    }
+}
+\f
+/* This function was added for C++ functionality.  I presume that it
+   condenses the bunches formed by reading in an additional .o file
+   (incremental linking). */
+
+static void
+condense_addl_misc_bunches ()
+{
+  register int i, j;
+  register struct misc_bunch *bunch;
+#ifdef NAMES_HAVE_UNDERSCORE
+  int offset = 1;
+#else
+  int offset = 0;
+#endif
+
+  misc_function_vector
+    = (struct misc_function *)  xrealloc (misc_function_vector,
+                                         (misc_count + misc_function_count) * sizeof (struct misc_function));
+
+  j = misc_function_count;
+  bunch = misc_bunch;
+  while (bunch)
+    {
+      for (i = 0; i < misc_bunch_index; i++)
+       {
+         misc_function_vector[j] = bunch->contents[i];
+         misc_function_vector[j].name
+           = concat (misc_function_vector[j].name
+                     + (misc_function_vector[j].name[0] == '_' ? offset : 0),
+                     "", "");
+         j++;
+       }
+      bunch = bunch->next;
+      misc_bunch_index = MISC_BUNCH_SIZE;
+    }
+
+  misc_function_count += misc_count;
+
+  /* Sort the misc functions by address.  */
+
+  qsort (misc_function_vector, misc_function_count,
+        sizeof (struct misc_function),  compare_misc_functions);
+}
+\f
+
+/* Read in another .o file and create a symtab entry for it.*/
+
+static void
+read_addl_syms (desc, stringtab, nlistlen, text_addr, text_size)
+     int desc;
+     register char *stringtab;
+     register int nlistlen;
+     unsigned text_addr;
+     int text_size;
+{
+  FILE *stream = fdopen (desc, "r");
+  register char *namestring;
+  register struct symbol *sym, *prev;
+  int hash;
+
+#ifdef N_BINCL
+  subfile_stack = 0;
+#endif
+
+  last_source_file = 0;
+  bzero (global_sym_chain, sizeof global_sym_chain);
+  symtab_input_desc = desc;
+  stringtab_global = stringtab;
+  fill_symbuf ();
+
+  for (symnum = 0; symnum < nlistlen; symnum++)
+    {
+      struct nlist *bufp;
+      unsigned char type;
+
+      QUIT;    /* allow this to be interruptable */
+      if (symbuf_idx == symbuf_end)
+       fill_symbuf ();
+      bufp = &symbuf[symbuf_idx++];
+      type = bufp->n_type & N_TYPE;
+      namestring = bufp->n_un.n_strx + stringtab;
+
+      if( (type == N_TEXT) || (type == N_DATA) || (type == N_BSS) )
+       {
+         /* Relocate this file's symbol table information
+            to the address it has been loaded into.  */
+         bufp->n_value += text_addr;
+       }
+
+      type = bufp->n_type;
+
+      if (type & N_STAB)
+       process_one_symbol (type, bufp->n_desc,
+                           bufp->n_value, namestring);
+      /* A static text symbol whose name ends in ".o"
+        can only mean the start of another object file.
+        So end the symtab of the source file we have been processing.
+        This is how we avoid counting the libraries as part
+        or the last source file.
+        Also this way we find end of first object file (crt0).  */
+      else if ((type == N_TEXT
+#ifdef N_NBTEXT
+               || type == N_NBTEXT
+#endif
+               )
+              && (!strcmp (namestring + strlen (namestring) - 2, ".o"))
+              || ! strncmp (namestring, "-l", 2))
+       {
+         if (last_source_file)
+           end_symtab (bufp->n_value);
+       }
+      else if (type & N_EXT || type == N_TEXT
+#ifdef N_NBTEXT
+              || type == N_NBTEXT
+#endif
+              )
+       {
+         int used_up = 0;
+
+         /* Record the location of _etext.  */
+         if (type == (N_TEXT | N_EXT)
+             && !strcmp (namestring, "_etext"))
+           end_of_text_addr = bufp->n_value;
+
+#if 0
+         /* 25 Sep 89: The following seems to be stolen from
+            read_ofile_symtab, and is wrong here (i.e. there was no
+            first pass for add-file symbols).  */
+         /* This shouldn't be necessary, as we now do all of this work
+            in scan_global syms and all misc functions should have been
+            recorded on the first pass.  */
+         /* Global symbol: see if we came across a dbx definition
+            for a corresponding symbol.  If so, store the value.
+            Remove syms from the chain when their values are stored,
+            but search the whole chain, as there may be several syms
+            from different files with the same name.  */
+         if (type & N_EXT)
+           {
+             prev = 0;
+#ifdef NAMES_HAVE_UNDERSCORE
+             hash = hashname (namestring + 1);
+#else /* not NAMES_HAVE_UNDERSCORE */
+             hash = hashname (namestring);
+#endif /* not NAMES_HAVE_UNDERSCORE */
+             for (sym = global_sym_chain[hash];
+                  sym;)
+               {
+                 if (
+#ifdef NAMES_HAVE_UNDERSCORE
+                     *namestring == '_'
+                     && namestring[1] == SYMBOL_NAME (sym)[0]
+                     &&
+                     !strcmp (namestring + 2, SYMBOL_NAME (sym) + 1)
+#else /* NAMES_HAVE_UNDERSCORE */
+                     namestring[0] == SYMBOL_NAME (sym)[0]
+                     &&
+                     !strcmp (namestring + 1, SYMBOL_NAME (sym) + 1)
+#endif /* NAMES_HAVE_UNDERSCORE */
+                     )
+                   {
+                     if (prev)
+                       SYMBOL_VALUE (prev) = SYMBOL_VALUE (sym);
+                     else
+                       global_sym_chain[hash]
+                         = (struct symbol *) SYMBOL_VALUE (sym);
+                     if (SYMBOL_CLASS (sym) == LOC_BLOCK)
+                       fix_common_block (sym, bufp->n_value);
+                     else
+                       SYMBOL_VALUE (sym) = bufp->n_value;
+                     if (prev)
+                       sym = (struct symbol *) SYMBOL_VALUE (prev);
+                     else
+                       sym = global_sym_chain[hash];
+
+                     used_up = 1;
+                   }
+                 else
+                   {
+                     prev = sym;
+                     sym = (struct symbol *) SYMBOL_VALUE (sym);
+                   }
+               }
+           }
+
+         /* Defined global or text symbol: record as a misc function
+            if it didn't give its address to a debugger symbol above.  */
+         if (type <= (N_TYPE | N_EXT)
+             && type != N_EXT
+             && ! used_up)
+           record_misc_function (namestring, bufp->n_value,
+                                 bufp->n_type);
+#endif /* 0 */
+       }
+    }
+
+  if (last_source_file)
+    end_symtab (text_addr + text_size);
+
+  fclose (stream);
+}
+
+/* C++:
+   This function allows the addition of incrementally linked object files.
+   Since this has a fair amount of code in common with symbol_file_command,
+   it might be worthwhile to consolidate things, as was done with
+   read_dbx_symtab and condense_misc_bunches. */
+
+void
+add_file_command (arg_string)
+     char* arg_string;
+{
+  register int desc;
+  DECLARE_FILE_HEADERS;
+  struct nlist *nlist;
+  char *stringtab;
+  long buffer;
+  register int val;
+  extern void close ();
+  struct cleanup *old_chain;
+  struct symtab *symseg;
+  struct stat statbuf;
+  char *name;
+  unsigned text_addr;
+
+  if (arg_string == 0)
+    error ("add-file takes a file name and an address");
+
+  arg_string = tilde_expand (arg_string);
+  make_cleanup (free, arg_string);
+
+  for( ; *arg_string == ' '; arg_string++ );
+  name = arg_string;
+  for( ; *arg_string && *arg_string != ' ' ; arg_string++ );
+  *arg_string++ = (char) 0;
+
+  if (name[0] == 0)
+    error ("add-file takes a file name and an address");
+
+  text_addr = parse_and_eval_address (arg_string);
+
+  dont_repeat ();
+
+  if (!query ("add symbol table from filename \"%s\" at text_addr = 0x%x\n",
+             name, text_addr))
+    error ("Not confirmed.");
+
+  desc = open (name, O_RDONLY);
+  if (desc < 0)
+    perror_with_name (name);
+
+  old_chain = make_cleanup (close, desc);
+
+  READ_FILE_HEADERS (desc, name);
+
+  if (NUMBER_OF_SYMBOLS == 0)
+    {
+      printf ("%s does not have a symbol-table.\n", name);
+      fflush (stdout);
+      return;
+    }
+
+  printf ("Reading symbol data from %s...", name);
+  fflush (stdout);
+
+  /* Now read the string table, all at once.  */
+  val = lseek (desc, STRING_TABLE_OFFSET, 0);
+  if (val < 0)
+    perror_with_name (name);
+  if (stat (name, &statbuf) < 0)
+    perror_with_name (name);
+  READ_STRING_TABLE_SIZE (buffer);
+  if (buffer >= 0 && buffer < statbuf.st_size)
+    {
+#ifdef BROKEN_LARGE_ALLOCA
+      stringtab = (char *) xmalloc (buffer);
+      make_cleanup (free, stringtab);
+#else
+      stringtab = (char *) alloca (buffer);
+#endif
+    }
+  else
+    stringtab = NULL;
+  if (stringtab == NULL)
+    error ("ridiculous string table size: %d bytes", buffer);
+
+  /* Usually READ_STRING_TABLE_SIZE will have shifted the file pointer.
+     Occaisionally, it won't.  */
+  val = lseek (desc, STRING_TABLE_OFFSET, 0);
+  if (val < 0)
+    perror_with_name (name);
+  val = myread (desc, stringtab, buffer);
+  if (val < 0)
+    perror_with_name (name);
+
+  /* Symsegs are no longer supported by GDB.  Setting symseg_chain to
+     0 is easier than finding all the symseg code and eliminating it.  */
+  symseg_chain = 0;
+
+  /* Position to read the symbol table.  Do not read it all at once. */
+  val = lseek (desc, SYMBOL_TABLE_OFFSET, 0);
+  if (val < 0)
+    perror_with_name (name);
+
+  init_misc_functions ();
+  make_cleanup (discard_misc_bunches, 0);
+  init_header_files ();
+  make_cleanup (free_header_files, 0);
+  free_pendings = 0;
+  pending_blocks = 0;
+  file_symbols = 0;
+  global_symbols = 0;
+  make_cleanup (really_free_pendings, 0);
+
+  read_addl_syms (desc, stringtab, NUMBER_OF_SYMBOLS, text_addr,
+                 SIZE_OF_TEXT_SEGMENT);
+
+
+  /* Sort symbols alphabetically within each block.  */
+
+  sort_syms ();
+
+  /* Go over the misc functions and install them in vector.  */
+
+  condense_addl_misc_bunches (1);
+
+  /* Don't allow char * to have a typename (else would get caddr_t.)  */
+
+  TYPE_NAME (lookup_pointer_type (builtin_type_char)) = 0;
+
+  do_cleanups (old_chain);
+
+  /* Free the symtabs made by read_symsegs, but not their contents,
+     which have been copied into symtabs on symtab_list.  */
+  while (symseg_chain)
+    {
+      register struct symtab *s = symseg_chain->next;
+      free (symseg_chain);
+      symseg_chain = s;
+    }
+
+  printf ("done.\n");
+  fflush (stdout);
+}
+\f
+/* Read a number by which a type is referred to in dbx data,
+   or perhaps read a pair (FILENUM, TYPENUM) in parentheses.
+   Just a single number N is equivalent to (0,N).
+   Return the two numbers by storing them in the vector TYPENUMS.
+   TYPENUMS will then be used as an argument to dbx_lookup_type.  */
+
+static void
+read_type_number (pp, typenums)
+     register char **pp;
+     register int *typenums;
+{
+  if (**pp == '(')
+    {
+      (*pp)++;
+      typenums[0] = read_number (pp, ',');
+      typenums[1] = read_number (pp, ')');
+    }
+  else
+    {
+      typenums[0] = 0;
+      typenums[1] = read_number (pp, 0);
+    }
+}
+
+
+\f
+static struct symbol *
+define_symbol (value, string, desc)
+     int value;
+     char *string;
+     int desc;
+{
+  register struct symbol *sym
+    = (struct symbol *) obstack_alloc (symbol_obstack, sizeof (struct symbol));
+  char *p = (char *) index (string, ':');
+  int deftype;
+  register int i;
+
+  /* Ignore syms with empty names.  */
+  if (string[0] == 0)
+    return 0;
+
+  /* Ignore old-style symbols from cc -go  */
+  if (p == 0)
+    return 0;
+
+  SYMBOL_NAME (sym)
+    = (char *) obstack_alloc (symbol_obstack, ((p - string) + 1));
+  /* Open-coded bcopy--saves function call time.  */
+  {
+    register char *p1 = string;
+    register char *p2 = SYMBOL_NAME (sym);
+    while (p1 != p)
+      *p2++ = *p1++;
+    *p2++ = '\0';
+  }
+  p++;
+  /* Determine the type of name being defined.  */
+  if ((*p >= '0' && *p <= '9') || *p == '(')
+    deftype = 'l';
+  else
+    deftype = *p++;
+
+  /* c is a special case, not followed by a type-number.
+     SYMBOL:c=iVALUE for an integer constant symbol.
+     SYMBOL:c=rVALUE for a floating constant symbol.
+     SYMBOL:c=eTYPE,INTVALUE for an enum constant symbol.
+        e.g. "b:c=e6,0" for "const b = blob1"
+       (where type 6 is defined by "blobs:t6=eblob1:0,blob2:1,;").  */
+  if (deftype == 'c')
+    {
+      if (*p++ != '=')
+       error ("Invalid symbol data at symtab pos %d.", symnum);
+      switch (*p++)
+       {
+       case 'r':
+         {
+           double d = atof (p);
+           char *value;
+
+           SYMBOL_TYPE (sym) = builtin_type_double;
+           value = (char *) obstack_alloc (symbol_obstack, sizeof (double));
+           bcopy (&d, value, sizeof (double));
+           SYMBOL_VALUE_BYTES (sym) = value;
+           SYMBOL_CLASS (sym) = LOC_CONST_BYTES;
+         }
+         break;
+       case 'i':
+         {
+           SYMBOL_TYPE (sym) = builtin_type_int;
+           SYMBOL_VALUE (sym) = atoi (p);
+           SYMBOL_CLASS (sym) = LOC_CONST;
+         }
+         break;
+       case 'e':
+         /* SYMBOL:c=eTYPE,INTVALUE for an enum constant symbol.
+            e.g. "b:c=e6,0" for "const b = blob1"
+            (where type 6 is defined by "blobs:t6=eblob1:0,blob2:1,;").  */
+         {
+           int typenums[2];
+           
+           read_type_number (&p, typenums);
+           if (*p++ != ',')
+             error ("Invalid symbol data: no comma in enum const symbol");
+           
+           SYMBOL_TYPE (sym) = *dbx_lookup_type (typenums);
+           SYMBOL_VALUE (sym) = atoi (p);
+           SYMBOL_CLASS (sym) = LOC_CONST;
+         }
+         break;
+       default:
+         error ("Invalid symbol data at symtab pos %d.", symnum);
+       }
+      SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE;
+      add_symbol_to_list (sym, &file_symbols);
+      return sym;
+    }
+
+  /* Now usually comes a number that says which data type,
+     and possibly more stuff to define the type
+     (all of which is handled by read_type)  */
+
+  if (deftype == 'p' && *p == 'F')
+    /* pF is a two-letter code that means a function parameter in Fortran.
+       The type-number specifies the type of the return value.
+       Translate it into a pointer-to-function type.  */
+    {
+      p++;
+      SYMBOL_TYPE (sym)
+       = lookup_pointer_type (lookup_function_type (read_type (&p)));
+    }
+  else
+    {
+      struct type *type = read_type (&p);
+
+      if ((deftype == 'F' || deftype == 'f')
+         && TYPE_CODE (type) != TYPE_CODE_FUNC)
+       SYMBOL_TYPE (sym) = lookup_function_type (type);
+      else
+       SYMBOL_TYPE (sym) = type;
+    }
+
+  switch (deftype)
+    {
+    case 'f':
+      SYMBOL_CLASS (sym) = LOC_BLOCK;
+      SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE;
+      add_symbol_to_list (sym, &file_symbols);
+      break;
+
+    case 'F':
+      SYMBOL_CLASS (sym) = LOC_BLOCK;
+      SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE;
+      add_symbol_to_list (sym, &global_symbols);
+      break;
+
+    case 'G':
+      /* For a class G (global) symbol, it appears that the
+        value is not correct.  It is necessary to search for the
+        corresponding linker definition to find the value.
+        These definitions appear at the end of the namelist.  */
+      i = hashname (SYMBOL_NAME (sym));
+      SYMBOL_VALUE (sym) = (int) global_sym_chain[i];
+      global_sym_chain[i] = sym;
+      SYMBOL_CLASS (sym) = LOC_STATIC;
+      SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE;
+      add_symbol_to_list (sym, &global_symbols);
+      break;
+
+      /* This case is faked by a conditional above,
+        when there is no code letter in the dbx data.
+        Dbx data never actually contains 'l'.  */
+    case 'l':
+      SYMBOL_CLASS (sym) = LOC_LOCAL;
+      SYMBOL_VALUE (sym) = value;
+      SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE;
+      add_symbol_to_list (sym, &local_symbols);
+      break;
+
+    case 'p':
+      SYMBOL_CLASS (sym) = LOC_ARG;
+      SYMBOL_VALUE (sym) = value;
+      SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE;
+      add_symbol_to_list (sym, &local_symbols);
+
+      /* If it's gcc compiled, if it says `short', believe it.  */
+      if (processing_gcc_compilation || BELIEVE_PCC_PROMOTION)
+       break;
+
+#if defined(BELIEVE_PCC_PROMOTION_TYPE)
+      /* This macro is defined on machines (e.g. sparc) where
+        we should believe the type of a PCC 'short' argument,
+        but shouldn't believe the address (the address is
+        the address of the corresponding int).  Note that
+        this is only different from the BELIEVE_PCC_PROMOTION
+        case on big-endian machines.
+
+        My guess is that this correction, as opposed to changing
+        the parameter to an 'int' (as done below, for PCC
+        on most machines), is the right thing to do
+        on all machines, but I don't want to risk breaking
+        something that already works.  On most PCC machines,
+        the sparc problem doesn't come up because the calling
+        function has to zero the top bytes (not knowing whether
+        the called function wants an int or a short), so there
+        is no practical difference between an int and a short
+        (except perhaps what happens when the GDB user types
+        "print short_arg = 0x10000;").
+        Hacked for SunOS 4.1 by gnu@cygnus.com.  In 4.1, the compiler
+        actually produces the correct address (we don't need to fix it
+        up).  I made this code adapt so that it will offset the symbol
+        if it was pointing at an int-aligned location and not
+        otherwise.  This way you can use the same gdb for 4.0.x and
+        4.1 systems.  */
+
+      if (0 == SYMBOL_VALUE (sym) % sizeof (int))
+       {
+         if (SYMBOL_TYPE (sym) == builtin_type_char
+             || SYMBOL_TYPE (sym) == builtin_type_unsigned_char)
+           SYMBOL_VALUE (sym) += 3;
+         else if (SYMBOL_TYPE (sym) == builtin_type_short
+             || SYMBOL_TYPE (sym) == builtin_type_unsigned_short)
+           SYMBOL_VALUE (sym) += 2;
+       }
+      break;
+
+#else /* no BELIEVE_PCC_PROMOTION_TYPE.  */
+
+      /* If PCC says a parameter is a short or a char,
+        it is really an int.  */
+      if (SYMBOL_TYPE (sym) == builtin_type_char
+         || SYMBOL_TYPE (sym) == builtin_type_short)
+       SYMBOL_TYPE (sym) = builtin_type_int;
+      else if (SYMBOL_TYPE (sym) == builtin_type_unsigned_char
+              || SYMBOL_TYPE (sym) == builtin_type_unsigned_short)
+       SYMBOL_TYPE (sym) = builtin_type_unsigned_int;
+      break;
+
+#endif /* no BELIEVE_PCC_PROMOTION_TYPE.  */
+
+    case 'P':
+      SYMBOL_CLASS (sym) = LOC_REGPARM;
+      SYMBOL_VALUE (sym) = STAB_REG_TO_REGNUM (value);
+      SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE;
+      add_symbol_to_list (sym, &local_symbols);
+      break;
+
+    case 'r':
+/* XXX */
+#ifdef sparc
+{
+       struct symbol *s0;
+
+       /*
+        * If we see a parm decl immediately followed by a reg decl of
+        * the same name (and in the same block), we change it to a single 
+        * instance of a reg parm.  Sun's cc will generate these.
+        */
+       if (local_symbols && 
+           (s0 = local_symbols->symbol[local_symbols->nsyms - 1]) &&
+           SYMBOL_CLASS(s0) == LOC_ARG && 
+           strcmp(SYMBOL_NAME(s0), SYMBOL_NAME(sym)) == 0) {
+               SYMBOL_CLASS (s0) = LOC_REGPARM;
+               SYMBOL_VALUE (s0) = STAB_REG_TO_REGNUM (value);
+               SYMBOL_NAMESPACE (s0) = VAR_NAMESPACE;
+               return s0;
+       }
+}
+#endif
+      SYMBOL_CLASS (sym) = LOC_REGISTER;
+      SYMBOL_VALUE (sym) = STAB_REG_TO_REGNUM (value);
+      SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE;
+      add_symbol_to_list (sym, &local_symbols);
+      break;
+
+    case 'S':
+      /* Static symbol at top level of file */
+      SYMBOL_CLASS (sym) = LOC_STATIC;
+      SYMBOL_VALUE (sym) = value;
+      SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE;
+      add_symbol_to_list (sym, &file_symbols);
+      break;
+
+    case 't':
+      SYMBOL_CLASS (sym) = LOC_TYPEDEF;
+      SYMBOL_VALUE (sym) = value;
+      SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE;
+      if (TYPE_NAME (SYMBOL_TYPE (sym)) == 0
+         && (TYPE_FLAGS (SYMBOL_TYPE (sym)) & TYPE_FLAG_PERM) == 0)
+       TYPE_NAME (SYMBOL_TYPE (sym)) =
+         obsavestring (SYMBOL_NAME (sym),
+                       strlen (SYMBOL_NAME (sym)));
+       /* C++ vagaries: we may have a type which is derived from
+        a base type which did not have its name defined when the
+        derived class was output.  We fill in the derived class's
+        base part member's name here in that case.  */
+       else if ((TYPE_CODE (SYMBOL_TYPE (sym)) == TYPE_CODE_STRUCT
+                || TYPE_CODE (SYMBOL_TYPE (sym)) == TYPE_CODE_UNION)
+               && TYPE_N_BASECLASSES (SYMBOL_TYPE (sym)))
+        {
+          int i;
+          for (i = TYPE_N_BASECLASSES (SYMBOL_TYPE (sym)); i > 0; i--)
+            if (TYPE_FIELD_NAME (SYMBOL_TYPE (sym), i - 1) == 0)
+              TYPE_FIELD_NAME (SYMBOL_TYPE (sym), i - 1) =
+                TYPE_NAME (TYPE_BASECLASS (SYMBOL_TYPE (sym), i));
+        }
+
+      add_symbol_to_list (sym, &file_symbols);
+      break;
+
+    case 'T':
+      SYMBOL_CLASS (sym) = LOC_TYPEDEF;
+      SYMBOL_VALUE (sym) = value;
+      SYMBOL_NAMESPACE (sym) = STRUCT_NAMESPACE;
+      if (TYPE_NAME (SYMBOL_TYPE (sym)) == 0
+         && (TYPE_FLAGS (SYMBOL_TYPE (sym)) & TYPE_FLAG_PERM) == 0)
+       TYPE_NAME (SYMBOL_TYPE (sym))
+         = obconcat ("",
+                     (TYPE_CODE (SYMBOL_TYPE (sym)) == TYPE_CODE_ENUM
+                      ? "enum "
+                      : (TYPE_CODE (SYMBOL_TYPE (sym)) == TYPE_CODE_STRUCT
+                         ? "struct " : "union ")),
+                     SYMBOL_NAME (sym));
+      add_symbol_to_list (sym, &file_symbols);
+      break;
+
+    case 'V':
+      /* Static symbol of local scope */
+      SYMBOL_CLASS (sym) = LOC_STATIC;
+      SYMBOL_VALUE (sym) = value;
+      SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE;
+      add_symbol_to_list (sym, &local_symbols);
+      break;
+
+    case 'v':
+      /* Reference parameter */
+      SYMBOL_CLASS (sym) = LOC_REF_ARG;
+      SYMBOL_VALUE (sym) = value;
+      SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE;
+      add_symbol_to_list (sym, &local_symbols);
+      break;
+
+    case 'X':
+      /* This is used by Sun FORTRAN for "function result value".
+        Sun claims ("dbx and dbxtool interfaces", 2nd ed)
+        that Pascal uses it too, but when I tried it Pascal used
+        "x:3" (local symbol) instead.  */
+      SYMBOL_CLASS (sym) = LOC_LOCAL;
+      SYMBOL_VALUE (sym) = value;
+      SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE;
+      add_symbol_to_list (sym, &local_symbols);
+      break;
+
+    default:
+      error ("Invalid symbol data: unknown symbol-type code `%c' at symtab pos %d.", deftype, symnum);
+    }
+  return sym;
+}
+\f
+/* What about types defined as forward references inside of a small lexical
+   scope?  */
+/* Add a type to the list of undefined types to be checked through
+   once this file has been read in.  */
+static void
+add_undefined_type (type)
+     struct type *type;
+{
+  if (undef_types_length == undef_types_allocated)
+    {
+      undef_types_allocated *= 2;
+      undef_types = (struct type **)
+       xrealloc (undef_types,
+                 undef_types_allocated * sizeof (struct type *));
+    }
+  undef_types[undef_types_length++] = type;
+}
+
+/* Add here something to go through each undefined type, see if it's
+   still undefined, and do a full lookup if so.  */
+static void
+cleanup_undefined_types ()
+{
+  struct type **type, *ntype;
+  struct symbol *sym;
+
+  for (type = undef_types; type < undef_types + undef_types_length; type++)
+    {
+      struct type *ntype = 0;
+      /* Reasonable test to see if it's been defined since.  */
+      if (TYPE_NFIELDS (*type) == 0)
+       {
+         struct pending *ppt;
+         int i;
+         /* Name of the type, without "struct" or "union" */
+         char *typename = TYPE_NAME (*type);
+
+         if (!strncmp (typename, "struct ", 7))
+           typename += 7;
+         if (!strncmp (typename, "union ", 6))
+           typename += 6;
+
+         for (ppt = file_symbols; ppt; ppt = ppt->next)
+           for (i = 0; i < ppt->nsyms; i++)
+             {
+               struct symbol *sym = ppt->symbol[i];
+
+               if (SYMBOL_CLASS (sym) == LOC_TYPEDEF
+                   && SYMBOL_NAMESPACE (sym) == STRUCT_NAMESPACE
+                   && (TYPE_CODE (SYMBOL_TYPE (sym)) ==
+                       TYPE_CODE (*type))
+                   && !strcmp (SYMBOL_NAME (sym), typename))
+                 bcopy (SYMBOL_TYPE (sym), *type, sizeof (struct type));
+             }
+       }
+      else
+       /* It has been defined; don't mark it as a stub.  */
+       TYPE_FLAGS (*type) &= ~TYPE_FLAG_STUB;
+    }
+  undef_types_length = 0;
+}
+
+
+\f
+/* Read a dbx type reference or definition;
+   return the type that is meant.
+   This can be just a number, in which case it references
+   a type already defined and placed in type_vector.
+   Or the number can be followed by an =, in which case
+   it means to define a new type according to the text that
+   follows the =.  */
+
+static
+struct type *
+read_type (pp)
+     register char **pp;
+{
+  register struct type *type = 0;
+  register int n;
+  struct type *type1;
+  int typenums[2];
+  int xtypenums[2];
+  char *tmpc;
+
+  /* Read type number if present.  The type number may be omitted.
+     for instance in a two-dimensional array declared with type
+     "ar1;1;10;ar1;1;10;4".  */
+  if ((**pp >= '0' && **pp <= '9')
+      || **pp == '(')
+    {
+      read_type_number (pp, typenums);
+      
+      /* Detect random reference to type not yet defined.
+        Allocate a type object but leave it zeroed.  */
+      if (**pp != '=')
+       return dbx_alloc_type (typenums);
+
+      *pp += 2;
+    }
+  else
+    {
+      /* 'typenums=' not present, type is anonymous.  Read and return
+        the definition, but don't put it in the type vector.  */
+      typenums[0] = typenums[1] = -1;
+      *pp += 1;
+    }
+      
+  switch ((*pp)[-1])
+    {
+    case 'x':
+      {
+       enum type_code code;
+
+       /* Used to index through file_symbols.  */
+       struct pending *ppt;
+       int i;
+       
+       /* Name including "struct", etc.  */
+       char *type_name;
+       
+       /* Name without "struct", etc.  */
+       char *type_name_only;
+
+       {
+         char *prefix;
+         char *from, *to;
+         
+         /* Set the type code according to the following letter.  */
+         switch ((*pp)[0])
+           {
+           case 's':
+             code = TYPE_CODE_STRUCT;
+             prefix = "struct ";
+             break;
+           case 'u':
+             code = TYPE_CODE_UNION;
+             prefix = "union ";
+             break;
+           case 'e':
+             code = TYPE_CODE_ENUM;
+             prefix = "enum ";
+             break;
+           default:
+             error ("Bad type cross reference at symnum: %d.", symnum);
+           }
+         
+         to = type_name = (char *)
+           obstack_alloc (symbol_obstack,
+                          (strlen (prefix) +
+                           ((char *) index (*pp, ':') - (*pp)) + 1));
+       
+         /* Copy the prefix.  */
+         from = prefix;
+         while (*to++ = *from++)
+           ;
+         to--; 
+       
+         type_name_only = to;
+
+         /* Copy the name.  */
+         from = *pp + 1;
+         while ((*to++ = *from++) != ':')
+           ;
+         *--to = '\0';
+         
+         /* Set the pointer ahead of the name which we just read.  */
+         *pp = from;
+       
+#if 0
+         /* The following hack is clearly wrong, because it doesn't
+            check whether we are in a baseclass.  I tried to reproduce
+            the case that it is trying to fix, but I couldn't get
+            g++ to put out a cross reference to a basetype.  Perhaps
+            it doesn't do it anymore.  */
+         /* Note: for C++, the cross reference may be to a base type which
+            has not yet been seen.  In this case, we skip to the comma,
+            which will mark the end of the base class name.  (The ':'
+            at the end of the base class name will be skipped as well.)
+            But sometimes (ie. when the cross ref is the last thing on
+            the line) there will be no ','.  */
+         from = (char *) index (*pp, ',');
+         if (from)
+           *pp = from;
+#endif /* 0 */
+       }
+
+       /* Now check to see whether the type has already been declared.  */
+       /* This is necessary at least in the case where the
+          program says something like
+            struct foo bar[5];
+          The compiler puts out a cross-reference; we better find
+          set the length of the structure correctly so we can
+          set the length of the array.  */
+       for (ppt = file_symbols; ppt; ppt = ppt->next)
+         for (i = 0; i < ppt->nsyms; i++)
+           {
+             struct symbol *sym = ppt->symbol[i];
+
+             if (SYMBOL_CLASS (sym) == LOC_TYPEDEF
+                 && SYMBOL_NAMESPACE (sym) == STRUCT_NAMESPACE
+                 && (TYPE_CODE (SYMBOL_TYPE (sym)) == code)
+                 && !strcmp (SYMBOL_NAME (sym), type_name_only))
+               {
+                 obstack_free (symbol_obstack, type_name);
+                 type = SYMBOL_TYPE (sym);
+                 return type;
+               }
+           }
+       
+       /* Didn't find the type to which this refers, so we must
+          be dealing with a forward reference.  Allocate a type
+          structure for it, and keep track of it so we can
+          fill in the rest of the fields when we get the full
+          type.  */
+       type = dbx_alloc_type (typenums);
+       TYPE_CODE (type) = code;
+       TYPE_NAME (type) = type_name;
+
+       TYPE_FLAGS (type) |= TYPE_FLAG_STUB;
+
+       add_undefined_type (type);
+       return type;
+      }
+
+    case '0':
+    case '1':
+    case '2':
+    case '3':
+    case '4':
+    case '5':
+    case '6':
+    case '7':
+    case '8':
+    case '9':
+    case '(':
+      (*pp)--;
+      read_type_number (pp, xtypenums);
+      type = *dbx_lookup_type (xtypenums);
+      if (type == 0)
+       type = builtin_type_void;
+      if (typenums[0] != -1)
+       *dbx_lookup_type (typenums) = type;
+      break;
+
+    case '*':
+      type1 = read_type (pp);
+      if (TYPE_POINTER_TYPE (type1))
+       {
+         type = TYPE_POINTER_TYPE (type1);
+         if (typenums[0] != -1)
+           *dbx_lookup_type (typenums) = type;
+       }
+      else
+       {
+         type = dbx_alloc_type (typenums);
+         smash_to_pointer_type (type, type1);
+       }
+      break;
+
+    case '@':
+      {
+       struct type *domain = read_type (pp);
+       char c;
+       struct type *memtype;
+
+       if (*(*pp)++ != ',')
+         error ("invalid member type data format, at symtab pos %d.",
+                symnum);
+
+       memtype = read_type (pp);
+       type = dbx_alloc_type (typenums);
+       smash_to_member_type (type, domain, memtype);
+      }
+      break;
+
+    case '#':
+      {
+       struct type *domain = read_type (pp);
+       char c;
+       struct type *return_type;
+       struct type **args;
+
+       if (*(*pp)++ != ',')
+         error ("invalid member type data format, at symtab pos %d.",
+                symnum);
+
+       return_type = read_type (pp);
+       args = read_args (pp, ';');
+       type = dbx_alloc_type (typenums);
+       smash_to_method_type (type, domain, return_type, args);
+      }
+      break;
+
+    case '&':
+      type1 = read_type (pp);
+      if (TYPE_REFERENCE_TYPE (type1))
+       {
+         type = TYPE_REFERENCE_TYPE (type1);
+         if (typenums[0] != -1)
+           *dbx_lookup_type (typenums) = type;
+       }
+      else
+       {
+         type = dbx_alloc_type (typenums);
+         smash_to_reference_type (type, type1);
+       }
+      break;
+
+    case 'f':
+      type1 = read_type (pp);
+      if (TYPE_FUNCTION_TYPE (type1))
+       {
+         type = TYPE_FUNCTION_TYPE (type1);
+         if (typenums[0] != -1)
+           *dbx_lookup_type (typenums) = type;
+       }
+      else
+       {
+         type = dbx_alloc_type (typenums);
+         smash_to_function_type (type, type1);
+       }
+      break;
+
+    case 'r':
+      type = read_range_type (pp, typenums);
+      if (typenums[0] != -1)
+       *dbx_lookup_type (typenums) = type;
+      break;
+
+    case 'e':
+      type = dbx_alloc_type (typenums);
+      type = read_enum_type (pp, type);
+      *dbx_lookup_type (typenums) = type;
+      break;
+
+    case 's':
+      type = dbx_alloc_type (typenums);
+      type = read_struct_type (pp, type);
+      break;
+
+    case 'u':
+      type = dbx_alloc_type (typenums);
+      type = read_struct_type (pp, type);
+      TYPE_CODE (type) = TYPE_CODE_UNION;
+      break;
+
+    case 'a':
+      if (*(*pp)++ != 'r')
+       error ("Invalid symbol data: unrecognized type-code `a%c' %s %d.",
+              (*pp)[-1], "at symtab position", symnum);
+
+      type = dbx_alloc_type (typenums);
+      type = read_array_type (pp, type);
+      break;
+
+    default:
+      error ("Invalid symbol data: unrecognized type-code `%c' at symtab pos %d.",
+            (*pp)[-1], symnum);
+    }
+
+  if (type == 0)
+    abort ();
+
+#if 0
+  /* If this is an overriding temporary alteration for a header file's
+     contents, and this type number is unknown in the global definition,
+     put this type into the global definition at this type number.  */
+  if (header_file_prev_index >= 0)
+    {
+      register struct type **tp
+        = explicit_lookup_type (header_file_prev_index, typenums[1]);
+      if (*tp == 0)
+       *tp = type;
+    }
+#endif
+  return type;
+}
+\f
+/* This page contains subroutines of read_type.  */
+
+/* Read the description of a structure (or union type)
+   and return an object describing the type.  */
+
+static struct type *
+read_struct_type (pp, type)
+     char **pp;
+     register struct type *type;
+{
+  struct nextfield
+    {
+      struct nextfield *next;
+      int visibility;
+      struct field field;
+    };
+
+  struct next_fnfield
+    {
+      struct next_fnfield *next;
+      int visibility;
+      struct fn_field fn_field;
+    };
+
+  struct next_fnfieldlist
+    {
+      struct next_fnfieldlist *next;
+      struct fn_fieldlist fn_fieldlist;
+    };
+
+  register struct nextfield *list = 0;
+  struct nextfield *new;
+  int totalsize;
+  char *name;
+  register char *p;
+  int nfields = 0;
+  register int n;
+
+  register struct next_fnfieldlist *mainlist = 0;
+  int nfn_fields = 0;
+  int read_possible_virtual_info = 0;
+
+  if (TYPE_MAIN_VARIANT (type) == 0)
+    {
+      TYPE_MAIN_VARIANT (type) = type;
+    }
+
+  TYPE_CODE (type) = TYPE_CODE_STRUCT;
+
+  /* First comes the total size in bytes.  */
+
+  TYPE_LENGTH (type) = read_number (pp, 0);
+
+  /* C++: Now, if the class is a derived class, then the next character
+     will be a '!', followed by the number of base classes derived from.
+     Each element in the list contains visibility information,
+     the offset of this base class in the derived structure,
+     and then the base type. */
+  if (**pp == '!')
+    {
+      int i, n_baseclasses, offset;
+      struct type **baseclass_vec;
+      struct type *baseclass;
+      int via_public;
+
+      /* Nonzero if it is a virtual baseclass, i.e.,
+
+        struct A{};
+        struct B{};
+        struct C : public B, public virtual A {};
+
+        B is a baseclass of C; A is a virtual baseclass for C.  This is a C++
+        2.0 language feature.  */
+      int via_virtual;
+
+      *pp += 1;
+
+      n_baseclasses = read_number (pp, ',');
+      baseclass_vec = (struct type **)
+       obstack_alloc (symbol_obstack,
+                      (n_baseclasses) * sizeof (struct type **)) - 1;
+
+      for (i = 1; i <= n_baseclasses; i++)
+       {
+         if (**pp == '\\')
+           *pp = next_symbol_text ();
+
+         switch (*(*pp)++)
+           {
+           case '0':
+             via_virtual = 0;
+             break;
+           case '1':
+             via_virtual = 1;
+             break;
+           default:
+             error ("Invalid symbol data: bad visibility format at symtab pos %d",
+                    symnum);
+           }
+
+         switch (*(*pp)++)
+           {
+           case '0':
+             via_public = 0;
+             break;
+           case '2':
+             via_public = 1;
+             break;
+           default:
+             error ("Invalid symbol data: bad visibility format at symtab pos %d.",
+                    symnum);
+           }
+
+         /* Offset of the portion of the object corresponding to
+            this baseclass.  Always zero in the absence of
+            multiple inheritance.  */
+         offset = read_number (pp, ',');
+         baseclass = read_type (pp);
+         *pp += 1;             /* skip trailing ';' */
+
+         if (offset != 0)
+           {
+             static int error_printed = 0;
+
+             if (!error_printed)
+               {
+                 fprintf (stderr, 
+"\nWarning:  GDB has limited understanding of multiple inheritance...");
+                 error_printed = 1;
+               }
+             offset = 0;
+           }
+
+         baseclass_vec[i] = lookup_basetype_type (baseclass, offset, via_virtual, via_public);
+
+          /* Since lookup_basetype_type can copy the type,
+            it might copy a stub type (complete with stub flag).
+            If so, we need to add it to the list of undefined types
+            to clean up later.  Even if lookup_basetype_type
+            didn't copy the type, adding it to the undefined list
+            will not do any harm.  */
+          if (TYPE_FLAGS(baseclass_vec[i]) & TYPE_FLAG_STUB)
+            add_undefined_type (baseclass_vec[i]);
+
+         /* Make this baseclass visible for structure-printing purposes.  */
+         new = (struct nextfield *) alloca (sizeof (struct nextfield));
+         new->next = list;
+         list = new;
+         list->field.type = baseclass_vec[i];
+         list->field.name = TYPE_NAME (baseclass_vec[i]);
+         list->field.bitpos = offset;
+         list->field.bitsize = 0;      /* this should be an unpacked field! */
+         nfields++;
+       }
+      TYPE_N_BASECLASSES (type) = n_baseclasses;
+      TYPE_BASECLASSES (type) = baseclass_vec;
+    }
+
+  /* Now come the fields, as NAME:?TYPENUM,BITPOS,BITSIZE; for each one.
+     At the end, we see a semicolon instead of a field.
+
+     In C++, this may wind up being NAME:?TYPENUM:PHYSNAME; for
+     a static field.
+
+     The `?' is a placeholder for one of '+' (public visibility),
+     '0' (protected visibility), and '-' (private visibility).  */
+
+  /* We better set p right now, in case there are no fields at all...    */
+  p = *pp;
+
+  while (**pp != ';')
+    {
+      int visibility;
+
+      /* Check for and handle cretinous dbx symbol name continuation!  */
+      if (**pp == '\\') *pp = next_symbol_text ();
+
+      /* Get space to record the next field's data.  */
+      new = (struct nextfield *) alloca (sizeof (struct nextfield));
+      new->next = list;
+      list = new;
+
+      /* Get the field name.  */
+      p = *pp;
+      while (*p != ':') p++;
+      list->field.name = obsavestring (*pp, p - *pp);
+
+      /* C++: Check to see if we have hit the methods yet.  */
+      if (p[1] == ':')
+       break;
+
+      *pp = p + 1;
+
+      /* This means we have a visibility for a field coming. */
+      if (**pp == '/')
+       {
+         switch (*++*pp)
+           {
+           case '0':
+             visibility = 0;
+             *pp += 1;
+             break;
+
+           case '1':
+             visibility = 1;
+             *pp += 1;
+             break;
+
+           case '2':
+             visibility = 2;
+             *pp += 1;
+             break;
+           }
+       }
+       /* else normal dbx-style format.  */
+
+      list->field.type = read_type (pp);
+      if (**pp == ':')
+       {
+         list->field.bitpos = (long)-1;
+         p = ++(*pp);
+         while (*p != ';') p++;
+         list->field.bitsize = (long) savestring (*pp, p - *pp);
+         *pp = p + 1;
+         nfields++;
+         continue;
+       }
+       else if (**pp != ',')
+        error ("Invalid symbol data: bad structure-type format at symtab pos %d.",
+              symnum);
+      (*pp)++;                 /* Skip the comma.  */
+      list->field.bitpos = read_number (pp, ',');
+      list->field.bitsize = read_number (pp, ';');
+
+#if 0
+      /* This is wrong because this is identical to the symbols
+        produced for GCC 0-size arrays.  For example:
+         typedef union {
+          int num;
+          char str[0];
+        } foo;
+        The code which dumped core in such circumstances should be
+        fixed not to dump core.  */
+
+      /* g++ -g0 can put out bitpos & bitsize zero for a static
+        field.  This does not give us any way of getting its
+        class, so we can't know its name.  But we can just
+        ignore the field so we don't dump core and other nasty
+        stuff.  */
+      if (list->field.bitpos == 0
+         && list->field.bitsize == 0)
+       {
+         /* Have we given the warning yet?  */
+         static int warning_given = 0;
+
+         /* Only give the warning once, no matter how many class
+            variables there are.  */
+         if (!warning_given)
+           {
+             warning_given = 1;
+             fprintf_filtered (stderr, "\n\
+Warning:  DBX-style class variable debugging information encountered.\n\
+You seem to have compiled your program with \
+\"g++ -g0\" instead of \"g++ -g\".\n\
+Therefore GDB will not know about your class variables.\n\
+");
+           }
+
+         /* Ignore this field.  */
+         list = list->next;
+       }
+      else
+#endif /* 0 */
+       {
+         /* Detect an unpacked field and mark it as such.
+            dbx gives a bit size for all fields.
+            Note that forward refs cannot be packed,
+            and treat enums as if they had the width of ints.  */
+         if (TYPE_CODE (list->field.type) != TYPE_CODE_INT
+             && TYPE_CODE (list->field.type) != TYPE_CODE_ENUM)
+           list->field.bitsize = 0;
+         if ((list->field.bitsize == 8 * TYPE_LENGTH (list->field.type)
+              || (TYPE_CODE (list->field.type) == TYPE_CODE_ENUM
+                  && (list->field.bitsize
+                      == 8 * TYPE_LENGTH (builtin_type_int))
+                  )
+              )
+             &&
+             list->field.bitpos % 8 == 0)
+           list->field.bitsize = 0;
+         nfields++;
+       }
+    }
+
+  /* Now come the method fields, as NAME::methods
+     where each method is of the form TYPENUM,ARGS,...:PHYSNAME;
+     At the end, we see a semicolon instead of a field.
+
+     For the case of overloaded operators, the format is
+     OPERATOR::*.methods, where OPERATOR is the string "operator",
+     `*' holds the place for an operator name (such as `+=')
+     and `.' marks the end of the operator name.  */
+  if (p[1] == ':')
+    {
+      /* Now, read in the methods.  To simplify matters, we
+        "unread" the name that has been read, so that we can
+        start from the top.  */
+
+      p = *pp;
+
+      /* chill the list of fields: the last entry (at the head)
+         is a partially constructed entry which we now scrub.  */
+      list = list->next;
+
+      /* For each list of method lists... */
+      do
+       {
+         int i;
+         struct next_fnfield *sublist = 0;
+         struct fn_field *fn_fields = 0;
+         int length = 0;
+         struct next_fnfieldlist *new_mainlist =
+           (struct next_fnfieldlist *)alloca (sizeof (struct next_fnfieldlist));
+
+         /* read in the name.  */
+         while (*p != ':') p++;
+         if ((*pp)[0] == 'o' && (*pp)[1] == 'p' && (*pp)[2] == '$')
+           {
+             static char opname[] = "operator";
+             char *o = opname + strlen(opname);
+
+             /* Skip past '::'.  */
+             p += 2;
+             while (*p != '.')
+               *o++ = *p++;
+             new_mainlist->fn_fieldlist.name = savestring (opname, o - opname);
+             /* Skip past '.'  */
+             *pp = p + 1;
+           }
+         else
+           {
+             i = 0;
+             new_mainlist->fn_fieldlist.name = savestring (*pp, p - *pp);
+             /* Skip past '::'.  */
+             *pp = p + 2;
+           }
+
+         do
+           {
+             struct next_fnfield *new_sublist =
+               (struct next_fnfield *)alloca (sizeof (struct next_fnfield));
+
+             /* Check for and handle cretinous dbx symbol name continuation!  */
+             if (**pp == '\\') *pp = next_symbol_text ();
+
+             new_sublist->fn_field.type = read_type (pp);
+             if (**pp != ':')
+               error ("invalid symtab info for method at symbol number %d.",
+                      symnum);
+             *pp += 1;
+             new_sublist->fn_field.args =
+               TYPE_ARG_TYPES (new_sublist->fn_field.type);
+             p = *pp;
+             while (*p != ';') p++;
+             new_sublist->fn_field.physname = savestring (*pp, p - *pp);
+             *pp = p + 1;
+             new_sublist->visibility = *(*pp)++ - '0';
+             if (**pp == '\\') *pp = next_symbol_text ();
+
+             switch (*(*pp)++)
+               {
+               case '*':
+                 /* virtual member function, followed by index.  */
+                 new_sublist->fn_field.voffset = read_number (pp, ';') + 1;
+                 break;
+               case '?':
+                 /* static member function.  */
+                 new_sublist->fn_field.voffset = 1;
+                 break;
+               default:
+                 /* **pp == '.'.  */
+                 /* normal member function.  */
+                 new_sublist->fn_field.voffset = 0;
+                 break;
+               }
+
+             new_sublist->next = sublist;
+             sublist = new_sublist;
+             length++;
+           }
+         while (**pp != ';');
+
+         *pp += 1;
+
+         new_mainlist->fn_fieldlist.fn_fields =
+           (struct fn_field *) obstack_alloc (symbol_obstack,
+                                              sizeof (struct fn_field) * length);
+         TYPE_FN_PRIVATE_BITS (new_mainlist->fn_fieldlist) =
+           (int *) obstack_alloc (symbol_obstack,
+                                  sizeof (int) * (1 + (length >> 5)));
+
+         TYPE_FN_PROTECTED_BITS (new_mainlist->fn_fieldlist) =
+           (int *) obstack_alloc (symbol_obstack,
+                                  sizeof (int) * (1 + (length >> 5)));
+
+         for (i = length; sublist; sublist = sublist->next)
+           {
+             new_mainlist->fn_fieldlist.fn_fields[--i] = sublist->fn_field;
+             if (sublist->visibility == 0)
+               B_SET (new_mainlist->fn_fieldlist.private_fn_field_bits, i);
+             else if (sublist->visibility == 1)
+               B_SET (new_mainlist->fn_fieldlist.protected_fn_field_bits, i);
+           }
+
+         new_mainlist->fn_fieldlist.length = length;
+         new_mainlist->next = mainlist;
+         mainlist = new_mainlist;
+         nfn_fields++;
+       }
+      while (**pp != ';');
+    }
+
+  *pp += 1;
+
+  /* Now create the vector of fields, and record how big it is.  */
+
+  TYPE_NFIELDS (type) = nfields;
+  TYPE_FIELDS (type) = (struct field *) obstack_alloc (symbol_obstack,
+                                                      sizeof (struct field) * nfields);
+  TYPE_FIELD_PRIVATE_BITS (type) =
+    (int *) obstack_alloc (symbol_obstack,
+                          sizeof (int) * (1 + (nfields >> 5)));
+  TYPE_FIELD_PROTECTED_BITS (type) =
+    (int *) obstack_alloc (symbol_obstack,
+                          sizeof (int) * (1 + (nfields >> 5)));
+
+  TYPE_NFN_FIELDS (type) = nfn_fields;
+  TYPE_NFN_FIELDS_TOTAL (type) = nfn_fields;
+
+  {
+    int i;
+    for (i = 1; i <= TYPE_N_BASECLASSES (type); ++i)
+      TYPE_NFN_FIELDS_TOTAL (type) +=
+       TYPE_NFN_FIELDS_TOTAL (TYPE_BASECLASS (type, i));
+  }
+
+  TYPE_FN_FIELDLISTS (type) =
+    (struct fn_fieldlist *) obstack_alloc (symbol_obstack,
+                                          sizeof (struct fn_fieldlist) * nfn_fields);
+
+  /* Copy the saved-up fields into the field vector.  */
+
+  for (n = nfields; list; list = list->next)
+    {
+      TYPE_FIELD (type, --n) = list->field;
+      if (list->visibility == 0)
+       SET_TYPE_FIELD_PRIVATE (type, n);
+      else if (list->visibility == 1)
+       SET_TYPE_FIELD_PROTECTED (type, n);
+    }
+
+  for (n = nfn_fields; mainlist; mainlist = mainlist->next)
+    TYPE_FN_FIELDLISTS (type)[--n] = mainlist->fn_fieldlist;
+
+  if (**pp == '~')
+    {
+      *pp += 1;
+
+      if (**pp == '=')
+       {
+         TYPE_FLAGS (type)
+           |= TYPE_FLAG_HAS_CONSTRUCTOR | TYPE_FLAG_HAS_DESTRUCTOR;
+         *pp += 1;
+       }
+      else if (**pp == '+')
+       {
+         TYPE_FLAGS (type) |= TYPE_FLAG_HAS_CONSTRUCTOR;
+         *pp += 1;
+       }
+      else if (**pp == '-')
+       {
+         TYPE_FLAGS (type) |= TYPE_FLAG_HAS_DESTRUCTOR;
+         *pp += 1;
+       }
+
+      /* Read either a '%' or the final ';'.  */
+      if (*(*pp)++ == '%')
+       {
+         /* Now we must record the virtual function table pointer's
+            field information.  */
+
+         struct type *t;
+         int i;
+
+         t = read_type (pp);
+         p = (*pp)++;
+         while (*p != ';') p++;
+         TYPE_VPTR_BASETYPE (type) = t;
+         if (type == t)
+           {
+             if (TYPE_FIELD_NAME (t, 0) == 0)
+               TYPE_VPTR_FIELDNO (type) = i = 0;
+             else for (i = TYPE_NFIELDS (t) - 1; i >= 0; --i)
+               if (! strncmp (TYPE_FIELD_NAME (t, i), *pp,
+                              strlen (TYPE_FIELD_NAME (t, i))))
+                 {
+                   TYPE_VPTR_FIELDNO (type) = i;
+                   break;
+                 }
+             if (i < 0)
+               error ("virtual function table field not found");
+           }
+         else
+           TYPE_VPTR_FIELDNO (type) = TYPE_VPTR_FIELDNO (TYPE_BASECLASS (type, 1));
+         *pp = p + 1;
+       }
+      else
+       {
+         TYPE_VPTR_BASETYPE (type) = 0;
+         TYPE_VPTR_FIELDNO (type) = -1;
+       }
+    }
+  else
+    {
+      TYPE_VPTR_BASETYPE (type) = 0;
+      TYPE_VPTR_FIELDNO (type) = -1;
+    }
+
+  return type;
+}
+
+/* Read a definition of an array type,
+   and create and return a suitable type object.
+   Also creates a range type which represents the bounds of that
+   array.  */
+static struct type *
+read_array_type (pp, type)
+     register char **pp;
+     register struct type *type;
+{
+  struct type *index_type, *element_type, *range_type;
+  int lower, upper;
+  int adjustable = 0;
+
+  /* Format of an array type:
+     "ar<index type>;lower;upper;<array_contents_type>".  Put code in
+     to handle this.
+
+     Fortran adjustable arrays use Adigits or Tdigits for lower or upper;
+     for these, produce a type like float[][].  */
+
+  index_type = read_type (pp);
+  if (*(*pp)++ != ';')
+    error ("Invalid symbol data; improper format of array type decl.");
+
+  if (!(**pp >= '0' && **pp <= '9'))
+    {
+      *pp += 1;
+      adjustable = 1;
+    }
+  lower = read_number (pp, ';');
+
+  if (!(**pp >= '0' && **pp <= '9'))
+    {
+      *pp += 1;
+      adjustable = 1;
+    }
+  upper = read_number (pp, ';');
+  
+  element_type = read_type (pp);
+
+  if (adjustable)
+    {
+      lower = 0;
+      upper = -1;
+    }
+
+  {
+    /* Create range type.  */
+    range_type = (struct type *) obstack_alloc (symbol_obstack,
+                                               sizeof (struct type));
+    TYPE_CODE (range_type) = TYPE_CODE_RANGE;
+    TYPE_TARGET_TYPE (range_type) = index_type;
+
+    /* This should never be needed.  */
+    TYPE_LENGTH (range_type) = sizeof (int);
+
+    TYPE_NFIELDS (range_type) = 2;
+    TYPE_FIELDS (range_type) =
+      (struct field *) obstack_alloc (symbol_obstack,
+                                     2 * sizeof (struct field));
+    TYPE_FIELD_BITPOS (range_type, 0) = lower;
+    TYPE_FIELD_BITPOS (range_type, 1) = upper;
+  }
+
+  TYPE_CODE (type) = TYPE_CODE_ARRAY;
+  TYPE_TARGET_TYPE (type) = element_type;
+  TYPE_LENGTH (type) = (upper - lower + 1) * TYPE_LENGTH (element_type);
+  TYPE_NFIELDS (type) = 1;
+  TYPE_FIELDS (type) =
+    (struct field *) obstack_alloc (symbol_obstack,
+                                   sizeof (struct field));
+  TYPE_FIELD_TYPE (type, 0) = range_type;
+
+  return type;
+}
+
+
+/* Read a definition of an enumeration type,
+   and create and return a suitable type object.
+   Also defines the symbols that represent the values of the type.  */
+
+static struct type *
+read_enum_type (pp, type)
+     register char **pp;
+     register struct type *type;
+{
+  register char *p;
+  char *name;
+  register long n;
+  register struct symbol *sym;
+  int nsyms = 0;
+  struct pending **symlist;
+  struct pending *osyms, *syms;
+  int o_nsyms;
+
+  if (within_function)
+    symlist = &local_symbols;
+  else
+    symlist = &file_symbols;
+  osyms = *symlist;
+  o_nsyms = osyms ? osyms->nsyms : 0;
+
+  /* Read the value-names and their values.
+     The input syntax is NAME:VALUE,NAME:VALUE, and so on.
+     A semicolon or comman instead of a NAME means the end.  */
+  while (**pp && **pp != ';' && **pp != ',')
+    {
+      /* Check for and handle cretinous dbx symbol name continuation!  */
+      if (**pp == '\\')        *pp = next_symbol_text ();
+
+      p = *pp;
+      while (*p != ':') p++;
+      name = obsavestring (*pp, p - *pp);
+      *pp = p + 1;
+      n = read_number (pp, ',');
+
+      sym = (struct symbol *) obstack_alloc (symbol_obstack, sizeof (struct symbol));
+      bzero (sym, sizeof (struct symbol));
+      SYMBOL_NAME (sym) = name;
+      SYMBOL_CLASS (sym) = LOC_CONST;
+      SYMBOL_NAMESPACE (sym) = VAR_NAMESPACE;
+      SYMBOL_VALUE (sym) = n;
+      add_symbol_to_list (sym, symlist);
+      nsyms++;
+    }
+
+  if (**pp == ';')
+    (*pp)++;                   /* Skip the semicolon.  */
+
+  /* Now fill in the fields of the type-structure.  */
+
+  TYPE_LENGTH (type) = sizeof (int);
+  TYPE_CODE (type) = TYPE_CODE_ENUM;
+  TYPE_NFIELDS (type) = nsyms;
+  TYPE_FIELDS (type) = (struct field *) obstack_alloc (symbol_obstack, sizeof (struct field) * nsyms);
+
+  /* Find the symbols for the values and put them into the type.
+     The symbols can be found in the symlist that we put them on
+     to cause them to be defined.  osyms contains the old value
+     of that symlist; everything up to there was defined by us.  */
+  /* Note that we preserve the order of the enum constants, so
+     that in something like "enum {FOO, LAST_THING=FOO}" we print
+     FOO, not LAST_THING.  */
+
+  for (syms = *symlist, n = 0; syms; syms = syms->next)
+    {
+      int j = 0;
+      if (syms == osyms)
+       j = o_nsyms;
+      for (; j < syms->nsyms; j++)
+       {
+         struct symbol *sym = syms->symbol[j];
+         SYMBOL_TYPE (sym) = type;
+         TYPE_FIELD_NAME (type, n) = SYMBOL_NAME (sym);
+         TYPE_FIELD_VALUE (type, n) = 0;
+         TYPE_FIELD_BITPOS (type, n) = SYMBOL_VALUE (sym);
+         TYPE_FIELD_BITSIZE (type, n++) = 0;
+       }
+      if (syms == osyms)
+       break;
+    }
+
+  return type;
+}
+
+#define        MAX_OF_TYPE(t)  ((1 << (sizeof (t) - 1)) - 1)
+#define MIN_OF_TYPE(t) (-(1 << (sizeof (t) - 1)))
+
+static struct type *
+read_range_type (pp, typenums)
+     char **pp;
+     int typenums[2];
+{
+  char *errp = *pp;
+  int rangenums[2];
+  int n2, n3;
+  int self_subrange;
+  struct type *result_type;
+
+  /* First comes a type we are a subrange of.
+     In C it is usually 0, 1 or the type being defined.  */
+  read_type_number (pp, rangenums);
+  self_subrange = (rangenums[0] == typenums[0] &&
+                  rangenums[1] == typenums[1]);
+
+  /* A semicolon should now follow; skip it.  */
+  if (**pp == ';')
+    (*pp)++;
+
+  /* The remaining two operands are usually lower and upper bounds
+     of the range.  But in some special cases they mean something else.  */
+  n2 = read_number (pp, ';');
+  n3 = read_number (pp, ';');
+
+  /* A type defined as a subrange of itself, with bounds both 0, is void.  */
+  if (self_subrange && n2 == 0 && n3 == 0)
+    return builtin_type_void;
+
+  /* If n3 is zero and n2 is not, we want a floating type,
+     and n2 is the width in bytes.
+
+     Fortran programs appear to use this for complex types also,
+     and they give no way to distinguish between double and single-complex!
+     We don't have complex types, so we would lose on all fortran files!
+     So return type `double' for all of those.  It won't work right
+     for the complex values, but at least it makes the file loadable.  */
+
+  if (n3 == 0 && n2 > 0)
+    {
+      if (n2 == sizeof (float))
+       return builtin_type_float;
+      return builtin_type_double;
+    }
+
+  /* If the upper bound is -1, it must really be an unsigned int.  */
+
+  else if (n2 == 0 && n3 == -1)
+    {
+      if (sizeof (int) == sizeof (long))
+       return builtin_type_unsigned_int;
+      else
+       return builtin_type_unsigned_long;
+    }
+
+  /* Special case: char is defined (Who knows why) as a subrange of
+     itself with range 0-127.  */
+  else if (self_subrange && n2 == 0 && n3 == 127)
+    return builtin_type_char;
+
+  /* Assumptions made here: Subrange of self is equivalent to subrange
+     of int.  */
+  else if (n2 == 0
+          && (self_subrange ||
+              *dbx_lookup_type (rangenums) == builtin_type_int))
+    {
+      /* an unsigned type */
+#ifdef LONG_LONG
+      if (n3 == - sizeof (long long))
+       return builtin_type_unsigned_long_long;
+#endif
+      if (n3 == (1 << (8 * sizeof (int))) - 1)
+       return builtin_type_unsigned_int;
+      if (n3 == (1 << (8 * sizeof (short))) - 1)
+       return builtin_type_unsigned_short;
+      if (n3 == (1 << (8 * sizeof (char))) - 1)
+       return builtin_type_unsigned_char;
+    }
+#ifdef LONG_LONG
+  else if (n3 == 0 && n2 == -sizeof (long long))
+    return builtin_type_long_long;
+#endif  
+  else if (n2 == -n3 -1)
+    {
+      /* a signed type */
+      if (n3 == (1 << (8 * sizeof (int) - 1)) - 1)
+       return builtin_type_int;
+      if (n3 == (1 << (8 * sizeof (long) - 1)) - 1)
+        return builtin_type_long;
+      if (n3 == (1 << (8 * sizeof (short) - 1)) - 1)
+       return builtin_type_short;
+      if (n3 == (1 << (8 * sizeof (char) - 1)) - 1)
+       return builtin_type_char;
+    }
+
+  /* We have a real range type on our hands.  Allocate space and
+     return a real pointer.  */
+
+  /* At this point I don't have the faintest idea how to deal with
+     a self_subrange type; I'm going to assume that this is used
+     as an idiom, and that all of them are special cases.  So . . .  */
+  if (self_subrange)
+    error ("Type defined as subrange of itself.");
+
+  result_type = (struct type *) obstack_alloc (symbol_obstack,
+                                              sizeof (struct type));
+  bzero (result_type, sizeof (struct type));
+
+  TYPE_TARGET_TYPE (result_type) = (self_subrange ?
+                                   builtin_type_int :
+                                   *dbx_lookup_type(rangenums));
+
+  /* We have to figure out how many bytes it takes to hold this
+     range type.  I'm going to assume that anything that is pushing
+     the bounds of a long was taken care of above.  */
+  if (n2 >= MIN_OF_TYPE(char) && n3 <= MAX_OF_TYPE(char))
+    TYPE_LENGTH (result_type) = 1;
+  else if (n2 >= MIN_OF_TYPE(short) && n3 <= MAX_OF_TYPE(short))
+    TYPE_LENGTH (result_type) = sizeof (short);
+  else if (n2 >= MIN_OF_TYPE(int) && n3 <= MAX_OF_TYPE(int))
+    TYPE_LENGTH (result_type) = sizeof (int);
+  else if (n2 >= MIN_OF_TYPE(long) && n3 <= MAX_OF_TYPE(long))
+    TYPE_LENGTH (result_type) = sizeof (long);
+  else
+    error ("Ranged type doesn't fit within known sizes.");
+
+  TYPE_LENGTH (result_type) = TYPE_LENGTH (TYPE_TARGET_TYPE (result_type));
+  TYPE_CODE (result_type) = TYPE_CODE_RANGE;
+  TYPE_NFIELDS (result_type) = 2;
+  TYPE_FIELDS (result_type) =
+    (struct field *) obstack_alloc (symbol_obstack,
+                                   2 * sizeof (struct field));
+  bzero (TYPE_FIELDS (result_type), 2 * sizeof (struct field));
+  TYPE_FIELD_BITPOS (result_type, 0) = n2;
+  TYPE_FIELD_BITPOS (result_type, 1) = n3;
+
+  return result_type;
+}
+
+/* Read a number from the string pointed to by *PP.
+   The value of *PP is advanced over the number.
+   If END is nonzero, the character that ends the
+   number must match END, or an error happens;
+   and that character is skipped if it does match.
+   If END is zero, *PP is left pointing to that character.  */
+
+static long
+read_number (pp, end)
+     char **pp;
+     int end;
+{
+  register char *p = *pp;
+  register long n = 0;
+  register int c;
+  int sign = 1;
+
+  /* Handle an optional leading minus sign.  */
+
+  if (*p == '-')
+    {
+      sign = -1;
+      p++;
+    }
+
+  /* Read the digits, as far as they go.  */
+
+  while ((c = *p++) >= '0' && c <= '9')
+    {
+      n *= 10;
+      n += c - '0';
+    }
+  if (end)
+    {
+      if (c && c != end)
+       error ("Invalid symbol data: invalid character \\%03o at symbol pos %d.", c, symnum);
+    }
+  else
+    --p;
+
+  *pp = p;
+  return n * sign;
+}
+
+/* Read in an argument list. This is a list of types. It is terminated with
+   a ':', FYI. Return the list of types read in. */
+static struct type **
+read_args (pp, end)
+     char **pp;
+     int end;
+{
+  struct type *types[1024], **rval; /* allow for fns of 1023 parameters */
+  int n = 0;
+
+  while (**pp != end)
+    {
+      if (**pp != ',')
+       error ("Invalid argument list: no ',', at symtab pos %d", symnum);
+      *pp += 1;
+
+      /* Check for and handle cretinous dbx symbol name continuation! */
+      if (**pp == '\\')
+       *pp = next_symbol_text ();
+
+      types[n++] = read_type (pp);
+    }
+  *pp += 1;                    /* get past `end' (the ':' character) */
+
+  if (n == 1)
+    {
+      rval = (struct type **) xmalloc (2 * sizeof (struct type *));
+    }
+  else if (TYPE_CODE (types[n-1]) != TYPE_CODE_VOID)
+    {
+      rval = (struct type **) xmalloc ((n + 1) * sizeof (struct type *));
+      bzero (rval + n, sizeof (struct type *));
+    }
+  else
+    {
+      rval = (struct type **) xmalloc (n * sizeof (struct type *));
+    }
+  bcopy (types, rval, n * sizeof (struct type *));
+  return rval;
+}
+
+/* This function is really horrible, but to avoid it, there would need
+   to be more filling in of forward references.  THIS SHOULD BE MOVED OUT
+   OF COFFREAD.C AND DBXREAD.C TO SOME PLACE WHERE IT CAN BE SHARED */
+int
+fill_in_vptr_fieldno (type)
+     struct type *type;
+{
+  if (TYPE_VPTR_FIELDNO (type) < 0)
+    TYPE_VPTR_FIELDNO (type) =
+      fill_in_vptr_fieldno (TYPE_BASECLASS (type, 1));
+  return TYPE_VPTR_FIELDNO (type);
+}
+\f
+/* Copy a pending list, used to record the contents of a common
+   block for later fixup. BUG FIX by rde@topexpress.co.uk */
+static struct pending *
+copy_pending (beg, begi, end)
+    struct pending *beg, *end;
+    int begi;
+{
+  struct pending *new = 0;
+  struct pending *next;
+
+  /* rde note: `begi' is an offset in block `end', NOT `beg' */
+  for (next = beg; next != 0; next = next->next)
+    {
+      register int j;
+      for (j = next == end ? begi : 0; j < next->nsyms; j++)
+       add_symbol_to_list (next->symbol[j], &new);
+
+      if (next == end)
+        break;
+    }
+  return new;
+}
+
+/* Add a common block's start address to the offset of each symbol
+   declared to be in it (by being between a BCOMM/ECOMM pair that uses
+   the common block name).  */
+
+static void
+fix_common_block (sym, value)
+    struct symbol *sym;
+    int value;
+{
+  struct pending *next = (struct pending *) SYMBOL_NAMESPACE (sym);
+  for ( ; next; next = next->next)
+    {
+      register int j;
+      for (j = next->nsyms - 1; j >= 0; j--)
+       SYMBOL_VALUE (next->symbol[j]) += value;
+    }
+}
+\f
+void
+_initialize_dbxread ()
+{
+  symfile = 0;
+  header_files = (struct header_file *) 0;
+  this_object_header_files = (int *) 0;
+
+  undef_types_allocated = 20;
+  undef_types_length = 0;
+  undef_types = (struct type **) xmalloc (undef_types_allocated *
+                                         sizeof (struct type *));
+
+  add_com ("symbol-file", class_files, symbol_file_command,
+          "Load symbol table (in dbx format) from executable file FILE.");
+
+  add_com ("add-file", class_files, add_file_command,
+           "Load the symbols from FILE, assuming its code is at TEXT_START.") ;
+}
+
+#endif /* READ_DBX_FORMAT */
diff --git a/usr/src/usr.bin/gdb/environ.c b/usr/src/usr.bin/gdb/environ.c
new file mode 100644 (file)
index 0000000..0220166
--- /dev/null
@@ -0,0 +1,185 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)environ.c  6.3 (Berkeley) 5/8/91";
+#endif /* not lint */
+
+/* environ.c -- library for manipulating environments for GNU.
+   Copyright (C) 1986, 1989 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 1, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#define min(a, b) ((a) < (b) ? (a) : (b))
+#define max(a, b) ((a) > (b) ? (a) : (b))
+
+#include "environ.h"
+\f
+/* Return a new environment object.  */
+
+struct environ *
+make_environ ()
+{
+  register struct environ *e;
+
+  e = (struct environ *) xmalloc (sizeof (struct environ));
+
+  e->allocated = 10;
+  e->vector = (char **) xmalloc ((e->allocated + 1) * sizeof (char *));
+  e->vector[0] = 0;
+  return e;
+}
+
+/* Free an environment and all the strings in it.  */
+
+void
+free_environ (e)
+     register struct environ *e;
+{
+  register char **vector = e->vector;
+
+  while (*vector)
+    free (*vector++);
+
+  free (e);
+}
+
+/* Copy the environment given to this process into E.
+   Also copies all the strings in it, so we can be sure
+   that all strings in these environments are safe to free.  */
+
+void
+init_environ (e)
+     register struct environ *e;
+{
+  extern char **environ;
+  register int i;
+
+  for (i = 0; environ[i]; i++);
+
+  if (e->allocated < i)
+    {
+      e->allocated = max (i, e->allocated + 10);
+      e->vector = (char **) xrealloc (e->vector,
+                                     (e->allocated + 1) * sizeof (char *));
+    }
+
+  bcopy (environ, e->vector, (i + 1) * sizeof (char *));
+
+  while (--i >= 0)
+    {
+      register int len = strlen (e->vector[i]) + 1;
+      register char *new = (char *) xmalloc (len);
+      bcopy (e->vector[i], new, len);
+      e->vector[i] = new;
+    }
+}
+
+/* Return the vector of environment E.
+   This is used to get something to pass to execve.  */
+
+char **
+environ_vector (e)
+     struct environ *e;
+{
+  return e->vector;
+}
+\f
+/* Return the value in environment E of variable VAR.  */
+
+char *
+get_in_environ (e, var)
+     struct environ *e;
+     char *var;
+{
+  register int len = strlen (var);
+  register char **vector = e->vector;
+  register char *s;
+
+  for (; s = *vector; vector++)
+    if (!strncmp (s, var, len)
+       && s[len] == '=')
+      return &s[len + 1];
+
+  return 0;
+}
+
+/* Store the value in E of VAR as VALUE.  */
+
+void
+set_in_environ (e, var, value)
+     struct environ *e;
+     char *var;
+     char *value;
+{
+  register int i;
+  register int len = strlen (var);
+  register char **vector = e->vector;
+  register char *s;
+
+  for (i = 0; s = vector[i]; i++)
+    if (!strncmp (s, var, len)
+       && s[len] == '=')
+      break;
+
+  if (s == 0)
+    {
+      if (i == e->allocated)
+       {
+         e->allocated += 10;
+         vector = (char **) xrealloc (vector,
+                                      (e->allocated + 1) * sizeof (char *));
+         e->vector = vector;
+       }
+      vector[i + 1] = 0;
+    }
+  else
+    free (s);
+
+  s = (char *) xmalloc (len + strlen (value) + 2);
+  strcpy (s, var);
+  strcat (s, "=");
+  strcat (s, value);
+  vector[i] = s;
+  return;
+}
+
+/* Remove the setting for variable VAR from environment E.  */
+
+void
+unset_in_environ (e, var)
+     struct environ *e;
+     char *var;
+{
+  register int len = strlen (var);
+  register char **vector = e->vector;
+  register char *s;
+
+  for (; s = *vector; vector++)
+    if (!strncmp (s, var, len)
+       && s[len] == '=')
+      {
+       free (s);
+       bcopy (vector + 1, vector,
+              (e->allocated - (vector - e->vector)) * sizeof (char *));
+       e->vector[e->allocated - 1] = 0;
+       return;
+      }
+}
diff --git a/usr/src/usr.bin/gdb/eval.c b/usr/src/usr.bin/gdb/eval.c
new file mode 100644 (file)
index 0000000..60779e6
--- /dev/null
@@ -0,0 +1,1065 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)eval.c     6.3 (Berkeley) 5/8/91";
+#endif /* not lint */
+
+/* Evaluate expressions for GDB.
+   Copyright (C) 1986, 1987, 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING.  If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#include "defs.h"
+#include "param.h"
+#include "symtab.h"
+#include "value.h"
+#include "expression.h"
+
+\f
+/* Parse the string EXP as a C expression, evaluate it,
+   and return the result as a number.  */
+
+CORE_ADDR
+parse_and_eval_address (exp)
+     char *exp;
+{
+  struct expression *expr = parse_c_expression (exp);
+  register CORE_ADDR addr;
+  register struct cleanup *old_chain
+    = make_cleanup (free_current_contents, &expr);
+
+  addr = (CORE_ADDR) value_as_long (evaluate_expression (expr));
+  do_cleanups (old_chain);
+  return addr;
+}
+
+/* Like parse_and_eval_address but takes a pointer to a char * variable
+   and advanced that variable across the characters parsed.  */
+
+CORE_ADDR
+parse_and_eval_address_1 (expptr)
+     char **expptr;
+{
+  struct expression *expr = parse_c_1 (expptr, 0, 0);
+  register CORE_ADDR addr;
+  register struct cleanup *old_chain
+    = make_cleanup (free_current_contents, &expr);
+
+  addr = value_as_long (evaluate_expression (expr));
+  do_cleanups (old_chain);
+  return addr;
+}
+
+value
+parse_and_eval (exp)
+     char *exp;
+{
+  struct expression *expr = parse_c_expression (exp);
+  register value val;
+  register struct cleanup *old_chain
+    = make_cleanup (free_current_contents, &expr);
+
+  val = evaluate_expression (expr);
+  do_cleanups (old_chain);
+  return val;
+}
+
+/* Parse up to a comma (or to a closeparen)
+   in the string EXPP as an expression, evaluate it, and return the value.
+   EXPP is advanced to point to the comma.  */
+
+value
+parse_to_comma_and_eval (expp)
+     char **expp;
+{
+  struct expression *expr = parse_c_1 (expp, 0, 1);
+  register value val;
+  register struct cleanup *old_chain
+    = make_cleanup (free_current_contents, &expr);
+
+  val = evaluate_expression (expr);
+  do_cleanups (old_chain);
+  return val;
+}
+\f
+/* Evaluate an expression in internal prefix form
+   such as is constructed by expread.y.
+
+   See expression.h for info on the format of an expression.  */
+
+static value evaluate_subexp ();
+static value evaluate_subexp_for_address ();
+static value evaluate_subexp_for_sizeof ();
+static value evaluate_subexp_with_coercion ();
+
+/* return true if 'var' has an address in inferior's memory. */
+static int
+value_has_lval(var)
+    register struct symbol *var;
+{
+  switch (SYMBOL_CLASS(var))
+    {
+    case LOC_STATIC:
+    case LOC_LABEL:
+    case LOC_ARG:
+    case LOC_REF_ARG:
+    case LOC_LOCAL:
+    case LOC_BLOCK:
+      return (1);
+    }
+  return (0);
+}
+
+/* Values of NOSIDE argument to eval_subexp.  */
+enum noside
+{ EVAL_NORMAL,
+  EVAL_SKIP,                   /* Only effect is to increment pos.  */
+  EVAL_AVOID_SIDE_EFFECTS,     /* Don't modify any variables or
+                                  call any functions.  The value
+                                  returned will have the correct
+                                  type, and will have an
+                                  approximately correct lvalue
+                                  type (inaccuracy: anything that is
+                                  listed as being in a register in
+                                  the function in which it was
+                                  declared will be lval_register).  */
+};
+
+value
+evaluate_expression (exp)
+     struct expression *exp;
+{
+  int pc = 0;
+  return evaluate_subexp (0, exp, &pc, EVAL_NORMAL);
+}
+
+/* Evaluate an expression, avoiding all memory references
+   and getting a value whose type alone is correct.  */
+
+value
+evaluate_type (exp)
+     struct expression *exp;
+{
+  int pc = 0;
+  return evaluate_subexp (0, exp, &pc, EVAL_AVOID_SIDE_EFFECTS);
+}
+
+static value
+evaluate_subexp (expect_type, exp, pos, noside)
+     struct type *expect_type;
+     register struct expression *exp;
+     register int *pos;
+     enum noside noside;
+{
+  enum exp_opcode op;
+  int tem;
+  register int pc, pc2, oldpos;
+  register value arg1, arg2, arg3;
+  int nargs;
+  value *argvec;
+
+  pc = (*pos)++;
+  op = exp->elts[pc].opcode;
+
+  switch (op)
+    {
+    case OP_SCOPE:
+      tem = strlen (&exp->elts[pc + 2].string);
+      (*pos) += 3 + ((tem + sizeof (union exp_element))
+                    / sizeof (union exp_element));
+      return value_static_field (exp->elts[pc + 1].type,
+                                &exp->elts[pc + 2].string, -1);
+
+    case OP_LONG:
+      (*pos) += 3;
+      return value_from_long (exp->elts[pc + 1].type,
+                             exp->elts[pc + 2].longconst);
+
+    case OP_DOUBLE:
+      (*pos) += 3;
+      return value_from_double (exp->elts[pc + 1].type,
+                               exp->elts[pc + 2].doubleconst);
+
+    case OP_VAR_VALUE:
+      (*pos) += 2;
+      if (noside == EVAL_SKIP)
+       goto nosideret;
+      if (noside == EVAL_AVOID_SIDE_EFFECTS)
+       {
+         struct symbol * sym = exp->elts[pc + 1].symbol;
+         enum lval_type lv;
+
+         switch (SYMBOL_CLASS (sym))
+           {
+           case LOC_CONST:
+           case LOC_LABEL:
+           case LOC_CONST_BYTES:
+             lv = not_lval;
+           case LOC_REGISTER:
+           case LOC_REGPARM:
+             lv = lval_register;
+           default:
+             lv = lval_memory;
+           }
+
+         return value_zero (SYMBOL_TYPE (sym), lv);
+       }
+      else
+       return value_of_variable (exp->elts[pc + 1].symbol);
+
+    case OP_LAST:
+      (*pos) += 2;
+      return access_value_history ((int) exp->elts[pc + 1].longconst);
+
+    case OP_REGISTER:
+      (*pos) += 2;
+      return value_of_register ((int) exp->elts[pc + 1].longconst);
+
+    case OP_INTERNALVAR:
+      (*pos) += 2;
+      return value_of_internalvar (exp->elts[pc + 1].internalvar);
+
+    case OP_STRING:
+      tem = strlen (&exp->elts[pc + 1].string);
+      (*pos) += 2 + ((tem + sizeof (union exp_element))
+                    / sizeof (union exp_element));
+      if (noside == EVAL_SKIP)
+       goto nosideret;
+      return value_string (&exp->elts[pc + 1].string, tem);
+
+    case TERNOP_COND:
+      /* Skip third and second args to evaluate the first one.  */
+      arg1 = evaluate_subexp (0, exp, pos, noside);
+      if (value_zerop (arg1))
+       {
+         evaluate_subexp (0, exp, pos, EVAL_SKIP);
+         return evaluate_subexp (0, exp, pos, noside);
+       }
+      else
+       {
+         arg2 = evaluate_subexp (0, exp, pos, noside);
+         evaluate_subexp (0, exp, pos, EVAL_SKIP);
+         return arg2;
+       }
+
+    case OP_FUNCALL:
+      (*pos) += 2;
+      op = exp->elts[*pos].opcode;
+      if (op == STRUCTOP_MEMBER || op == STRUCTOP_MPTR)
+       {
+         int fnptr;
+         int tem2;
+
+         nargs = (int) exp->elts[pc + 1].longconst + 1;
+         /* First, evaluate the structure into arg2 */
+         pc2 = (*pos)++;
+
+         if (noside == EVAL_SKIP)
+           goto nosideret;
+
+         if (op == STRUCTOP_MEMBER)
+           {
+             arg2 = evaluate_subexp_for_address (exp, pos, noside);
+           }
+         else
+           {
+             arg2 = evaluate_subexp (0, exp, pos, noside);
+           }
+
+         /* If the function is a virtual function, then the
+            aggregate value (providing the structure) plays
+            its part by providing the vtable.  Otherwise,
+            it is just along for the ride: call the function
+            directly.  */
+
+         arg1 = evaluate_subexp (0, exp, pos, noside);
+
+         fnptr = (int) value_as_long (arg1);
+         if (fnptr < 128)
+           {
+             struct type *basetype;
+             int i, j;
+             basetype = TYPE_TARGET_TYPE (VALUE_TYPE (arg2));
+             basetype = TYPE_VPTR_BASETYPE (basetype);
+             for (i = TYPE_NFN_FIELDS (basetype) - 1; i >= 0; i--)
+               {
+                 struct fn_field *f = TYPE_FN_FIELDLIST1 (basetype, i);
+                 /* If one is virtual, then all are virtual.  */
+                 if (TYPE_FN_FIELD_VIRTUAL_P (f, 0))
+                   for (j = TYPE_FN_FIELDLIST_LENGTH (basetype, i) - 1; j >= 0; --j)
+                     if (TYPE_FN_FIELD_VOFFSET (f, j) == fnptr)
+                       {
+                         value vtbl;
+                         value base = value_ind (arg2);
+                         struct type *fntype = lookup_pointer_type (TYPE_FN_FIELD_TYPE (f, j));
+
+                         if (TYPE_VPTR_FIELDNO (basetype) < 0)
+                           TYPE_VPTR_FIELDNO (basetype)
+                             = fill_in_vptr_fieldno (basetype);
+
+                         VALUE_TYPE (base) = basetype;
+                         vtbl = value_field (base, TYPE_VPTR_FIELDNO (basetype));
+                         VALUE_TYPE (vtbl) = lookup_pointer_type (fntype);
+                         VALUE_TYPE (arg1) = builtin_type_int;
+                         arg1 = value_subscript (vtbl, arg1);
+                         VALUE_TYPE (arg1) = fntype;
+                         goto got_it;
+                       }
+               }
+             if (i < 0)
+               error ("virtual function at index %d not found", fnptr);
+           }
+         else
+           {
+             VALUE_TYPE (arg1) = lookup_pointer_type (TYPE_TARGET_TYPE (VALUE_TYPE (arg1)));
+           }
+       got_it:
+
+         /* Now, say which argument to start evaluating from */
+         tem = 2;
+       }
+      else if (op == STRUCTOP_STRUCT || op == STRUCTOP_PTR)
+       {
+         /* Hair for method invocations */
+         int tem2;
+
+         nargs = (int) exp->elts[pc + 1].longconst + 1;
+         /* First, evaluate the structure into arg2 */
+         pc2 = (*pos)++;
+         tem2 = strlen (&exp->elts[pc2 + 1].string);
+         *pos += 2 + (tem2 + sizeof (union exp_element)) / sizeof (union exp_element);
+         if (noside == EVAL_SKIP)
+           goto nosideret;
+
+         if (op == STRUCTOP_STRUCT)
+           {
+             arg2 = evaluate_subexp_for_address (exp, pos, noside);
+           }
+         else
+           {
+             arg2 = evaluate_subexp (0, exp, pos, noside);
+           }
+         /* Now, say which argument to start evaluating from */
+         tem = 2;
+       }
+      else
+       {
+         nargs = (int) exp->elts[pc + 1].longconst;
+         tem = 0;
+       }
+      argvec = (value *) alloca (sizeof (value) * (nargs + 2));
+      for (; tem <= nargs; tem++)
+       /* Ensure that array expressions are coerced into pointer objects. */
+       argvec[tem] = evaluate_subexp_with_coercion (exp, pos, noside);
+
+      /* signal end of arglist */
+      argvec[tem] = 0;
+
+      if (op == STRUCTOP_STRUCT || op == STRUCTOP_PTR)
+       {
+         int static_memfuncp;
+
+         argvec[1] = arg2;
+         argvec[0] =
+           value_struct_elt (arg2, argvec+1, &exp->elts[pc2 + 1].string,
+                             &static_memfuncp,
+                             op == STRUCTOP_STRUCT
+                             ? "structure" : "structure pointer");
+         if (static_memfuncp)
+           {
+             argvec[1] = argvec[0];
+             nargs--;
+             argvec++;
+           }
+       }
+      else if (op == STRUCTOP_MEMBER || op == STRUCTOP_MPTR)
+       {
+         argvec[1] = arg2;
+         argvec[0] = arg1;
+       }
+
+      if (noside == EVAL_SKIP)
+       goto nosideret;
+      if (noside == EVAL_AVOID_SIDE_EFFECTS)
+       {
+         /* If the return type doesn't look like a function type, call an
+            error.  This can happen if somebody tries to turn a variable into
+            a function call. This is here because people often want to
+            call, eg, strcmp, which gdb doesn't know is a function.  If
+            gdb isn't asked for it's opinion (ie. through "whatis"),
+            it won't offer it. */
+
+         struct type *ftype =
+           TYPE_TARGET_TYPE (VALUE_TYPE (argvec[0]));
+
+         if (ftype)
+           return allocate_value (TYPE_TARGET_TYPE (VALUE_TYPE (argvec[0])));
+         else
+           error ("Expression of type other than \"Function returning ...\" used as function");
+       }
+      return call_function (argvec[0], nargs, argvec + 1);
+
+    case STRUCTOP_STRUCT:
+      tem = strlen (&exp->elts[pc + 1].string);
+      (*pos) += 2 + ((tem + sizeof (union exp_element))
+                    / sizeof (union exp_element));
+
+      /* Try to convert "foo.bar" into "(&foo)->bar" so we won't copy
+       * the entire contents of a large struct just to extract one
+       * value from it. */
+      if (noside == EVAL_NORMAL && exp->elts[*pos].opcode == OP_VAR_VALUE
+         && value_has_lval(exp->elts[*pos + 1].symbol))
+       arg1 = evaluate_subexp_for_address(exp, pos, noside);
+      else
+       arg1 = evaluate_subexp (0, exp, pos, noside);
+
+      if (noside == EVAL_SKIP)
+       goto nosideret;
+      if (noside == EVAL_AVOID_SIDE_EFFECTS)
+       {
+         register struct type *type = VALUE_TYPE (arg1);
+         if (TYPE_CODE (type) == TYPE_CODE_PTR)
+           type = TYPE_TARGET_TYPE (type);
+         return value_zero (lookup_struct_elt_type (type,
+                                                    &exp->elts[pc + 1].string),
+                            lval_memory);
+       }
+      else
+       return value_struct_elt (arg1, 0, &exp->elts[pc + 1].string, 0,
+                                "structure");
+
+    case STRUCTOP_PTR:
+      tem = strlen (&exp->elts[pc + 1].string);
+      (*pos) += 2 + (tem + sizeof (union exp_element)) / sizeof (union exp_element);
+      arg1 = evaluate_subexp (0, exp, pos, noside);
+      if (noside == EVAL_SKIP)
+       goto nosideret;
+      if (noside == EVAL_AVOID_SIDE_EFFECTS)
+       return value_zero (lookup_struct_elt_type (TYPE_TARGET_TYPE
+                                                  (VALUE_TYPE (arg1)),
+                                                  &exp->elts[pc + 1].string),
+                          lval_memory);
+      else
+       return value_struct_elt (arg1, 0, &exp->elts[pc + 1].string, 0,
+                                "structure pointer");
+
+    case STRUCTOP_MEMBER:
+      arg1 = evaluate_subexp_for_address (exp, pos, noside);
+      arg2 = evaluate_subexp (0, exp, pos, noside);
+      if (noside == EVAL_SKIP)
+       goto nosideret;
+      /* Now, convert these values to an address.  */
+      if (TYPE_CODE (VALUE_TYPE (arg2)) != TYPE_CODE_PTR
+         || ((TYPE_CODE (TYPE_TARGET_TYPE (VALUE_TYPE (arg2)))
+              != TYPE_CODE_MEMBER)
+             && (TYPE_CODE (TYPE_TARGET_TYPE (VALUE_TYPE (arg2)))
+                 != TYPE_CODE_METHOD)))
+       error ("non-pointer-to-member value used in pointer-to-member construct");
+      arg3 = value_from_long (builtin_type_long,
+                             value_as_long (arg1) + value_as_long (arg2));
+      VALUE_TYPE (arg3) =
+       lookup_pointer_type (TYPE_TARGET_TYPE (TYPE_TARGET_TYPE (VALUE_TYPE (arg2))));
+      return value_ind (arg3);
+
+    case STRUCTOP_MPTR:
+      arg1 = evaluate_subexp (0, exp, pos, noside);
+      arg2 = evaluate_subexp (0, exp, pos, noside);
+      if (noside == EVAL_SKIP)
+       goto nosideret;
+      /* Now, convert these values to an address.  */
+      if (TYPE_CODE (VALUE_TYPE (arg2)) != TYPE_CODE_PTR
+         || (TYPE_CODE (TYPE_TARGET_TYPE (VALUE_TYPE (arg2))) != TYPE_CODE_MEMBER
+             && TYPE_CODE (TYPE_TARGET_TYPE (VALUE_TYPE (arg2))) != TYPE_CODE_METHOD))
+       error ("non-pointer-to-member value used in pointer-to-member construct");
+      arg3 = value_from_long (builtin_type_long,
+                             value_as_long (arg1) + value_as_long (arg2));
+      VALUE_TYPE (arg3) =
+       lookup_pointer_type (TYPE_TARGET_TYPE (TYPE_TARGET_TYPE (VALUE_TYPE (arg2))));
+      return value_ind (arg3);
+
+    case BINOP_ASSIGN:
+      arg1 = evaluate_subexp (0, exp, pos, noside);
+      arg2 = evaluate_subexp (VALUE_TYPE (arg1), exp, pos, noside);
+      if (noside == EVAL_SKIP || noside == EVAL_AVOID_SIDE_EFFECTS)
+       return arg1;
+      if (binop_user_defined_p (op, arg1, arg2))
+       return value_x_binop (arg1, arg2, op, 0);
+      else
+       return value_assign (arg1, arg2);
+
+    case BINOP_ASSIGN_MODIFY:
+      (*pos) += 2;
+      arg1 = evaluate_subexp (0, exp, pos, noside);
+      arg2 = evaluate_subexp (VALUE_TYPE (arg1), exp, pos, noside);
+      if (noside == EVAL_SKIP || noside == EVAL_AVOID_SIDE_EFFECTS)
+       return arg1;
+      op = exp->elts[pc + 1].opcode;
+      if (binop_user_defined_p (op, arg1, arg2))
+       return value_x_binop (arg1, arg2, BINOP_ASSIGN_MODIFY, op);
+      else if (op == BINOP_ADD)
+       arg2 = value_add (arg1, arg2);
+      else if (op == BINOP_SUB)
+       arg2 = value_sub (arg1, arg2);
+      else
+       arg2 = value_binop (arg1, arg2, op);
+      return value_assign (arg1, arg2);
+
+    case BINOP_ADD:
+      arg1 = evaluate_subexp_with_coercion (exp, pos, noside);
+      arg2 = evaluate_subexp_with_coercion (exp, pos, noside);
+      if (noside == EVAL_SKIP)
+       goto nosideret;
+      if (binop_user_defined_p (op, arg1, arg2))
+       return value_x_binop (arg1, arg2, op, 0);
+      else
+       return value_add (arg1, arg2);
+
+    case BINOP_SUB:
+      arg1 = evaluate_subexp_with_coercion (exp, pos, noside);
+      arg2 = evaluate_subexp_with_coercion (exp, pos, noside);
+      if (noside == EVAL_SKIP)
+       goto nosideret;
+      if (binop_user_defined_p (op, arg1, arg2))
+       return value_x_binop (arg1, arg2, op, 0);
+      else
+       return value_sub (arg1, arg2);
+
+    case BINOP_MUL:
+    case BINOP_DIV:
+    case BINOP_REM:
+    case BINOP_LSH:
+    case BINOP_RSH:
+    case BINOP_LOGAND:
+    case BINOP_LOGIOR:
+    case BINOP_LOGXOR:
+      arg1 = evaluate_subexp (0, exp, pos, noside);
+      arg2 = evaluate_subexp (0, exp, pos, noside);
+      if (noside == EVAL_SKIP)
+       goto nosideret;
+      if (binop_user_defined_p (op, arg1, arg2))
+       return value_x_binop (arg1, arg2, op, 0);
+      else
+       if (noside == EVAL_AVOID_SIDE_EFFECTS
+           && op == BINOP_DIV)
+         return value_zero (VALUE_TYPE (arg1), not_lval);
+      else
+       return value_binop (arg1, arg2, op);
+
+    case BINOP_SUBSCRIPT:
+      arg1 = evaluate_subexp_with_coercion (exp, pos, noside);
+      arg2 = evaluate_subexp_with_coercion (exp, pos, noside);
+      if (noside == EVAL_SKIP)
+       goto nosideret;
+      if (noside == EVAL_AVOID_SIDE_EFFECTS)
+       return value_zero (TYPE_TARGET_TYPE (VALUE_TYPE (arg1)),
+                          VALUE_LVAL (arg1));
+                          
+      if (binop_user_defined_p (op, arg1, arg2))
+       return value_x_binop (arg1, arg2, op, 0);
+      else
+       return value_subscript (arg1, arg2);
+      
+    case BINOP_AND:
+      arg1 = evaluate_subexp (0, exp, pos, noside);
+      if (noside == EVAL_SKIP)
+       {
+         arg2 = evaluate_subexp (0, exp, pos, noside);
+         goto nosideret;
+       }
+      
+      oldpos = *pos;
+      arg2 = evaluate_subexp (0, exp, pos, EVAL_AVOID_SIDE_EFFECTS);
+      *pos = oldpos;
+      
+      if (binop_user_defined_p (op, arg1, arg2)) 
+       {
+         arg2 = evaluate_subexp (0, exp, pos, noside);
+         return value_x_binop (arg1, arg2, op, 0);
+       }
+      else
+       {
+         tem = value_zerop (arg1);
+         arg2 = evaluate_subexp (0, exp, pos,
+                                 (tem ? EVAL_SKIP : noside));
+         return value_from_long (builtin_type_int,
+                                 (LONGEST) (!tem && !value_zerop (arg2)));
+       }
+
+    case BINOP_OR:
+      arg1 = evaluate_subexp (0, exp, pos, noside);
+      if (noside == EVAL_SKIP)
+       {
+         arg2 = evaluate_subexp (0, exp, pos, noside);
+         goto nosideret;
+       }
+      
+      oldpos = *pos;
+      arg2 = evaluate_subexp (0, exp, pos, EVAL_AVOID_SIDE_EFFECTS);
+      *pos = oldpos;
+      
+      if (binop_user_defined_p (op, arg1, arg2)) 
+       {
+         arg2 = evaluate_subexp (0, exp, pos, noside);
+         return value_x_binop (arg1, arg2, op, 0);
+       }
+      else
+       {
+         tem = value_zerop (arg1);
+         arg2 = evaluate_subexp (0, exp, pos,
+                                 (!tem ? EVAL_SKIP : noside));
+         return value_from_long (builtin_type_int,
+                                 (LONGEST) (!tem || !value_zerop (arg2)));
+       }
+
+    case BINOP_EQUAL:
+      arg1 = evaluate_subexp (0, exp, pos, noside);
+      arg2 = evaluate_subexp (VALUE_TYPE (arg1), exp, pos, noside);
+      if (noside == EVAL_SKIP)
+       goto nosideret;
+      if (binop_user_defined_p (op, arg1, arg2))
+       {
+         return value_x_binop (arg1, arg2, op, 0);
+       }
+      else
+       {
+         tem = value_equal (arg1, arg2);
+         return value_from_long (builtin_type_int, (LONGEST) tem);
+       }
+
+    case BINOP_NOTEQUAL:
+      arg1 = evaluate_subexp (0, exp, pos, noside);
+      arg2 = evaluate_subexp (VALUE_TYPE (arg1), exp, pos, noside);
+      if (noside == EVAL_SKIP)
+       goto nosideret;
+      if (binop_user_defined_p (op, arg1, arg2))
+       {
+         return value_x_binop (arg1, arg2, op, 0);
+       }
+      else
+       {
+         tem = value_equal (arg1, arg2);
+         return value_from_long (builtin_type_int, (LONGEST) ! tem);
+       }
+
+    case BINOP_LESS:
+      arg1 = evaluate_subexp (0, exp, pos, noside);
+      arg2 = evaluate_subexp (VALUE_TYPE (arg1), exp, pos, noside);
+      if (noside == EVAL_SKIP)
+       goto nosideret;
+      if (binop_user_defined_p (op, arg1, arg2))
+       {
+         return value_x_binop (arg1, arg2, op, 0);
+       }
+      else
+       {
+         tem = value_less (arg1, arg2);
+         return value_from_long (builtin_type_int, (LONGEST) tem);
+       }
+
+    case BINOP_GTR:
+      arg1 = evaluate_subexp (0, exp, pos, noside);
+      arg2 = evaluate_subexp (VALUE_TYPE (arg1), exp, pos, noside);
+      if (noside == EVAL_SKIP)
+       goto nosideret;
+      if (binop_user_defined_p (op, arg1, arg2))
+       {
+         return value_x_binop (arg1, arg2, op, 0);
+       }
+      else
+       {
+         tem = value_less (arg2, arg1);
+         return value_from_long (builtin_type_int, (LONGEST) tem);
+       }
+
+    case BINOP_GEQ:
+      arg1 = evaluate_subexp (0, exp, pos, noside);
+      arg2 = evaluate_subexp (VALUE_TYPE (arg1), exp, pos, noside);
+      if (noside == EVAL_SKIP)
+       goto nosideret;
+      if (binop_user_defined_p (op, arg1, arg2))
+       {
+         return value_x_binop (arg1, arg2, op, 0);
+       }
+      else
+       {
+         tem = value_less (arg1, arg2);
+         return value_from_long (builtin_type_int, (LONGEST) ! tem);
+       }
+
+    case BINOP_LEQ:
+      arg1 = evaluate_subexp (0, exp, pos, noside);
+      arg2 = evaluate_subexp (VALUE_TYPE (arg1), exp, pos, noside);
+      if (noside == EVAL_SKIP)
+       goto nosideret;
+      if (binop_user_defined_p (op, arg1, arg2))
+       {
+         return value_x_binop (arg1, arg2, op, 0);
+       }
+      else 
+       {
+         tem = value_less (arg2, arg1);
+         return value_from_long (builtin_type_int, (LONGEST) ! tem);
+       }
+
+    case BINOP_REPEAT:
+      arg1 = evaluate_subexp (0, exp, pos, noside);
+      arg2 = evaluate_subexp (0, exp, pos, noside);
+      if (noside == EVAL_SKIP)
+       goto nosideret;
+      if (TYPE_CODE (VALUE_TYPE (arg2)) != TYPE_CODE_INT)
+       error ("Non-integral right operand for \"@\" operator.");
+      if (noside == EVAL_AVOID_SIDE_EFFECTS)
+       return allocate_repeat_value (VALUE_TYPE (arg1),
+                                      (int) value_as_long (arg2));
+      else
+       return value_repeat (arg1, (int) value_as_long (arg2));
+
+    case BINOP_COMMA:
+      evaluate_subexp (0, exp, pos, noside);
+      return evaluate_subexp (0, exp, pos, noside);
+
+    case UNOP_NEG:
+      arg1 = evaluate_subexp (0, exp, pos, noside);
+      if (noside == EVAL_SKIP)
+       goto nosideret;
+      if (unop_user_defined_p (op, arg1))
+       return value_x_unop (arg1, op);
+      else
+       return value_neg (arg1);
+
+    case UNOP_LOGNOT:
+      arg1 = evaluate_subexp (0, exp, pos, noside);
+      if (noside == EVAL_SKIP)
+       goto nosideret;
+      if (unop_user_defined_p (op, arg1))
+       return value_x_unop (arg1, op);
+      else
+       return value_lognot (arg1);
+
+    case UNOP_ZEROP:
+      arg1 = evaluate_subexp (0, exp, pos, noside);
+      if (noside == EVAL_SKIP)
+       goto nosideret;
+      if (unop_user_defined_p (op, arg1))
+       return value_x_unop (arg1, op);
+      else
+       return value_from_long (builtin_type_int,
+                               (LONGEST) value_zerop (arg1));
+
+    case UNOP_IND:
+      if (expect_type && TYPE_CODE (expect_type) == TYPE_CODE_PTR)
+        expect_type = TYPE_TARGET_TYPE (expect_type);
+      arg1 = evaluate_subexp (expect_type, exp, pos, noside);
+      if (noside == EVAL_SKIP)
+       goto nosideret;
+      if (noside == EVAL_AVOID_SIDE_EFFECTS)
+       {
+         if (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_PTR
+             || TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_REF
+             /* In C you can dereference an array to get the 1st elt.  */
+             || TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_ARRAY
+             )
+           return value_zero (TYPE_TARGET_TYPE (VALUE_TYPE (arg1)),
+                              lval_memory);
+         else if (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_INT)
+           /* GDB allows dereferencing an int.  */
+           return value_zero (builtin_type_int, lval_memory);
+         else
+           error ("Attempt to take contents of a non-pointer value.");
+       }
+      return value_ind (arg1);
+
+    case UNOP_ADDR:
+      /* C++: check for and handle pointer to members.  */
+      
+      op = exp->elts[*pos].opcode;
+
+      if (noside == EVAL_SKIP)
+       {
+         if (op == OP_SCOPE)
+           {
+             char *name = &exp->elts[pc+3].string;
+             int tem = strlen (name);
+             (*pos) += 2 + (tem + sizeof (union exp_element)) / sizeof (union exp_element);
+           }
+         else
+           evaluate_subexp (expect_type, exp, pos, EVAL_SKIP);
+         goto nosideret;
+       }
+
+      if (op == OP_SCOPE)
+       {
+         char *name = &exp->elts[pc+3].string;
+         int tem = strlen (name);
+         struct type *domain = exp->elts[pc+2].type;
+         (*pos) += 2 + (tem + sizeof (union exp_element)) / sizeof (union exp_element);
+         arg1 = value_struct_elt_for_address (domain, expect_type, name);
+         if (arg1)
+           return arg1;
+         error ("no field `%s' in structure", name);
+       }
+      else
+       return evaluate_subexp_for_address (exp, pos, noside);
+
+    case UNOP_SIZEOF:
+      if (noside == EVAL_SKIP)
+       {
+         evaluate_subexp (0, exp, pos, EVAL_SKIP);
+         goto nosideret;
+       }
+      return evaluate_subexp_for_sizeof (exp, pos);
+
+    case UNOP_CAST:
+      (*pos) += 2;
+      arg1 = evaluate_subexp (expect_type, exp, pos, noside);
+      if (noside == EVAL_SKIP)
+       goto nosideret;
+      return value_cast (exp->elts[pc + 1].type, arg1);
+
+    case UNOP_MEMVAL:
+      (*pos) += 2;
+      arg1 = evaluate_subexp (expect_type, exp, pos, noside);
+      if (noside == EVAL_SKIP)
+       goto nosideret;
+      if (noside == EVAL_AVOID_SIDE_EFFECTS)
+       return value_zero (exp->elts[pc + 1].type, lval_memory);
+      else
+       return value_at (exp->elts[pc + 1].type,
+                        (CORE_ADDR) value_as_long (arg1));
+
+    case UNOP_PREINCREMENT:
+      arg1 = evaluate_subexp (expect_type, exp, pos, noside);
+      if (noside == EVAL_SKIP || noside == EVAL_AVOID_SIDE_EFFECTS)
+       return arg1;
+      else if (unop_user_defined_p (op, arg1))
+       {
+         return value_x_unop (arg1, op);
+       }
+      else
+       {
+         arg2 = value_add (arg1, value_from_long (builtin_type_char, 
+                                                  (LONGEST) 1));
+         return value_assign (arg1, arg2);
+       }
+
+    case UNOP_PREDECREMENT:
+      arg1 = evaluate_subexp (expect_type, exp, pos, noside);
+      if (noside == EVAL_SKIP || noside == EVAL_AVOID_SIDE_EFFECTS)
+       return arg1;
+      else if (unop_user_defined_p (op, arg1))
+       {
+         return value_x_unop (arg1, op);
+       }
+      else
+       {
+         arg2 = value_sub (arg1, value_from_long (builtin_type_char, 
+                                                  (LONGEST) 1));
+         return value_assign (arg1, arg2);
+       }
+
+    case UNOP_POSTINCREMENT:
+      arg1 = evaluate_subexp (expect_type, exp, pos, noside);
+      if (noside == EVAL_SKIP || noside == EVAL_AVOID_SIDE_EFFECTS)
+       return arg1;
+      else if (unop_user_defined_p (op, arg1))
+       {
+         return value_x_unop (arg1, op);
+       }
+      else
+       {
+         arg2 = value_add (arg1, value_from_long (builtin_type_char, 
+                                                  (LONGEST) 1));
+         value_assign (arg1, arg2);
+         return arg1;
+       }
+
+    case UNOP_POSTDECREMENT:
+      arg1 = evaluate_subexp (expect_type, exp, pos, noside);
+      if (noside == EVAL_SKIP || noside == EVAL_AVOID_SIDE_EFFECTS)
+       return arg1;
+      else if (unop_user_defined_p (op, arg1))
+       {
+         return value_x_unop (arg1, op);
+       }
+      else
+       {
+         arg2 = value_sub (arg1, value_from_long (builtin_type_char, 
+                                                  (LONGEST) 1));
+         value_assign (arg1, arg2);
+         return arg1;
+       }
+       
+    case OP_THIS:
+      (*pos) += 1;
+      return value_of_this (1);
+
+    default:
+      error ("internal error: I do not know how to evaluate what you gave me");
+    }
+
+ nosideret:
+  return value_from_long (builtin_type_long, (LONGEST) 1);
+}
+\f
+/* Evaluate a subexpression of EXP, at index *POS,
+   and return the address of that subexpression.
+   Advance *POS over the subexpression.
+   If the subexpression isn't an lvalue, get an error.
+   NOSIDE may be EVAL_AVOID_SIDE_EFFECTS;
+   then only the type of the result need be correct.  */
+
+static value
+evaluate_subexp_for_address (exp, pos, noside)
+     register struct expression *exp;
+     register int *pos;
+     enum noside noside;
+{
+  enum exp_opcode op;
+  register int pc;
+
+  pc = (*pos);
+  op = exp->elts[pc].opcode;
+
+  switch (op)
+    {
+    case UNOP_IND:
+      (*pos)++;
+      return evaluate_subexp (0, exp, pos, noside);
+
+    case UNOP_MEMVAL:
+      (*pos) += 3;
+      return value_cast (lookup_pointer_type (exp->elts[pc + 1].type),
+                        evaluate_subexp (0, exp, pos, noside));
+
+    case OP_VAR_VALUE:
+      (*pos) += 3;
+      if (noside == EVAL_AVOID_SIDE_EFFECTS)
+       {
+         struct type *type =
+           lookup_pointer_type (SYMBOL_TYPE (exp->elts[pc + 1].symbol));
+         enum address_class sym_class =
+           SYMBOL_CLASS (exp->elts[pc + 1].symbol);
+
+         if (sym_class == LOC_CONST
+             || sym_class == LOC_CONST_BYTES
+             || sym_class == LOC_REGISTER
+             || sym_class == LOC_REGPARM)
+           error ("Attempt to take address of register or constant.");
+
+       return
+         value_zero (type, not_lval);
+       }
+      else
+       return locate_var_value (exp->elts[pc + 1].symbol, (CORE_ADDR) 0);
+
+    default:
+      if (noside == EVAL_AVOID_SIDE_EFFECTS)
+       {
+         value x = evaluate_subexp (0, exp, pos, noside);
+         if (VALUE_LVAL (x) == lval_memory)
+           return value_zero (TYPE_POINTER_TYPE (VALUE_TYPE (x)),
+                              not_lval);
+         else
+           error ("Attempt to take address of non-lval");
+       }
+      return value_addr (evaluate_subexp (0, exp, pos, noside));
+    }
+}
+
+/* Evaluate like `evaluate_subexp' except coercing arrays to pointers.
+   When used in contexts where arrays will be coerced anyway,
+   this is equivalent to `evaluate_subexp'
+   but much faster because it avoids actually fetching array contents.  */
+
+static value
+evaluate_subexp_with_coercion (exp, pos, noside)
+     register struct expression *exp;
+     register int *pos;
+     enum noside noside;
+{
+  register enum exp_opcode op;
+  register int pc;
+  register value val;
+
+  pc = (*pos);
+  op = exp->elts[pc].opcode;
+
+  switch (op)
+    {
+    case OP_VAR_VALUE:
+      if (TYPE_CODE (SYMBOL_TYPE (exp->elts[pc + 1].symbol)) == TYPE_CODE_ARRAY)
+       {
+         (*pos) += 3;
+         val = locate_var_value (exp->elts[pc + 1].symbol, (CORE_ADDR) 0);
+         return value_cast (lookup_pointer_type (TYPE_TARGET_TYPE (SYMBOL_TYPE (exp->elts[pc + 1].symbol))),
+                            val);
+       }
+    }
+
+  return evaluate_subexp (0, exp, pos, noside);
+}
+
+/* Evaluate a subexpression of EXP, at index *POS,
+   and return a value for the size of that subexpression.
+   Advance *POS over the subexpression.  */
+
+static value
+evaluate_subexp_for_sizeof (exp, pos)
+     register struct expression *exp;
+     register int *pos;
+{
+  enum exp_opcode op;
+  register int pc;
+  value val;
+
+  pc = (*pos);
+  op = exp->elts[pc].opcode;
+
+  switch (op)
+    {
+      /* This case is handled specially
+        so that we avoid creating a value for the result type.
+        If the result type is very big, it's desirable not to
+        create a value unnecessarily.  */
+    case UNOP_IND:
+      (*pos)++;
+      val = evaluate_subexp (0, exp, pos, EVAL_AVOID_SIDE_EFFECTS);
+      return value_from_long (builtin_type_int, (LONGEST)
+                     TYPE_LENGTH (TYPE_TARGET_TYPE (VALUE_TYPE (val))));
+
+    case UNOP_MEMVAL:
+      (*pos) += 3;
+      return value_from_long (builtin_type_int, 
+                             (LONGEST) TYPE_LENGTH (exp->elts[pc + 1].type));
+
+    case OP_VAR_VALUE:
+      (*pos) += 3;
+      return value_from_long (builtin_type_int,
+        (LONGEST) TYPE_LENGTH (SYMBOL_TYPE (exp->elts[pc + 1].symbol)));
+
+    default:
+      val = evaluate_subexp (0, exp, pos, EVAL_AVOID_SIDE_EFFECTS);
+      return value_from_long (builtin_type_int,
+                             (LONGEST) TYPE_LENGTH (VALUE_TYPE (val)));
+    }
+}
diff --git a/usr/src/usr.bin/gdb/infcmd.c b/usr/src/usr.bin/gdb/infcmd.c
new file mode 100644 (file)
index 0000000..378784f
--- /dev/null
@@ -0,0 +1,1204 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)infcmd.c   6.4 (Berkeley) 5/8/91";
+#endif /* not lint */
+
+/* Memory-access and commands for inferior process, for GDB.
+   Copyright (C) 1986, 1987, 1988, 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING.  If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#include <stdio.h>
+#include "defs.h"
+#include "param.h"
+#include "symtab.h"
+#include "frame.h"
+#include "inferior.h"
+#include "environ.h"
+#include "value.h"
+
+#include <signal.h>
+#include <sys/param.h>
+
+extern char *sys_siglist[];
+
+#define ERROR_NO_INFERIOR \
+   if (inferior_pid == 0) error ("The program is not being run.");
+
+/* String containing arguments to give to the program,
+   with a space added at the front.  Just a space means no args.  */
+
+static char *inferior_args;
+
+/* File name for default use for standard in/out in the inferior.  */
+
+char *inferior_io_terminal;
+
+/* Pid of our debugged inferior, or 0 if no inferior now.  */
+
+int inferior_pid;
+
+/* Last signal that the inferior received (why it stopped).  */
+
+int stop_signal;
+
+/* Address at which inferior stopped.  */
+
+CORE_ADDR stop_pc;
+
+/* Stack frame when program stopped.  */
+
+FRAME_ADDR stop_frame_address;
+
+/* Number of breakpoint it stopped at, or 0 if none.  */
+
+int stop_breakpoint;
+
+/* Nonzero if stopped due to a step command.  */
+
+int stop_step;
+
+/* Nonzero if stopped due to completion of a stack dummy routine.  */
+
+int stop_stack_dummy;
+
+/* Nonzero if stopped due to a random (unexpected) signal in inferior
+   process.  */
+
+int stopped_by_random_signal;
+
+/* Range to single step within.
+   If this is nonzero, respond to a single-step signal
+   by continuing to step if the pc is in this range.  */
+
+CORE_ADDR step_range_start; /* Inclusive */
+CORE_ADDR step_range_end; /* Exclusive */
+
+/* Stack frame address as of when stepping command was issued.
+   This is how we know when we step into a subroutine call,
+   and how to set the frame for the breakpoint used to step out.  */
+
+FRAME_ADDR step_frame_address;
+
+/* 1 means step over all subroutine calls.
+   -1 means step over calls to undebuggable functions.  */
+
+int step_over_calls;
+
+/* If stepping, nonzero means step count is > 1
+   so don't print frame next time inferior stops
+   if it stops due to stepping.  */
+
+int step_multi;
+
+/* Environment to use for running inferior,
+   in format described in environ.h.  */
+
+struct environ *inferior_environ;
+
+CORE_ADDR read_pc ();
+struct command_line *get_breakpoint_commands ();
+void breakpoint_clear_ignore_counts ();
+
+\f
+int
+have_inferior_p ()
+{
+  return inferior_pid != 0;
+}
+
+static void 
+set_args_command (args)
+     char *args;
+{
+  free (inferior_args);
+  if (!args) args = "";
+  inferior_args = concat (" ", args, "");
+}
+
+void
+tty_command (file, from_tty)
+     char *file;
+     int from_tty;
+{
+  if (file == 0)
+    error_no_arg ("terminal name for running target process");
+
+  inferior_io_terminal = savestring (file, strlen (file));
+}
+
+static void
+run_command (args, from_tty)
+     char *args;
+     int from_tty;
+{
+  extern char **environ;
+  register int i;
+  char *exec_file;
+  char *allargs;
+
+  extern int sys_nerr;
+  extern char *sys_errlist[];
+  extern int errno;
+
+  dont_repeat ();
+
+  if (inferior_pid)
+    {
+      extern int inhibit_confirm;
+      if (!(inhibit_confirm ||
+           query ("The program being debugged has been started already.\n\
+Start it from the beginning? ")))
+       error ("Program not restarted.");
+      kill_inferior ();
+    }
+
+#if 0
+  /* On the other hand, some users want to do
+        break open
+        ignore 1 40
+        run
+     So it's not clear what is best.  */
+
+  /* It is confusing to the user for ignore counts to stick around
+     from previous runs of the inferior.  So clear them.  */
+  breakpoint_clear_ignore_counts ();
+#endif
+
+  exec_file = (char *) get_exec_file (1);
+
+  if (remote_debugging)
+    {
+      if (from_tty)
+       {
+         printf ("Starting program: %s\n", exec_file);
+         fflush (stdout);
+       }
+    }
+  else
+    {
+      if (args)
+       set_args_command (args);
+
+      if (from_tty)
+       {
+         printf ("Starting program: %s%s\n",
+                 exec_file, inferior_args);
+         fflush (stdout);
+       }
+
+      allargs = concat ("exec ", exec_file, inferior_args);
+      inferior_pid = create_inferior (allargs, environ_vector (inferior_environ));
+    }
+
+  clear_proceed_status ();
+
+  start_inferior ();
+}
+\f
+void
+cont_command (proc_count_exp, from_tty)
+     char *proc_count_exp;
+     int from_tty;
+{
+  ERROR_NO_INFERIOR;
+
+  clear_proceed_status ();
+
+  /* If have argument, set proceed count of breakpoint we stopped at.  */
+
+  if (stop_breakpoint > 0 && proc_count_exp)
+    {
+      set_ignore_count (stop_breakpoint,
+                       parse_and_eval_address (proc_count_exp) - 1,
+                       from_tty);
+      if (from_tty)
+       printf ("  ");
+    }
+
+  if (from_tty)
+    printf ("Continuing.\n");
+
+  proceed (-1, -1, 0);
+}
+\f
+/* Step until outside of current statement.  */
+static void step_1 ();
+
+static void
+step_command (count_string)
+{
+  step_1 (0, 0, count_string);
+}
+
+/* Likewise, but skip over subroutine calls as if single instructions.  */
+
+static void
+next_command (count_string)
+{
+  step_1 (1, 0, count_string);
+}
+
+/* Likewise, but step only one instruction.  */
+
+static void
+stepi_command (count_string)
+{
+  step_1 (0, 1, count_string);
+}
+
+static void
+nexti_command (count_string)
+{
+  step_1 (1, 1, count_string);
+}
+
+static void
+step_1 (skip_subroutines, single_inst, count_string)
+     int skip_subroutines;
+     int single_inst;
+     char *count_string;
+{
+  register int count = 1;
+
+  ERROR_NO_INFERIOR;
+  count = count_string ? parse_and_eval_address (count_string) : 1;
+
+  for (; count > 0; count--)
+    {
+      clear_proceed_status ();
+
+      step_frame_address = FRAME_FP (get_current_frame ());
+
+      if (! single_inst)
+       {
+         find_pc_line_pc_range (stop_pc, &step_range_start, &step_range_end);
+         if (step_range_end == 0)
+           {
+             int misc;
+
+             misc = find_pc_misc_function (stop_pc);
+             terminal_ours ();
+             printf ("Current function has no line number information.\n");
+             fflush (stdout);
+
+             /* No info or after _etext ("Can't happen") */
+             if (misc == -1 || misc == misc_function_count - 1)
+               error ("No data available on pc function.");
+
+             printf ("Single stepping until function exit.\n");
+             fflush (stdout);
+
+             step_range_start = misc_function_vector[misc].address;
+             step_range_end = misc_function_vector[misc + 1].address;
+           }
+       }
+      else
+       {
+         /* Say we are stepping, but stop after one insn whatever it does.
+            Don't step through subroutine calls even to undebuggable
+            functions.  */
+         step_range_start = step_range_end = 1;
+         if (!skip_subroutines)
+           step_over_calls = 0;
+       }
+
+      if (skip_subroutines)
+       step_over_calls = 1;
+
+      step_multi = (count > 1);
+      proceed (-1, -1, 1);
+      if (! stop_step)
+       break;
+    }
+}
+\f
+/* Continue program at specified address.  */
+
+static void
+jump_command (arg, from_tty)
+     char *arg;
+     int from_tty;
+{
+  register CORE_ADDR addr;
+  struct symtabs_and_lines sals;
+  struct symtab_and_line sal;
+
+  ERROR_NO_INFERIOR;
+
+  if (!arg)
+    error_no_arg ("starting address");
+
+  sals = decode_line_spec_1 (arg, 1);
+  if (sals.nelts != 1)
+    {
+      error ("Unreasonable jump request");
+    }
+
+  sal = sals.sals[0];
+  free (sals.sals);
+
+  if (sal.symtab == 0 && sal.pc == 0)
+    error ("No source file has been specified.");
+
+  if (sal.pc == 0)
+    sal.pc = find_line_pc (sal.symtab, sal.line);
+
+  {
+    struct symbol *fn = get_frame_function (get_current_frame ());
+    struct symbol *sfn = find_pc_function (sal.pc);
+    if (fn != 0 && sfn != fn
+       && ! query ("Line %d is not in `%s'.  Jump anyway? ",
+                   sal.line, SYMBOL_NAME (fn)))
+      error ("Not confirmed.");
+  }
+
+  if (sal.pc == 0)
+    error ("No line %d in file \"%s\".", sal.line, sal.symtab->filename);
+
+  addr = sal.pc;
+
+  clear_proceed_status ();
+
+  if (from_tty)
+    printf ("Continuing at 0x%x.\n", addr);
+
+  proceed (addr, 0, 0);
+}
+
+/* Continue program giving it specified signal.  */
+
+static void
+signal_command (signum_exp, from_tty)
+     char *signum_exp;
+     int from_tty;
+{
+  register int signum;
+
+  dont_repeat ();              /* Too dangerous.  */
+  ERROR_NO_INFERIOR;
+
+  if (!signum_exp)
+    error_no_arg ("signal number");
+
+  signum = parse_and_eval_address (signum_exp);
+
+  clear_proceed_status ();
+
+  if (from_tty)
+    printf ("Continuing with signal %d.\n", signum);
+
+  proceed (stop_pc, signum, 0);
+}
+
+/* Execute a "stack dummy", a piece of code stored in the stack
+   by the debugger to be executed in the inferior.
+
+   To call: first, do PUSH_DUMMY_FRAME.
+   Then push the contents of the dummy.  It should end with a breakpoint insn.
+   Then call here, passing address at which to start the dummy.
+
+   The contents of all registers are saved before the dummy frame is popped
+   and copied into the buffer BUFFER.
+
+   The dummy's frame is automatically popped whenever that break is hit.
+   If that is the first time the program stops, run_stack_dummy
+   returns to its caller with that frame already gone.
+   Otherwise, the caller never gets returned to.  */
+
+/* 4 => return instead of letting the stack dummy run.  */
+
+static int stack_dummy_testing = 0;
+
+void
+run_stack_dummy (addr, buffer)
+     CORE_ADDR addr;
+     REGISTER_TYPE *buffer;
+{
+  /* Now proceed, having reached the desired place.  */
+  clear_proceed_status ();
+#ifdef notdef
+  if (stack_dummy_testing & 4)
+    {
+      POP_FRAME;
+      return;
+    }
+#endif
+  proceed (addr, 0, 0);
+
+  if (!stop_stack_dummy)
+    error ("Cannot continue previously requested operation.");
+
+  /* On return, the stack dummy has been popped already.  */
+
+  read_register_bytes(0, buffer, REGISTER_BYTES);
+}
+\f
+/* Proceed until we reach the given line as argument or exit the
+   function.  When called with no argument, proceed until we reach a
+   different source line with pc greater than our current one or exit
+   the function.  We skip calls in both cases.
+
+   The effect of this command with an argument is identical to setting
+   a momentary breakpoint at the line specified and executing
+   "finish".
+
+   Note that eventually this command should probably be changed so
+   that only source lines are printed out when we hit the breakpoint
+   we set.  I'm going to postpone this until after a hopeful rewrite
+   of wait_for_inferior and the proceed status code. -- randy */
+
+void
+until_next_command (arg, from_tty)
+     char *arg;
+     int from_tty;
+{
+  FRAME frame;
+  CORE_ADDR pc;
+  struct symbol *func;
+  struct symtab_and_line sal;
+    
+  clear_proceed_status ();
+
+  frame = get_current_frame ();
+
+  /* Step until either exited from this function or greater
+     than the current line (if in symbolic section) or pc (if
+     not). */
+
+  pc = read_pc ();
+  func = find_pc_function (pc);
+  
+  if (!func)
+    {
+      int misc_func = find_pc_misc_function (pc);
+      
+      if (misc_func != -1)
+       error ("Execution is not within a known function.");
+      
+      step_range_start = misc_function_vector[misc_func].address;
+      step_range_end = pc;
+    }
+  else
+    {
+      sal = find_pc_line (pc, 0);
+      
+      step_range_start = BLOCK_START (SYMBOL_BLOCK_VALUE (func));
+      step_range_end = sal.end;
+    }
+  
+  step_over_calls = 1;
+  step_frame_address = FRAME_FP (frame);
+  
+  step_multi = 0;              /* Only one call to proceed */
+  
+  proceed (-1, -1, 1);
+}
+
+void 
+until_command (arg, from_tty)
+     char *arg;
+     int from_tty;
+{
+  if (!have_inferior_p ())
+    error ("The program is not being run.");
+
+  if (arg)
+    until_break_command (arg, from_tty);
+  else
+    until_next_command (arg, from_tty);
+}
+\f
+/* "finish": Set a temporary breakpoint at the place
+   the selected frame will return to, then continue.  */
+
+static void
+finish_command (arg, from_tty)
+     char *arg;
+     int from_tty;
+{
+  struct symtab_and_line sal;
+  register FRAME frame;
+  struct frame_info *fi;
+  register struct symbol *function;
+
+  if (!have_inferior_p ())
+    error ("The program is not being run.");
+  if (arg)
+    error ("The \"finish\" command does not take any arguments.");
+
+  frame = get_prev_frame (selected_frame);
+  if (frame == 0)
+    error ("\"finish\" not meaningful in the outermost frame.");
+
+  clear_proceed_status ();
+
+  fi = get_frame_info (frame);
+  sal = find_pc_line (fi->pc, 0);
+  sal.pc = fi->pc;
+  set_momentary_breakpoint (sal, frame);
+
+  /* Find the function we will return from.  */
+
+  fi = get_frame_info (selected_frame);
+  function = find_pc_function (fi->pc);
+
+  if (from_tty)
+    {
+      printf ("Run till exit from ");
+      print_selected_frame ();
+    }
+
+  proceed (-1, -1, 0);
+
+  if (stop_breakpoint == -3 && function != 0)
+    {
+      struct type *value_type;
+      register value val;
+      CORE_ADDR funcaddr;
+      extern char registers[];
+
+      value_type = TYPE_TARGET_TYPE (SYMBOL_TYPE (function));
+      if (!value_type)
+       fatal ("internal: finish_command: function has no target type");
+      
+      if (TYPE_CODE (value_type) == TYPE_CODE_VOID)
+       return;
+
+      funcaddr = BLOCK_START (SYMBOL_BLOCK_VALUE (function));
+
+      val = value_being_returned (value_type, registers,
+                                 using_struct_return (function,
+                                                      funcaddr,
+                                                      value_type));
+
+      printf ("Value returned is $%d = ", record_latest_value (val));
+      value_print (val, stdout, 0, Val_no_prettyprint);
+      putchar ('\n');
+    }
+}
+\f
+static void
+program_info ()
+{
+  if (inferior_pid == 0)
+    {
+      printf ("The program being debugged is not being run.\n");
+      return;
+    }
+
+  printf ("Program being debugged is in process %d, stopped at 0x%x.\n",
+         inferior_pid, stop_pc);
+  if (stop_step)
+    printf ("It stopped after being stepped.\n");
+  else if (stop_breakpoint > 0)
+    printf ("It stopped at breakpoint %d.\n", stop_breakpoint);
+  else if (stop_signal)
+    printf ("It stopped with signal %d (%s).\n",
+           stop_signal, sys_siglist[stop_signal]);
+
+  printf ("\nType \"info stack\" or \"info reg\" for more information.\n");
+}
+\f
+static void
+environment_info (var)
+     char *var;
+{
+  if (var)
+    {
+      register char *val = get_in_environ (inferior_environ, var);
+      if (val)
+       printf ("%s = %s\n", var, val);
+      else
+       printf ("Environment variable \"%s\" not defined.\n", var);
+    }
+  else
+    {
+      register char **vector = environ_vector (inferior_environ);
+      while (*vector)
+       printf ("%s\n", *vector++);
+    }
+}
+
+static void
+set_environment_command (arg)
+     char *arg;
+{
+  register char *p, *val, *var;
+  int nullset = 0;
+
+  if (arg == 0)
+    error_no_arg ("environment variable and value");
+
+  /* Find seperation between variable name and value */
+  p = (char *) index (arg, '=');
+  val = (char *) index (arg, ' ');
+
+  if (p != 0 && val != 0)
+    {
+      /* We have both a space and an equals.  If the space is before the
+        equals and the only thing between the two is more space, use
+        the equals */
+      if (p > val)
+       while (*val == ' ')
+         val++;
+
+      /* Take the smaller of the two.  If there was space before the
+        "=", they will be the same right now. */
+      p = arg + min (p - arg, val - arg);
+    }
+  else if (val != 0 && p == 0)
+    p = val;
+
+  if (p == arg)
+    error_no_arg ("environment variable to set");
+
+  if (p == 0 || p[1] == 0)
+    {
+      nullset = 1;
+      if (p == 0)
+       p = arg + strlen (arg); /* So that savestring below will work */
+    }
+  else
+    {
+      /* Not setting variable value to null */
+      val = p + 1;
+      while (*val == ' ' || *val == '\t')
+       val++;
+    }
+
+  while (p != arg && (p[-1] == ' ' || p[-1] == '\t')) p--;
+
+  var = savestring (arg, p - arg);
+  if (nullset)
+    {
+      printf ("Setting environment variable \"%s\" to null value.\n", var);
+      set_in_environ (inferior_environ, var, "");
+    }
+  else
+    set_in_environ (inferior_environ, var, val);
+  free (var);
+}
+
+static void
+unset_environment_command (var, from_tty)
+     char *var;
+     int from_tty;
+{
+  if (var == 0)
+    /* If there is no argument, delete all environment variables.
+       Ask for confirmation if reading from the terminal.  */
+    if (!from_tty || query ("Delete all environment variables? "))
+      {
+       free_environ (inferior_environ);
+       inferior_environ = make_environ ();
+      }
+
+  unset_in_environ (inferior_environ, var);
+}
+\f
+/* Read an integer from debugged memory, given address and number of bytes.  */
+
+long
+read_memory_integer (memaddr, len)
+     CORE_ADDR memaddr;
+     int len;
+{
+  char cbuf;
+  short sbuf;
+  int ibuf;
+  long lbuf;
+  int result_err;
+  extern int sys_nerr;
+  extern char *sys_errlist[];
+
+  if (len == sizeof (char))
+    {
+      result_err = read_memory (memaddr, &cbuf, len);
+      if (result_err)
+       error ("Error reading memory address 0x%x: %s (%d).",
+              memaddr, (result_err < sys_nerr ?
+                        sys_errlist[result_err] :
+                        "uknown error"), result_err);
+      return cbuf;
+    }
+  if (len == sizeof (short))
+    {
+      result_err = read_memory (memaddr, &sbuf, len);
+      if (result_err)
+       error ("Error reading memory address 0x%x: %s (%d).",
+              memaddr, (result_err < sys_nerr ?
+                        sys_errlist[result_err] :
+                        "uknown error"), result_err);
+      return sbuf;
+    }
+  if (len == sizeof (int))
+    {
+      result_err = read_memory (memaddr, &ibuf, len);
+      if (result_err)
+       error ("Error reading memory address 0x%x: %s (%d).",
+              memaddr, (result_err < sys_nerr ?
+                        sys_errlist[result_err] :
+                        "uknown error"), result_err);
+      return ibuf;
+    }
+  if (len == sizeof (lbuf))
+    {
+      result_err = read_memory (memaddr, &lbuf, len);
+      if (result_err)
+       error ("Error reading memory address 0x%x: %s (%d).",
+              memaddr, (result_err < sys_nerr ?
+                        sys_errlist[result_err] :
+                        "uknown error"), result_err);
+      return lbuf;
+    }
+  error ("Cannot handle integers of %d bytes.", len);
+}
+\f
+CORE_ADDR
+read_pc ()
+{
+  return (CORE_ADDR) read_register (PC_REGNUM);
+}
+
+void
+write_pc (val)
+     CORE_ADDR val;
+{
+  write_register (PC_REGNUM, (long) val);
+#ifdef NPC_REGNUM
+  write_register (NPC_REGNUM, (long) val+4);
+#endif
+}
+
+char *reg_names[] = REGISTER_NAMES;
+
+#if !defined (DO_REGISTERS_INFO)
+static void
+print_one_register(i)
+       int i;
+{
+       unsigned char raw_buffer[MAX_REGISTER_RAW_SIZE];
+       unsigned char virtual_buffer[MAX_REGISTER_VIRTUAL_SIZE];
+       REGISTER_TYPE val;
+
+       /* Get the data in raw format, then convert also to virtual format.  */
+       read_relative_register_raw_bytes (i, raw_buffer);
+       REGISTER_CONVERT_TO_VIRTUAL (i, raw_buffer, virtual_buffer);
+
+       fputs_filtered (reg_names[i], stdout);
+       print_spaces_filtered (15 - strlen (reg_names[i]), stdout);
+
+       /* If virtual format is floating, print it that way.  */
+       if (TYPE_CODE (REGISTER_VIRTUAL_TYPE (i)) == TYPE_CODE_FLT
+           && ! INVALID_FLOAT (virtual_buffer, REGISTER_VIRTUAL_SIZE (i)))
+               val_print (REGISTER_VIRTUAL_TYPE (i), virtual_buffer, 0,
+                          stdout, 0, 1, 0, Val_pretty_default);
+       /* Else if virtual format is too long for printf,
+          print in hex a byte at a time.  */
+       else if (REGISTER_VIRTUAL_SIZE (i) > sizeof (long))
+       {
+               register int j;
+               printf_filtered ("0x");
+               for (j = 0; j < REGISTER_VIRTUAL_SIZE (i); j++)
+                       printf_filtered ("%02x", virtual_buffer[j]);
+       }
+       /* Else print as integer in hex and in decimal.  */
+       else
+       {
+               long val;
+               
+               bcopy (virtual_buffer, &val, sizeof (long));
+               if (val == 0)
+                       printf_filtered ("0");
+               else
+                       printf_filtered ("0x%08x  %d", val, val);
+       }
+       
+       /* If register has different raw and virtual formats,
+          print the raw format in hex now.  */
+       
+       if (REGISTER_CONVERTIBLE (i))
+       {
+               register int j;
+               
+               printf_filtered ("  (raw 0x");
+               for (j = 0; j < REGISTER_RAW_SIZE (i); j++)
+                       printf_filtered ("%02x", raw_buffer[j]);
+               printf_filtered (")");
+       }
+       printf_filtered ("\n");
+}
+
+
+/* Print out the machine register regnum. If regnum is -1,
+   print all registers.
+   For most machines, having all_registers_info() print the
+   register(s) one per line is good enough. If a different format
+   is required, (eg, for SPARC or Pyramid 90x, which both have
+   lots of regs), or there is an existing convention for showing
+   all the registers, define the macro DO_REGISTERS_INFO(regnum)
+   to provide that format.  */  
+static void
+do_registers_info (regnum, fpregs)
+       int regnum;
+       int fpregs;
+{
+       register int i;
+
+       if (regnum >= 0) {
+               print_one_register(regnum);
+               return;
+       }
+#ifdef notdef
+       printf_filtered (
+"Register       Contents (relative to selected stack frame)\n\n");
+#endif
+       for (i = 0; i < NUM_REGS; i++)
+               if (TYPE_CODE(REGISTER_VIRTUAL_TYPE(i)) != TYPE_CODE_FLT ||
+                   fpregs)
+                       print_one_register(i);
+}
+#endif /* no DO_REGISTERS_INFO.  */
+
+static void
+registers_info (addr_exp, fpregs)
+     char *addr_exp;
+     int fpregs;
+{
+  int regnum;
+
+  if (!have_inferior_p () && !have_core_file_p ())
+    error ("No inferior or core file");
+
+  if (addr_exp)
+    {
+      if (*addr_exp >= '0' && *addr_exp <= '9')
+       regnum = atoi (addr_exp);
+      else
+       {
+         register char *p = addr_exp;
+         if (p[0] == '$')
+           p++;
+         for (regnum = 0; regnum < NUM_REGS; regnum++)
+           if (!strcmp (p, reg_names[regnum]))
+             break;
+         if (regnum == NUM_REGS)
+           error ("%s: invalid register name.", addr_exp);
+       }
+    }
+  else
+    regnum = -1;
+
+#ifdef DO_REGISTERS_INFO
+  DO_REGISTERS_INFO(regnum);
+#else
+  do_registers_info(regnum, fpregs);
+#endif
+}
+
+static void
+all_registers_info (addr_exp)
+     char *addr_exp;
+{
+       registers_info(addr_exp, 1);
+}
+
+static void
+nofp_registers_info (addr_exp)
+     char *addr_exp;
+{
+       registers_info(addr_exp, 0);
+}
+
+\f
+#ifdef ATTACH_DETACH
+#define PROCESS_ATTACH_ALLOWED 1
+#else
+#define PROCESS_ATTACH_ALLOWED 0
+#endif
+/*
+ * TODO:
+ * Should save/restore the tty state since it might be that the
+ * program to be debugged was started on this tty and it wants
+ * the tty in some state other than what we want.  If it's running
+ * on another terminal or without a terminal, then saving and
+ * restoring the tty state is a harmless no-op.
+ * This only needs to be done if we are attaching to a process.
+ */
+
+/*
+ * attach_command --
+ * takes a program started up outside of gdb and ``attaches'' to it.
+ * This stops it cold in its tracks and allows us to start tracing it.
+ * For this to work, we must be able to send the process a
+ * signal and we must have the same effective uid as the program.
+ */
+static void
+attach_command (args, from_tty)
+     char *args;
+     int from_tty;
+{
+  char *exec_file;
+  int pid;
+  int remote = 0;
+
+  dont_repeat();
+
+  if (!args)
+    error_no_arg ("process-id or device file to attach");
+
+  while (*args == ' ' || *args == '\t') args++;
+
+  if (args[0] < '0' || args[0] > '9')
+    remote = 1;
+  else
+#ifndef ATTACH_DETACH
+    error ("Can't attach to a process on this machine.");
+#else
+    pid = atoi (args);
+#endif
+
+  if (inferior_pid)
+    {
+      if (query ("A program is being debugged already.  Kill it? "))
+       kill_inferior ();
+      else
+       error ("Inferior not killed.");
+    }
+
+  exec_file = (char *) get_exec_file (1);
+
+  if (from_tty)
+    {
+      if (remote)
+       printf ("Attaching remote machine\n");
+      else
+       printf ("Attaching program: %s pid %d\n",
+               exec_file, pid);
+      fflush (stdout);
+    }
+
+  if (remote)
+    {
+      remote_open (args, from_tty);
+      start_remote ();
+    }
+#ifdef ATTACH_DETACH
+  else
+    attach_program (pid);
+#endif
+}
+
+/*
+ * detach_command --
+ * takes a program previously attached to and detaches it.
+ * The program resumes execution and will no longer stop
+ * on signals, etc.  We better not have left any breakpoints
+ * in the program or it'll die when it hits one.  For this
+ * to work, it may be necessary for the process to have been
+ * previously attached.  It *might* work if the program was
+ * started via the normal ptrace (PTRACE_TRACEME).
+ */
+
+static void
+detach_command (args, from_tty)
+     char *args;
+     int from_tty;
+{
+  int signal = 0;
+
+#ifdef ATTACH_DETACH
+  if (inferior_pid && !remote_debugging)
+    {
+      if (from_tty)
+       {
+         char *exec_file = (char *)get_exec_file (0);
+         if (exec_file == 0)
+           exec_file = "";
+         printf ("Detaching program: %s pid %d\n",
+                 exec_file, inferior_pid);
+         fflush (stdout);
+       }
+      if (args)
+       signal = atoi (args);
+      
+      detach (signal);
+      inferior_pid = 0;
+    }
+  else
+#endif
+    {
+      if (!remote_debugging)
+       error ("Not currently attached to subsidiary or remote process.");
+
+      if (args)
+       error ("Argument given to \"detach\" when remotely debugging.");
+
+      inferior_pid = 0;
+      remote_close (from_tty);
+    }
+}
+
+/* ARGSUSED */
+static void
+float_info (addr_exp)
+     char *addr_exp;
+{
+#ifdef FLOAT_INFO
+       FLOAT_INFO;
+#else
+       printf ("No floating point info available for this processor.\n");
+#endif
+}
+\f
+extern struct cmd_list_element *setlist, *deletelist;
+
+void
+_initialize_infcmd ()
+{
+  add_com ("tty", class_run, tty_command,
+          "Set terminal for future runs of program being debugged.");
+
+  add_cmd ("args", class_run, set_args_command,
+          "Specify arguments to give program being debugged when it is started.\n\
+Follow this command with any number of args, to be passed to the program.",
+          &setlist);
+
+  add_info ("environment", environment_info,
+           "The environment to give the program, or one variable's value.\n\
+With an argument VAR, prints the value of environment variable VAR to\n\
+give the program being debugged.  With no arguments, prints the entire\n\
+environment to be given to the program.");
+
+  add_cmd ("environment", class_run, unset_environment_command,
+          "Cancel environment variable VAR for the program.\n\
+This does not affect the program until the next \"run\" command.",
+          &deletelist);
+
+  add_cmd ("environment", class_run, set_environment_command,
+          "Set environment variable value to give the program.\n\
+Arguments are VAR VALUE where VAR is variable name and VALUE is value.\n\
+VALUES of environment variables are uninterpreted strings.\n\
+This does not affect the program until the next \"run\" command.",
+          &setlist);
+#ifdef ATTACH_DETACH
+ add_com ("attach", class_run, attach_command,
+          "Attach to a process that was started up outside of GDB.\n\
+This command may take as argument a process id or a device file.\n\
+For a process id, you must have permission to send the process a signal,\n\
+and it must have the same effective uid as the debugger.\n\
+For a device file, the file must be a connection to a remote debug server.\n\n\
+Before using \"attach\", you must use the \"exec-file\" command\n\
+to specify the program running in the process,\n\
+and the \"symbol-file\" command to load its symbol table.");
+#else
+ add_com ("attach", class_run, attach_command,
+          "Attach to a process that was started up outside of GDB.\n\
+This commands takes as an argument the name of a device file.\n\
+This file must be a connection to a remote debug server.\n\n\
+Before using \"attach\", you must use the \"exec-file\" command\n\
+to specify the program running in the process,\n\
+and the \"symbol-file\" command to load its symbol table.");
+#endif
+  add_com ("detach", class_run, detach_command,
+          "Detach the process previously attached.\n\
+The process is no longer traced and continues its execution.");
+
+  add_com ("signal", class_run, signal_command,
+          "Continue program giving it signal number SIGNUMBER.");
+
+  add_com ("stepi", class_run, stepi_command,
+          "Step one instruction exactly.\n\
+Argument N means do this N times (or till program stops for another reason).");
+  add_com_alias ("si", "stepi", class_alias, 0);
+
+  add_com ("nexti", class_run, nexti_command,
+          "Step one instruction, but proceed through subroutine calls.\n\
+Argument N means do this N times (or till program stops for another reason).");
+  add_com_alias ("ni", "nexti", class_alias, 0);
+
+  add_com ("finish", class_run, finish_command,
+          "Execute until selected stack frame returns.\n\
+Upon return, the value returned is printed and put in the value history.");
+
+  add_com ("next", class_run, next_command,
+          "Step program, proceeding through subroutine calls.\n\
+Like the \"step\" command as long as subroutine calls do not happen;\n\
+when they do, the call is treated as one instruction.\n\
+Argument N means do this N times (or till program stops for another reason).");
+  add_com_alias ("n", "next", class_run, 1);
+
+  add_com ("step", class_run, step_command,
+          "Step program until it reaches a different source line.\n\
+Argument N means do this N times (or till program stops for another reason).");
+  add_com_alias ("s", "step", class_run, 1);
+
+  add_com ("until", class_run, until_command,
+          "Execute until the program reaches a source line greater than the current\n\
+or a specified line or address or function (same args as break command).\n\
+Execution will also stop upon exit from the current stack frame.");
+  add_com_alias ("u", "until", class_run, 1);
+  
+  add_com ("jump", class_run, jump_command,
+          "Continue program being debugged at specified line or address.\n\
+Give as argument either LINENUM or *ADDR, where ADDR is an expression\n\
+for an address to start at.");
+
+  add_com ("cont", class_run, cont_command,
+          "Continue program being debugged, after signal or breakpoint.\n\
+If proceeding from breakpoint, a number N may be used as an argument:\n\
+then the same breakpoint won't break until the Nth time it is reached.");
+  add_com_alias ("c", "cont", class_run, 1);
+
+  add_com ("run", class_run, run_command,
+          "Start debugged program.  You may specify arguments to give it.\n\
+Args may include \"*\", or \"[...]\"; they are expanded using \"sh\".\n\
+Input and output redirection with \">\", \"<\", or \">>\" are also allowed.\n\n\
+With no arguments, uses arguments last specified (with \"run\" or \"set args\".\n\
+To cancel previous arguments and run with no arguments,\n\
+use \"set args\" without arguments.");
+  add_com_alias ("r", "run", class_run, 1);
+
+  add_info ("registers", nofp_registers_info,
+           "List of registers and their contents, for selected stack frame.\n\
+Register name as argument means describe only that register.\n\
+(Doesn't display floating point registers; use 'info all-registers'.)\n");
+
+  add_info ("all-registers", all_registers_info,
+           "List of registers and their contents, for selected stack frame.\n\
+Register name as argument means describe only that register.");
+
+  add_info ("program", program_info,
+           "Execution status of the program.");
+
+  add_info ("float", float_info,
+           "Print the status of the floating point unit\n");
+
+  inferior_args = savestring (" ", 1); /* By default, no args.  */
+  inferior_environ = make_environ ();
+  init_environ (inferior_environ);
+}
+
diff --git a/usr/src/usr.bin/gdb/inflow.c b/usr/src/usr.bin/gdb/inflow.c
new file mode 100644 (file)
index 0000000..209fcf3
--- /dev/null
@@ -0,0 +1,569 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)inflow.c   6.5 (Berkeley) 5/8/91";
+#endif /* not lint */
+
+/* Low level interface to ptrace, for GDB when running under Unix.
+   Copyright (C) 1986, 1987, 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING.  If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#include <stdio.h>
+#include "defs.h"
+#include "param.h"
+#include "frame.h"
+#include "inferior.h"
+
+#ifdef USG
+#include <sys/types.h>
+#endif
+
+/* Some USG-esque systems (some of which are BSD-esque enough so that USG
+   is not defined) want this header, and it won't do any harm.  */
+#include <fcntl.h>
+
+#include <sys/param.h>
+#include <sys/dir.h>
+#include <signal.h>
+
+#ifdef HAVE_TERMIO
+#include <termio.h>
+#undef TIOCGETP
+#define TIOCGETP TCGETA
+#undef TIOCSETN
+#define TIOCSETN TCSETA
+#undef TIOCSETP
+#define TIOCSETP TCSETAF
+#define TERMINAL struct termio
+#else
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <sgtty.h>
+#define TERMINAL struct sgttyb
+#endif
+
+#ifdef SET_STACK_LIMIT_HUGE
+#include <sys/time.h>
+#include <sys/resource.h>
+extern int original_stack_limit;
+#endif /* SET_STACK_LIMIT_HUGE */
+
+extern int errno;
+
+/* Nonzero if we are debugging an attached outside process
+   rather than an inferior.  */
+
+int attach_flag;
+
+\f
+/* Record terminal status separately for debugger and inferior.  */
+
+static TERMINAL sg_inferior;
+static TERMINAL sg_ours;
+
+static int tflags_inferior;
+static int tflags_ours;
+
+#if defined(TIOCGETC) && !defined(TIOCGETC_BROKEN)
+static struct tchars tc_inferior;
+static struct tchars tc_ours;
+#endif
+
+#ifdef TIOCGLTC
+static struct ltchars ltc_inferior;
+static struct ltchars ltc_ours;
+#endif
+
+#ifdef TIOCLGET
+static int lmode_inferior;
+static int lmode_ours;
+#endif
+
+#ifdef TIOCGPGRP
+static int pgrp_inferior;
+static int pgrp_ours;
+#else
+static int (*sigint_ours) ();
+static int (*sigquit_ours) ();
+#endif /* TIOCGPGRP */
+
+/* Copy of inferior_io_terminal when inferior was last started.  */
+static char *inferior_thisrun_terminal;
+
+static void terminal_ours_1 ();
+
+/* Nonzero if our terminal settings are in effect.
+   Zero if the inferior's settings are in effect.  */
+static int terminal_is_ours;
+
+/* Initialize the terminal settings we record for the inferior,
+   before we actually run the inferior.  */
+
+void
+terminal_init_inferior ()
+{
+  if (remote_debugging)
+    return;
+
+  sg_inferior = sg_ours;
+  tflags_inferior = tflags_ours;
+
+#if defined(TIOCGETC) && !defined(TIOCGETC_BROKEN)
+  tc_inferior = tc_ours;
+#endif
+
+#ifdef TIOCGLTC
+  ltc_inferior = ltc_ours;
+#endif
+
+#ifdef TIOCLGET
+  lmode_inferior = lmode_ours;
+#endif
+
+#ifdef TIOCGPGRP
+  pgrp_inferior = inferior_pid;
+#endif /* TIOCGPGRP */
+
+  terminal_is_ours = 1;
+}
+
+/* Put the inferior's terminal settings into effect.
+   This is preparation for starting or resuming the inferior.  */
+
+void
+terminal_inferior ()
+{
+  if (remote_debugging)
+    return;
+
+  if (terminal_is_ours)   /*  && inferior_thisrun_terminal == 0) */
+    {
+      fcntl (0, F_SETFL, tflags_inferior);
+      fcntl (0, F_SETFL, tflags_inferior);
+      ioctl (0, TIOCSETN, &sg_inferior);
+
+#if defined(TIOCGETC) && !defined(TIOCGETC_BROKEN)
+      ioctl (0, TIOCSETC, &tc_inferior);
+#endif
+#ifdef TIOCGLTC
+      ioctl (0, TIOCSLTC, &ltc_inferior);
+#endif
+#ifdef TIOCLGET
+      ioctl (0, TIOCLSET, &lmode_inferior);
+#endif
+
+#ifdef TIOCGPGRP
+      ioctl (0, TIOCSPGRP, &pgrp_inferior);
+#else
+      sigint_ours = (int (*) ()) signal (SIGINT, SIG_IGN);
+      sigquit_ours = (int (*) ()) signal (SIGQUIT, SIG_IGN);
+#endif /* TIOCGPGRP */
+    }
+  terminal_is_ours = 0;
+}
+
+/* Put some of our terminal settings into effect,
+   enough to get proper results from our output,
+   but do not change into or out of RAW mode
+   so that no input is discarded.
+
+   After doing this, either terminal_ours or terminal_inferior
+   should be called to get back to a normal state of affairs.  */
+
+void
+terminal_ours_for_output ()
+{
+  if (remote_debugging)
+    return;
+
+  terminal_ours_1 (1);
+}
+
+/* Put our terminal settings into effect.
+   First record the inferior's terminal settings
+   so they can be restored properly later.  */
+
+void
+terminal_ours ()
+{
+  if (remote_debugging)
+    return;
+
+  terminal_ours_1 (0);
+}
+
+static void
+terminal_ours_1 (output_only)
+     int output_only;
+{
+#ifdef TIOCGPGRP
+  /* Ignore this signal since it will happen when we try to set the pgrp.  */
+  void (*osigttou) ();
+#endif /* TIOCGPGRP */
+
+  if (!terminal_is_ours)  /*   && inferior_thisrun_terminal == 0)  */
+    {
+      terminal_is_ours = 1;
+
+#ifdef TIOCGPGRP
+      osigttou = signal (SIGTTOU, SIG_IGN);
+
+      ioctl (0, TIOCGPGRP, &pgrp_inferior);
+      ioctl (0, TIOCSPGRP, &pgrp_ours);
+
+      signal (SIGTTOU, osigttou);
+#else
+      signal (SIGINT, sigint_ours);
+      signal (SIGQUIT, sigquit_ours);
+#endif /* TIOCGPGRP */
+
+      tflags_inferior = fcntl (0, F_GETFL, 0);
+      ioctl (0, TIOCGETP, &sg_inferior);
+
+#if defined(TIOCGETC) && !defined(TIOCGETC_BROKEN)
+      ioctl (0, TIOCGETC, &tc_inferior);
+#endif
+#ifdef TIOCGLTC
+      ioctl (0, TIOCGLTC, &ltc_inferior);
+#endif
+#ifdef TIOCLGET
+      ioctl (0, TIOCLGET, &lmode_inferior);
+#endif
+    }
+
+#ifdef HAVE_TERMIO
+  sg_ours.c_lflag |= ICANON;
+  if (output_only && !(sg_inferior.c_lflag & ICANON))
+    sg_ours.c_lflag &= ~ICANON;
+#else /* not HAVE_TERMIO */
+  sg_ours.sg_flags &= ~RAW & ~CBREAK;
+  if (output_only)
+    sg_ours.sg_flags |= (RAW | CBREAK) & sg_inferior.sg_flags;
+#endif /* not HAVE_TERMIO */
+
+  fcntl (0, F_SETFL, tflags_ours);
+  fcntl (0, F_SETFL, tflags_ours);
+  ioctl (0, TIOCSETN, &sg_ours);
+
+#if defined(TIOCGETC) && !defined(TIOCGETC_BROKEN)
+  ioctl (0, TIOCSETC, &tc_ours);
+#endif
+#ifdef TIOCGLTC
+  ioctl (0, TIOCSLTC, &ltc_ours);
+#endif
+#ifdef TIOCLGET
+  ioctl (0, TIOCLSET, &lmode_ours);
+#endif
+
+#ifdef HAVE_TERMIO
+  sg_ours.c_lflag |= ICANON;
+#else /* not HAVE_TERMIO */
+  sg_ours.sg_flags &= ~RAW & ~CBREAK;
+#endif /* not HAVE_TERMIO */
+}
+
+static void
+term_status_command ()
+{
+  register int i;
+
+  if (remote_debugging)
+    {
+      printf_filtered ("No terminal status when remote debugging.\n");
+      return;
+    }
+
+  printf_filtered ("Inferior's terminal status (currently saved by GDB):\n");
+
+#ifdef HAVE_TERMIO
+
+  printf_filtered ("fcntl flags = 0x%x, c_iflag = 0x%x, c_oflag = 0x%x,\n",
+         tflags_inferior, sg_inferior.c_iflag, sg_inferior.c_oflag);
+  printf_filtered ("c_cflag = 0x%x, c_lflag = 0x%x, c_line = 0x%x.\n",
+         sg_inferior.c_cflag, sg_inferior.c_lflag, sg_inferior.c_line);
+  printf_filtered ("c_cc: ");
+  for (i = 0; (i < NCC); i += 1)
+    printf_filtered ("0x%x ", sg_inferior.c_cc[i]);
+  printf_filtered ("\n");
+
+#else /* not HAVE_TERMIO */
+
+  printf_filtered ("fcntl flags = 0x%x, sgttyb.sg_flags = 0x%x, owner pid = %d.\n",
+         tflags_inferior, sg_inferior.sg_flags, pgrp_inferior);
+
+#endif /* not HAVE_TERMIO */
+
+#if defined(TIOCGETC) && !defined(TIOCGETC_BROKEN)
+  printf_filtered ("tchars: ");
+  for (i = 0; i < sizeof (struct tchars); i++)
+    printf_filtered ("0x%x ", ((char *)&tc_inferior)[i]);
+  printf_filtered ("\n");
+#endif
+
+#ifdef TIOCGLTC
+  printf_filtered ("ltchars: ");
+  for (i = 0; i < sizeof (struct ltchars); i++)
+    printf_filtered ("0x%x ", ((char *)&ltc_inferior)[i]);
+  printf_filtered ("\n");
+  ioctl (0, TIOCSLTC, &ltc_ours);
+#endif
+  
+#ifdef TIOCLGET
+  printf_filtered ("lmode:  %x\n", lmode_inferior);
+#endif
+}
+\f
+static void
+new_tty (ttyname)
+     char *ttyname;
+{
+  register int tty;
+  register int fd;
+
+#ifdef TIOCNOTTY
+  /* Disconnect the child process from our controlling terminal.  */
+  tty = open("/dev/tty", O_RDWR);
+  if (tty > 0)
+    {
+      ioctl(tty, TIOCNOTTY, 0);
+      close(tty);
+    }
+#endif
+
+  /* Now open the specified new terminal.  */
+
+  tty = open(ttyname, O_RDWR);
+  if (tty == -1)
+    _exit(1);
+
+  /* Avoid use of dup2; doesn't exist on all systems.  */
+  if (tty != 0)
+    { close (0); dup (tty); }
+  if (tty != 1)
+    { close (1); dup (tty); }
+  if (tty != 2)
+    { close (2); dup (tty); }
+  if (tty > 2)
+    close(tty);
+}
+\f
+/* Start an inferior process and returns its pid.
+   ALLARGS is a string containing shell command to run the program.
+   ENV is the environment vector to pass.  */
+
+#ifndef SHELL_FILE
+#define SHELL_FILE "/bin/sh"
+#endif
+
+int
+create_inferior (allargs, env)
+     char *allargs;
+     char **env;
+{
+  int pid;
+  char *shell_command;
+  extern int sys_nerr;
+  extern char *sys_errlist[];
+  extern int errno;
+
+  /* If desired, concat something onto the front of ALLARGS.
+     SHELL_COMMAND is the result.  */
+#ifdef SHELL_COMMAND_CONCAT
+  shell_command = (char *) alloca (strlen (SHELL_COMMAND_CONCAT) + strlen (allargs) + 1);
+  strcpy (shell_command, SHELL_COMMAND_CONCAT);
+  strcat (shell_command, allargs);
+#else
+  shell_command = allargs;
+#endif
+
+  /* exec is said to fail if the executable is open.  */
+  close_exec_file ();
+
+#if defined(USG) && !defined(HAVE_VFORK)
+  pid = fork ();
+#else
+  pid = vfork ();
+#endif
+
+  if (pid < 0)
+    perror_with_name ("vfork");
+
+  if (pid == 0)
+    {
+#ifdef TIOCGPGRP
+      /* Run inferior in a separate process group.  */
+      setpgrp (getpid (), getpid ());
+#endif /* TIOCGPGRP */
+
+#ifdef SET_STACK_LIMIT_HUGE
+      /* Reset the stack limit back to what it was.  */
+      {
+       struct rlimit rlim;
+
+       getrlimit (RLIMIT_STACK, &rlim);
+       rlim.rlim_cur = original_stack_limit;
+       setrlimit (RLIMIT_STACK, &rlim);
+      }
+#endif /* SET_STACK_LIMIT_HUGE */
+
+
+      inferior_thisrun_terminal = inferior_io_terminal;
+      if (inferior_io_terminal != 0)
+       new_tty (inferior_io_terminal);
+
+/* It seems that changing the signal handlers for the inferior after
+   a vfork also changes them for the superior.  See comments in
+   initialize_signals for how we get the right signal handlers
+   for the inferior.  */
+/* Not needed on Sun, at least, and loses there
+   because it clobbers the superior.  */
+/*???      signal (SIGQUIT, SIG_DFL);
+      signal (SIGINT, SIG_DFL);  */
+
+      call_ptrace (0);
+      execle (SHELL_FILE, "sh", "-c", shell_command, 0, env);
+
+      fprintf (stderr, "Cannot exec %s: %s.\n", SHELL_FILE,
+              errno < sys_nerr ? sys_errlist[errno] : "unknown error");
+      fflush (stderr);
+      _exit (0177);
+    }
+
+#ifdef TIOCGPGRP
+  /* Avoid race with TIOCSPGRP: guarantee that inferior's pgrp exists.  */
+  setpgrp (pid, pid);
+#endif /* TIOCGPGRP */
+
+#ifdef CREATE_INFERIOR_HOOK
+  CREATE_INFERIOR_HOOK (pid);
+#endif  
+  return pid;
+}
+
+/* Kill the inferior process.  Make us have no inferior.  */
+
+static void
+kill_command ()
+{
+  if (remote_debugging)
+    {
+    inferior_pid = 0;
+    return;
+    }
+  if (inferior_pid == 0)
+    error ("The program is not being run.");
+  if (!query ("Kill the inferior process? "))
+    error ("Not confirmed.");
+  kill_inferior ();
+}
+
+void
+inferior_died ()
+{
+  inferior_pid = 0;
+  attach_flag = 0;
+  mark_breakpoints_out ();
+  select_frame ((FRAME) 0, -1);
+  reopen_exec_file ();
+  if (have_core_file_p ())
+    set_current_frame ( create_new_frame (read_register (FP_REGNUM),
+                                         read_pc ()));
+  else
+    set_current_frame (0);
+}
+\f
+#if 0 
+/* This function is just for testing, and on some systems (Sony NewsOS
+   3.2) <sys/user.h> also includes <sys/time.h> which leads to errors
+   (since on this system at least sys/time.h is not protected against
+   multiple inclusion).  */
+static void
+try_writing_regs_command ()
+{
+  register int i;
+  register int value;
+  extern int errno;
+
+  if (inferior_pid == 0)
+    error ("There is no inferior process now.");
+
+  /* A Sun 3/50 or 3/60 (at least) running SunOS 4.0.3 will have a
+     kernel panic if we try to write past the end of the user area.
+     Presumably Sun will fix this bug (it has been reported), but it
+     is tacky to crash the system, so at least on SunOS4 we need to
+     stop writing when we hit the end of the user area.  */
+  for (i = 0; i < sizeof (struct user); i += 2)
+    {
+      QUIT;
+      errno = 0;
+      value = call_ptrace (3, inferior_pid, i, 0);
+      call_ptrace (6, inferior_pid, i, value);
+      if (errno == 0)
+       {
+         printf (" Succeeded with address 0x%x; value 0x%x (%d).\n",
+                 i, value, value);
+       }
+      else if ((i & 0377) == 0)
+       printf (" Failed at 0x%x.\n", i);
+    }
+}
+#endif
+\f
+void
+_initialize_inflow ()
+{
+  add_com ("term-status", class_obscure, term_status_command,
+          "Print info on inferior's saved terminal status.");
+
+#if 0
+  add_com ("try-writing-regs", class_obscure, try_writing_regs_command,
+          "Try writing all locations in inferior's system block.\n\
+Report which ones can be written.");
+#endif
+
+  add_com ("kill", class_run, kill_command,
+          "Kill execution of program being debugged.");
+
+  inferior_pid = 0;
+
+  ioctl (0, TIOCGETP, &sg_ours);
+  tflags_ours = fcntl (0, F_GETFL, 0);
+
+#if defined(TIOCGETC) && !defined(TIOCGETC_BROKEN)
+  ioctl (0, TIOCGETC, &tc_ours);
+#endif
+#ifdef TIOCGLTC
+  ioctl (0, TIOCGLTC, &ltc_ours);
+#endif
+#ifdef TIOCLGET
+  ioctl (0, TIOCLGET, &lmode_ours);
+#endif
+
+#ifdef TIOCGPGRP
+  ioctl (0, TIOCGPGRP, &pgrp_ours);
+#endif /* TIOCGPGRP */
+
+  terminal_is_ours = 1;
+}
+
diff --git a/usr/src/usr.bin/gdb/infrun.c b/usr/src/usr.bin/gdb/infrun.c
new file mode 100644 (file)
index 0000000..887a0bb
--- /dev/null
@@ -0,0 +1,1459 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)infrun.c   6.4 (Berkeley) 5/8/91";
+#endif /* not lint */
+
+/* Start and stop the inferior process, for GDB.
+   Copyright (C) 1986, 1987, 1988, 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING.  If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+/* Notes on the algorithm used in wait_for_inferior to determine if we
+   just did a subroutine call when stepping.  We have the following
+   information at that point:
+
+                  Current and previous (just before this step) pc.
+                 Current and previous sp.
+                 Current and previous start of current function.
+
+   If the start's of the functions don't match, then
+
+       a) We did a subroutine call.
+
+   In this case, the pc will be at the beginning of a function.
+
+       b) We did a subroutine return.
+
+   Otherwise.
+
+       c) We did a longjmp.
+
+   If we did a longjump, we were doing "nexti", since a next would
+   have attempted to skip over the assembly language routine in which
+   the longjmp is coded and would have simply been the equivalent of a
+   continue.  I consider this ok behaivior.  We'd like one of two
+   things to happen if we are doing a nexti through the longjmp()
+   routine: 1) It behaves as a stepi, or 2) It acts like a continue as
+   above.  Given that this is a special case, and that anybody who
+   thinks that the concept of sub calls is meaningful in the context
+   of a longjmp, I'll take either one.  Let's see what happens.  
+
+   Acts like a subroutine return.  I can handle that with no problem
+   at all.
+
+   -->So: If the current and previous beginnings of the current
+   function don't match, *and* the pc is at the start of a function,
+   we've done a subroutine call.  If the pc is not at the start of a
+   function, we *didn't* do a subroutine call.  
+
+   -->If the beginnings of the current and previous function do match,
+   either: 
+
+       a) We just did a recursive call.
+
+          In this case, we would be at the very beginning of a
+          function and 1) it will have a prologue (don't jump to
+          before prologue, or 2) (we assume here that it doesn't have
+          a prologue) there will have been a change in the stack
+          pointer over the last instruction.  (Ie. it's got to put
+          the saved pc somewhere.  The stack is the usual place.  In
+          a recursive call a register is only an option if there's a
+          prologue to do something with it.  This is even true on
+          register window machines; the prologue sets up the new
+          window.  It might not be true on a register window machine
+          where the call instruction moved the register window
+          itself.  Hmmm.  One would hope that the stack pointer would
+          also change.  If it doesn't, somebody send me a note, and
+          I'll work out a more general theory.
+          randy@wheaties.ai.mit.edu).  This is true (albeit slipperly
+          so) on all machines I'm aware of:
+
+             m68k:     Call changes stack pointer.  Regular jumps don't.
+
+             sparc:    Recursive calls must have frames and therefor,
+                       prologues.
+
+             vax:      All calls have frames and hence change the
+                       stack pointer.
+
+       b) We did a return from a recursive call.  I don't see that we
+          have either the ability or the need to distinguish this
+          from an ordinary jump.  The stack frame will be printed
+          when and if the frame pointer changes; if we are in a
+          function without a frame pointer, it's the users own
+          lookout.
+
+       c) We did a jump within a function.  We assume that this is
+          true if we didn't do a recursive call.
+
+       d) We are in no-man's land ("I see no symbols here").  We
+          don't worry about this; it will make calls look like simple
+          jumps (and the stack frames will be printed when the frame
+          pointer moves), which is a reasonably non-violent response.
+
+#if 0
+    We skip this; it causes more problems than it's worth.
+#ifdef SUN4_COMPILER_FEATURE
+    We do a special ifdef for the sun 4, forcing it to single step
+  into calls which don't have prologues.  This means that we can't
+  nexti over leaf nodes, we can probably next over them (since they
+  won't have debugging symbols, usually), and we can next out of
+  functions returning structures (with a "call .stret4" at the end).
+#endif
+#endif
+*/
+   
+
+   
+   
+
+#include <stdio.h>
+#include "defs.h"
+#include "param.h"
+#include "symtab.h"
+#include "frame.h"
+#include "inferior.h"
+#include "wait.h"
+
+#include <signal.h>
+
+/* unistd.h is needed to #define X_OK */
+#ifdef USG
+#include <unistd.h>
+#else
+#include <sys/file.h>
+#endif
+
+#ifdef UMAX_PTRACE
+#include <aouthdr.h>
+#include <sys/param.h>
+#include <sys/ptrace.h>
+#endif /* UMAX_PTRACE */
+
+/* Required by <sys/user.h>.  */
+#include <sys/types.h>
+/* Required by <sys/user.h>, at least on system V.  */
+#include <sys/dir.h>
+/* Needed by IN_SIGTRAMP on some machines (e.g. vax).  */
+#include <sys/param.h>
+/* Needed by IN_SIGTRAMP on some machines (e.g. vax).  */
+#include <sys/user.h>
+
+extern char *sys_siglist[];
+extern int errno;
+
+/* Sigtramp is a routine that the kernel calls (which then calls the
+   signal handler).  On most machines it is a library routine that
+   is linked into the executable.
+
+   This macro, given a program counter value and the name of the
+   function in which that PC resides (which can be null if the
+   name is not known), returns nonzero if the PC and name show
+   that we are in sigtramp.
+
+   On most machines just see if the name is sigtramp (and if we have
+   no name, assume we are not in sigtramp).  */
+#if !defined (IN_SIGTRAMP)
+#define IN_SIGTRAMP(pc, name) \
+  name && !strcmp ("_sigtramp", name)
+#endif
+
+/* Tables of how to react to signals; the user sets them.  */
+
+static char signal_stop[NSIG];
+static char signal_print[NSIG];
+static char signal_program[NSIG];
+
+/* Nonzero if breakpoints are now inserted in the inferior.  */
+
+static int breakpoints_inserted;
+
+/* Function inferior was in as of last step command.  */
+
+static struct symbol *step_start_function;
+
+/* This is the sequence of bytes we insert for a breakpoint.  */
+
+static char break_insn[] = BREAKPOINT;
+
+/* Nonzero => address for special breakpoint for resuming stepping.  */
+
+static CORE_ADDR step_resume_break_address;
+
+/* Original contents of the byte where the special breakpoint is.  */
+
+static char step_resume_break_shadow[sizeof break_insn];
+
+/* Nonzero means the special breakpoint is a duplicate
+   so it has not itself been inserted.  */
+
+static int step_resume_break_duplicate;
+
+/* Nonzero if we are expecting a trace trap and should proceed from it.
+   2 means expecting 2 trace traps and should continue both times.
+   That occurs when we tell sh to exec the program: we will get
+   a trap after the exec of sh and a second when the program is exec'd.  */
+
+static int trap_expected;
+
+/* Nonzero if the next time we try to continue the inferior, it will
+   step one instruction and generate a spurious trace trap.
+   This is used to compensate for a bug in HP-UX.  */
+
+static int trap_expected_after_continue;
+
+/* Nonzero means expecting a trace trap
+   and should stop the inferior and return silently when it happens.  */
+
+int stop_after_trap;
+
+/* Nonzero means expecting a trace trap due to attaching to a process.  */
+
+int stop_after_attach;
+
+/* Nonzero if pc has been changed by the debugger
+   since the inferior stopped.  */
+
+int pc_changed;
+
+/* Nonzero if debugging a remote machine via a serial link or ethernet.  */
+
+int remote_debugging;
+
+/* Nonzero if program stopped due to error trying to insert breakpoints.  */
+
+static int breakpoints_failed;
+
+/* Nonzero if inferior is in sh before our program got exec'd.  */
+
+static int running_in_shell;
+
+/* Nonzero after stop if current stack frame should be printed.  */
+
+static int stop_print_frame;
+
+#ifdef NO_SINGLE_STEP
+extern int one_stepped;                /* From machine dependent code */
+extern void single_step ();    /* Same. */
+#endif /* NO_SINGLE_STEP */
+
+static void insert_step_breakpoint ();
+static void remove_step_breakpoint ();
+static void wait_for_inferior ();
+static void normal_stop ();
+
+\f
+/* Clear out all variables saying what to do when inferior is continued.
+   First do this, then set the ones you want, then call `proceed'.  */
+
+void
+clear_proceed_status ()
+{
+  trap_expected = 0;
+  step_range_start = 0;
+  step_range_end = 0;
+  step_frame_address = 0;
+  step_over_calls = -1;
+  step_resume_break_address = 0;
+  stop_after_trap = 0;
+  stop_after_attach = 0;
+
+  /* Discard any remaining commands left by breakpoint we had stopped at.  */
+  clear_breakpoint_commands ();
+}
+
+/* Basic routine for continuing the program in various fashions.
+
+   ADDR is the address to resume at, or -1 for resume where stopped.
+   SIGNAL is the signal to give it, or 0 for none,
+     or -1 for act according to how it stopped.
+   STEP is nonzero if should trap after one instruction.
+     -1 means return after that and print nothing.
+     You should probably set various step_... variables
+     before calling here, if you are stepping.
+
+   You should call clear_proceed_status before calling proceed.  */
+
+void
+proceed (addr, signal, step)
+     CORE_ADDR addr;
+     int signal;
+     int step;
+{
+  int oneproc = 0;
+
+  if (step > 0)
+    step_start_function = find_pc_function (read_pc ());
+  if (step < 0)
+    stop_after_trap = 1;
+
+  if (addr == -1)
+    {
+      /* If there is a breakpoint at the address we will resume at,
+        step one instruction before inserting breakpoints
+        so that we do not stop right away.  */
+
+      if (!pc_changed && breakpoint_here_p (read_pc ()))
+       oneproc = 1;
+    }
+  else
+    {
+      write_register (PC_REGNUM, addr);
+#ifdef NPC_REGNUM
+      write_register (NPC_REGNUM, addr + 4);
+#endif
+    }
+
+  if (trap_expected_after_continue)
+    {
+      /* If (step == 0), a trap will be automatically generated after
+        the first instruction is executed.  Force step one
+        instruction to clear this condition.  This should not occur
+        if step is nonzero, but it is harmless in that case.  */
+      oneproc = 1;
+      trap_expected_after_continue = 0;
+    }
+
+  if (oneproc)
+    /* We will get a trace trap after one instruction.
+       Continue it automatically and insert breakpoints then.  */
+    trap_expected = 1;
+  else
+    {
+      int temp = insert_breakpoints ();
+      if (temp)
+       {
+         print_sys_errmsg ("ptrace", temp);
+         error ("Cannot insert breakpoints.\n\
+The same program may be running in another process.");
+       }
+      breakpoints_inserted = 1;
+    }
+
+  /* Install inferior's terminal modes.  */
+  terminal_inferior ();
+
+  if (signal >= 0)
+    stop_signal = signal;
+  /* If this signal should not be seen by program,
+     give it zero.  Used for debugging signals.  */
+  else if (stop_signal < NSIG && !signal_program[stop_signal])
+    stop_signal= 0;
+
+  /* Resume inferior.  */
+  resume (oneproc || step, stop_signal);
+
+  /* Wait for it to stop (if not standalone)
+     and in any case decode why it stopped, and act accordingly.  */
+
+  wait_for_inferior ();
+  normal_stop ();
+}
+
+/* Writing the inferior pc as a register calls this function
+   to inform infrun that the pc has been set in the debugger.  */
+
+void
+writing_pc (val)
+     CORE_ADDR val;
+{
+  stop_pc = val;
+  pc_changed = 1;
+}
+
+/* Start an inferior process for the first time.
+   Actually it was started by the fork that created it,
+   but it will have stopped one instruction after execing sh.
+   Here we must get it up to actual execution of the real program.  */
+
+void
+start_inferior ()
+{
+  /* We will get a trace trap after one instruction.
+     Continue it automatically.  Eventually (after shell does an exec)
+     it will get another trace trap.  Then insert breakpoints and continue.  */
+
+#ifdef START_INFERIOR_TRAPS_EXPECTED
+  trap_expected = START_INFERIOR_TRAPS_EXPECTED;
+#else
+  trap_expected = 2;
+#endif
+
+  running_in_shell = 0;                /* Set to 1 at first SIGTRAP, 0 at second.  */
+  trap_expected_after_continue = 0;
+  breakpoints_inserted = 0;
+  mark_breakpoints_out ();
+
+  /* Set up the "saved terminal modes" of the inferior
+     based on what modes we are starting it with.  */
+  terminal_init_inferior ();
+
+  /* Install inferior's terminal modes.  */
+  terminal_inferior ();
+
+  if (remote_debugging)
+    {
+      trap_expected = 0;
+      fetch_inferior_registers();
+      set_current_frame (create_new_frame (read_register (FP_REGNUM),
+                                          read_pc ()));
+      stop_frame_address = FRAME_FP (get_current_frame());
+      inferior_pid = 3;
+      if (insert_breakpoints())
+       fatal("Can't insert breakpoints");
+      breakpoints_inserted = 1;
+      proceed(-1, -1, 0);
+    }
+  else
+    {
+      wait_for_inferior ();
+      normal_stop ();
+    }
+}
+
+/* Start or restart remote-debugging of a machine over a serial link.  */
+
+void
+restart_remote ()
+{
+  clear_proceed_status ();
+  running_in_shell = 0;
+  trap_expected = 0;
+  stop_after_attach = 1;
+  inferior_pid = 3;
+  wait_for_inferior ();
+  normal_stop();
+}
+
+void
+start_remote ()
+{
+  breakpoints_inserted = 0;
+  mark_breakpoints_out ();
+  restart_remote();
+}
+
+#ifdef ATTACH_DETACH
+
+/* Attach to process PID, then initialize for debugging it
+   and wait for the trace-trap that results from attaching.  */
+
+void
+attach_program (pid)
+     int pid;
+{
+  attach (pid);
+  inferior_pid = pid;
+
+  mark_breakpoints_out ();
+  terminal_init_inferior ();
+  clear_proceed_status ();
+  stop_after_attach = 1;
+  /*proceed (-1, 0, -2);*/
+  terminal_inferior ();
+  wait_for_inferior ();
+  normal_stop ();
+}
+#endif /* ATTACH_DETACH */
+\f
+/* Wait for control to return from inferior to debugger.
+   If inferior gets a signal, we may decide to start it up again
+   instead of returning.  That is why there is a loop in this function.
+   When this function actually returns it means the inferior
+   should be left stopped and GDB should read more commands.  */
+
+static void
+wait_for_inferior ()
+{
+  register int pid;
+  WAITTYPE w;
+  CORE_ADDR pc;
+  int tem;
+  int another_trap;
+  int random_signal;
+  CORE_ADDR stop_sp, prev_sp;
+  CORE_ADDR prev_func_start, stop_func_start;
+  char *prev_func_name, *stop_func_name;
+  CORE_ADDR prologue_pc;
+  int stop_step_resume_break;
+  CORE_ADDR step_resume_break_sp;
+  int newmisc;
+  int newfun_pc;
+  struct symtab_and_line sal;
+  int prev_pc;
+  extern CORE_ADDR text_end;
+  int remove_breakpoints_on_following_step = 0;
+
+  prev_pc = read_pc ();
+  (void) find_pc_partial_function (prev_pc, &prev_func_name,
+                                  &prev_func_start);
+  prev_func_start += FUNCTION_START_OFFSET;
+  prev_sp = read_register (SP_REGNUM);
+
+  while (1)
+    {
+      /* Clean up saved state that will become invalid.  */
+      pc_changed = 0;
+      flush_cached_frames ();
+
+      if (remote_debugging)
+       remote_wait (&w);
+      else
+       {
+         pid = wait (&w);
+         if (pid != inferior_pid)
+           continue;
+       }
+
+      /* See if the process still exists; clean up if it doesn't.  */
+      if (WIFEXITED (w))
+       {
+         terminal_ours_for_output ();
+         if (WEXITSTATUS (w))
+           printf ("\nProgram exited with code 0%o.\n", WEXITSTATUS (w));
+         else
+           printf ("\nProgram exited normally.\n");
+         fflush (stdout);
+         inferior_died ();
+#ifdef NO_SINGLE_STEP
+         one_stepped = 0;
+#endif
+         stop_print_frame = 0;
+         break;
+       }
+      else if (!WIFSTOPPED (w))
+       {
+         kill_inferior ();
+         stop_print_frame = 0;
+         stop_signal = WTERMSIG (w);
+         terminal_ours_for_output ();
+         printf ("\nProgram terminated with signal %d, %s\n",
+                 stop_signal,
+                 stop_signal < NSIG
+                 ? sys_siglist[stop_signal]
+                 : "(undocumented)");
+         printf ("The inferior process no longer exists.\n");
+         fflush (stdout);
+#ifdef NO_SINGLE_STEP
+         one_stepped = 0;
+#endif
+         break;
+       }
+      
+#ifdef NO_SINGLE_STEP
+      if (one_stepped)
+       single_step (0);        /* This actually cleans up the ss */
+#endif /* NO_SINGLE_STEP */
+      
+      fetch_inferior_registers ();
+      stop_pc = read_pc ();
+      set_current_frame ( create_new_frame (read_register (FP_REGNUM),
+                                           read_pc ()));
+      
+      stop_frame_address = FRAME_FP (get_current_frame ());
+      stop_sp = read_register (SP_REGNUM);
+      stop_func_start = 0;
+      stop_func_name = 0;
+      /* Don't care about return value; stop_func_start and stop_func_name
+        will both be 0 if it doesn't work.  */
+      (void) find_pc_partial_function (stop_pc, &stop_func_name,
+                                      &stop_func_start);
+      stop_func_start += FUNCTION_START_OFFSET;
+      another_trap = 0;
+      stop_breakpoint = 0;
+      stop_step = 0;
+      stop_stack_dummy = 0;
+      stop_print_frame = 1;
+      stop_step_resume_break = 0;
+      random_signal = 0;
+      stopped_by_random_signal = 0;
+      breakpoints_failed = 0;
+      
+      /* Look at the cause of the stop, and decide what to do.
+        The alternatives are:
+        1) break; to really stop and return to the debugger,
+        2) drop through to start up again
+        (set another_trap to 1 to single step once)
+        3) set random_signal to 1, and the decision between 1 and 2
+        will be made according to the signal handling tables.  */
+      
+      stop_signal = WSTOPSIG (w);
+      
+      /* First, distinguish signals caused by the debugger from signals
+        that have to do with the program's own actions.
+        Note that breakpoint insns may cause SIGTRAP or SIGILL
+        or SIGEMT, depending on the operating system version.
+        Here we detect when a SIGILL or SIGEMT is really a breakpoint
+        and change it to SIGTRAP.  */
+      
+      if (stop_signal == SIGTRAP
+         || (breakpoints_inserted &&
+             (stop_signal == SIGILL
+              || stop_signal == SIGEMT))
+         || stop_after_attach)
+       {
+         if (stop_signal == SIGTRAP && stop_after_trap)
+           {
+             stop_print_frame = 0;
+             break;
+           }
+         if (stop_after_attach)
+           break;
+         /* Don't even think about breakpoints
+            if still running the shell that will exec the program
+            or if just proceeded over a breakpoint.  */
+         if (stop_signal == SIGTRAP && trap_expected)
+           stop_breakpoint = 0;
+         else
+           {
+             /* See if there is a breakpoint at the current PC.  */
+#if DECR_PC_AFTER_BREAK
+             /* Notice the case of stepping through a jump
+                that leads just after a breakpoint.
+                Don't confuse that with hitting the breakpoint.
+                What we check for is that 1) stepping is going on
+                and 2) the pc before the last insn does not match
+                the address of the breakpoint before the current pc.  */
+             if (!(prev_pc != stop_pc - DECR_PC_AFTER_BREAK
+                   && step_range_end && !step_resume_break_address))
+#endif /* DECR_PC_AFTER_BREAK not zero */
+               {
+                 /* See if we stopped at the special breakpoint for
+                    stepping over a subroutine call.  */
+                 if (stop_pc - DECR_PC_AFTER_BREAK
+                     == step_resume_break_address)
+                   {
+                     stop_step_resume_break = 1;
+                     if (DECR_PC_AFTER_BREAK)
+                       {
+                         stop_pc -= DECR_PC_AFTER_BREAK;
+                         write_register (PC_REGNUM, stop_pc);
+                         pc_changed = 0;
+                       }
+                   }
+                 else
+                   {
+                     stop_breakpoint =
+                       breakpoint_stop_status (stop_pc, stop_frame_address);
+                     /* Following in case break condition called a
+                        function.  */
+                     stop_print_frame = 1;
+                     if (stop_breakpoint && DECR_PC_AFTER_BREAK)
+                       {
+                         stop_pc -= DECR_PC_AFTER_BREAK;
+                         write_register (PC_REGNUM, stop_pc);
+#ifdef NPC_REGNUM
+                         write_register (NPC_REGNUM, stop_pc + 4);
+#endif
+                         pc_changed = 0;
+                       }
+                   }
+               }
+           }
+         
+         if (stop_signal == SIGTRAP)
+           random_signal
+             = !(stop_breakpoint || trap_expected
+                 || stop_step_resume_break
+#ifndef CANNOT_EXECUTE_STACK
+                 || (stop_sp INNER_THAN stop_pc
+                     && stop_pc INNER_THAN stop_frame_address)
+#else
+                 || stop_pc == text_end - 2
+#endif
+                 || (step_range_end && !step_resume_break_address));
+         else
+           {
+             random_signal
+               = !(stop_breakpoint
+                   || stop_step_resume_break
+#ifdef sony_news
+                   || (stop_sp INNER_THAN stop_pc
+                       && stop_pc INNER_THAN stop_frame_address)
+#endif
+                   
+                   );
+             if (!random_signal)
+               stop_signal = SIGTRAP;
+           }
+       }
+      else
+       random_signal = 1;
+      
+      /* For the program's own signals, act according to
+        the signal handling tables.  */
+      
+      if (random_signal
+         && !(running_in_shell && stop_signal == SIGSEGV))
+       {
+         /* Signal not for debugging purposes.  */
+         int printed = 0;
+         
+         stopped_by_random_signal = 1;
+         
+         if (stop_signal >= NSIG
+             || signal_print[stop_signal])
+           {
+             printed = 1;
+             terminal_ours_for_output ();
+             printf ("\nProgram received signal %d, %s\n",
+                     stop_signal,
+                     stop_signal < NSIG
+                     ? sys_siglist[stop_signal]
+                     : "(undocumented)");
+             fflush (stdout);
+           }
+         if (stop_signal >= NSIG
+             || signal_stop[stop_signal])
+           break;
+         /* If not going to stop, give terminal back
+            if we took it away.  */
+         else if (printed)
+           terminal_inferior ();
+       }
+      
+      /* Handle cases caused by hitting a breakpoint.  */
+      
+      if (!random_signal
+         && (stop_breakpoint || stop_step_resume_break))
+       {
+         /* Does a breakpoint want us to stop?  */
+         if (stop_breakpoint && stop_breakpoint != -1
+             && stop_breakpoint != -0x1000001)
+           {
+             /* 0x1000000 is set in stop_breakpoint as returned by
+                breakpoint_stop_status to indicate a silent
+                breakpoint.  */
+             if ((stop_breakpoint > 0 ? stop_breakpoint :
+                  -stop_breakpoint)
+                 & 0x1000000)
+               {
+                 stop_print_frame = 0;
+                 if (stop_breakpoint > 0)
+                   stop_breakpoint -= 0x1000000;
+                 else
+                   stop_breakpoint += 0x1000000;
+               }
+             break;
+           }
+         /* But if we have hit the step-resumption breakpoint,
+            remove it.  It has done its job getting us here.
+            The sp test is to make sure that we don't get hung
+            up in recursive calls in functions without frame
+            pointers.  If the stack pointer isn't outside of
+            where the breakpoint was set (within a routine to be
+            stepped over), we're in the middle of a recursive
+            call. Not true for reg window machines (sparc)
+            because the must change frames to call things and
+            the stack pointer doesn't have to change if it
+            the bp was set in a routine without a frame (pc can
+            be stored in some other window).
+            
+            The removal of the sp test is to allow calls to
+            alloca.  Nasty things were happening.  Oh, well,
+            gdb can only handle one level deep of lack of
+            frame pointer. */
+         if (stop_step_resume_break
+             && (step_frame_address == 0
+                 || (stop_frame_address == step_frame_address)))
+           {
+             remove_step_breakpoint ();
+             step_resume_break_address = 0;
+           }
+         /* Otherwise, must remove breakpoints and single-step
+            to get us past the one we hit.  */
+         else
+           {
+             remove_breakpoints ();
+             remove_step_breakpoint ();
+             breakpoints_inserted = 0;
+             another_trap = 1;
+           }
+         
+         /* We come here if we hit a breakpoint but should not
+            stop for it.  Possibly we also were stepping
+            and should stop for that.  So fall through and
+            test for stepping.  But, if not stepping,
+            do not stop.  */
+       }
+      
+      /* If this is the breakpoint at the end of a stack dummy,
+        just stop silently.  */
+#ifndef CANNOT_EXECUTE_STACK
+      if (stop_sp INNER_THAN stop_pc
+         && stop_pc INNER_THAN stop_frame_address)
+#else
+       if (stop_pc == text_end - 2)
+#endif
+         {
+           stop_print_frame = 0;
+           stop_stack_dummy = 1;
+#ifdef HP_OS_BUG
+           trap_expected_after_continue = 1;
+#endif
+           break;
+         }
+      
+      if (step_resume_break_address)
+       /* Having a step-resume breakpoint overrides anything
+          else having to do with stepping commands until
+          that breakpoint is reached.  */
+       ;
+      /* If stepping through a line, keep going if still within it.  */
+      else if (!random_signal
+              && step_range_end
+              && stop_pc >= step_range_start
+              && stop_pc < step_range_end
+              /* The step range might include the start of the
+                 function, so if we are at the start of the
+                 step range and either the stack or frame pointers
+                 just changed, we've stepped outside */
+              && !(stop_pc == step_range_start
+                   && stop_frame_address
+                   && (stop_sp INNER_THAN prev_sp
+                       || stop_frame_address != step_frame_address)))
+       {
+         /* Don't step through the return from a function
+            unless that is the first instruction stepped through.  */
+         if (ABOUT_TO_RETURN (stop_pc))
+           {
+             stop_step = 1;
+             break;
+           }
+       }
+      
+      /* We stepped out of the stepping range.  See if that was due
+        to a subroutine call that we should proceed to the end of.  */
+      else if (!random_signal && step_range_end)
+       {
+         if (stop_func_start)
+           {
+             prologue_pc = stop_func_start;
+             SKIP_PROLOGUE (prologue_pc);
+           }
+
+         /* Did we just take a signal?  */
+         if (IN_SIGTRAMP (stop_pc, stop_func_name)
+             && !IN_SIGTRAMP (prev_pc, prev_func_name))
+           {
+             /* This code is needed at least in the following case:
+                The user types "next" and then a signal arrives (before
+                the "next" is done).  */
+             /* We've just taken a signal; go until we are back to
+                the point where we took it and one more.  */
+             step_resume_break_address = prev_pc;
+             step_resume_break_duplicate =
+               breakpoint_here_p (step_resume_break_address);
+             step_resume_break_sp = stop_sp;
+             if (breakpoints_inserted)
+               insert_step_breakpoint ();
+             /* Make sure that the stepping range gets us past
+                that instruction.  */
+             if (step_range_end == 1)
+               step_range_end = (step_range_start = prev_pc) + 1;
+             remove_breakpoints_on_following_step = 1;
+           }
+
+         /* ==> See comments at top of file on this algorithm.  <==*/
+         
+         else if (stop_pc == stop_func_start
+             && (stop_func_start != prev_func_start
+                 || prologue_pc != stop_func_start
+                 || stop_sp != prev_sp))
+           {
+             /* It's a subroutine call */
+             if (step_over_calls > 0 
+                 || (step_over_calls &&  find_pc_function (stop_pc) == 0))
+               {
+                 /* A subroutine call has happened.  */
+                 /* Set a special breakpoint after the return */
+                 step_resume_break_address =
+                   SAVED_PC_AFTER_CALL (get_current_frame ());
+                 step_resume_break_duplicate
+                   = breakpoint_here_p (step_resume_break_address);
+                 step_resume_break_sp = stop_sp;
+                 if (breakpoints_inserted)
+                   insert_step_breakpoint ();
+               }
+             /* Subroutine call with source code we should not step over.
+                Do step to the first line of code in it.  */
+             else if (step_over_calls)
+               {
+                 SKIP_PROLOGUE (stop_func_start);
+                 sal = find_pc_line (stop_func_start, 0);
+                 /* Use the step_resume_break to step until
+                    the end of the prologue, even if that involves jumps
+                    (as it seems to on the vax under 4.2).  */
+                 /* If the prologue ends in the middle of a source line,
+                    continue to the end of that source line.
+                    Otherwise, just go to end of prologue.  */
+#ifdef PROLOGUE_FIRSTLINE_OVERLAP
+                 /* no, don't either.  It skips any code that's
+                    legitimately on the first line.  */
+#else
+                 if (sal.end && sal.pc != stop_func_start)
+                   stop_func_start = sal.end;
+#endif
+                 
+                 if (stop_func_start == stop_pc)
+                   {
+                     /* We are already there: stop now.  */
+                     stop_step = 1;
+                     break;
+                   }
+                 else
+                   /* Put the step-breakpoint there and go until there. */
+                   {
+                     step_resume_break_address = stop_func_start;
+                     step_resume_break_sp = stop_sp;
+                     
+                     step_resume_break_duplicate
+                       = breakpoint_here_p (step_resume_break_address);
+                     if (breakpoints_inserted)
+                       insert_step_breakpoint ();
+                     /* Do not specify what the fp should be when we stop
+                        since on some machines the prologue
+                        is where the new fp value is established.  */
+                     step_frame_address = 0;
+                     /* And make sure stepping stops right away then.  */
+                     step_range_end = step_range_start;
+                   }
+               }
+             else
+               {
+                 /* We get here only if step_over_calls is 0 and we
+                    just stepped into a subroutine.  I presume
+                    that step_over_calls is only 0 when we're
+                    supposed to be stepping at the assembly
+                    language level.*/
+                 stop_step = 1;
+                 break;
+               }
+           }
+         /* No subroutince call; stop now.  */
+         else
+           {
+             stop_step = 1;
+             break;
+           }
+       }
+
+      /* Save the pc before execution, to compare with pc after stop.  */
+      prev_pc = read_pc ();    /* Might have been DECR_AFTER_BREAK */
+      prev_func_start = stop_func_start; /* Ok, since if DECR_PC_AFTER
+                                         BREAK is defined, the
+                                         original pc would not have
+                                         been at the start of a
+                                         function. */
+      prev_func_name = stop_func_name;
+      prev_sp = stop_sp;
+
+      /* If we did not do break;, it means we should keep
+        running the inferior and not return to debugger.  */
+
+      /* If trap_expected is 2, it means continue once more
+        and insert breakpoints at the next trap.
+        If trap_expected is 1 and the signal was SIGSEGV, it means
+        the shell is doing some memory allocation--just resume it
+        with SIGSEGV.
+        Otherwise insert breakpoints now, and possibly single step.  */
+
+      if (trap_expected > 1)
+       {
+         trap_expected--;
+         running_in_shell = 1;
+         resume (0, 0);
+       }
+      else if (running_in_shell && stop_signal == SIGSEGV)
+       {
+         resume (0, SIGSEGV);
+       }
+      else if (trap_expected && stop_signal != SIGTRAP)
+       {
+         /* We took a signal which we are supposed to pass through to
+            the inferior and we haven't yet gotten our trap.  Simply
+            continue.  */
+         resume ((step_range_end && !step_resume_break_address)
+                 || trap_expected,
+                 stop_signal);
+       }
+      else
+       {
+         /* Here, we are not awaiting another exec to get
+            the program we really want to debug.
+            Insert breakpoints now, unless we are trying
+            to one-proceed past a breakpoint.  */
+         running_in_shell = 0;
+         /* If we've just finished a special step resume and we don't
+            want to hit a breakpoint, pull em out.  */
+         if (!step_resume_break_address &&
+             remove_breakpoints_on_following_step)
+           {
+             remove_breakpoints_on_following_step = 0;
+             remove_breakpoints ();
+             breakpoints_inserted = 0;
+           }
+         else if (!breakpoints_inserted && !another_trap)
+           {
+             insert_step_breakpoint ();
+             breakpoints_failed = insert_breakpoints ();
+             if (breakpoints_failed)
+               break;
+             breakpoints_inserted = 1;
+           }
+
+         trap_expected = another_trap;
+
+         if (stop_signal == SIGTRAP)
+           stop_signal = 0;
+
+         resume ((step_range_end && !step_resume_break_address)
+                 || trap_expected,
+                 stop_signal);
+       }
+    }
+}
+\f
+/* Here to return control to GDB when the inferior stops for real.
+   Print appropriate messages, remove breakpoints, give terminal our modes.
+
+   RUNNING_IN_SHELL nonzero means the shell got a signal before
+   exec'ing the program we wanted to run.
+   STOP_PRINT_FRAME nonzero means print the executing frame
+   (pc, function, args, file, line number and line text).
+   BREAKPOINTS_FAILED nonzero means stop was due to error
+   attempting to insert breakpoints.  */
+
+static void
+normal_stop ()
+{
+  /* Make sure that the current_frame's pc is correct.  This
+     is a correction for setting up the frame info before doing
+     DECR_PC_AFTER_BREAK */
+  if (inferior_pid)
+    (get_current_frame ())->pc = read_pc ();
+  
+  if (breakpoints_failed)
+    {
+      terminal_ours_for_output ();
+      print_sys_errmsg ("ptrace", breakpoints_failed);
+      printf ("Stopped; cannot insert breakpoints.\n\
+The same program may be running in another process.\n");
+    }
+
+  if (inferior_pid)
+    remove_step_breakpoint ();
+
+  if (inferior_pid && breakpoints_inserted)
+    if (remove_breakpoints ())
+      {
+       terminal_ours_for_output ();
+       printf ("Cannot remove breakpoints because program is no longer writable.\n\
+It must be running in another process.\n\
+Further execution is probably impossible.\n");
+      }
+
+  breakpoints_inserted = 0;
+
+  /* Delete the breakpoint we stopped at, if it wants to be deleted.
+     Delete any breakpoint that is to be deleted at the next stop.  */
+
+  breakpoint_auto_delete (stop_breakpoint);
+
+  /* If an auto-display called a function and that got a signal,
+     delete that auto-display to avoid an infinite recursion.  */
+
+  if (stopped_by_random_signal)
+    disable_current_display ();
+
+  if (step_multi && stop_step)
+    return;
+
+  terminal_ours ();
+
+  if (running_in_shell)
+    {
+      if (stop_signal == SIGSEGV)
+       {
+         char *exec_file = (char *) get_exec_file (1);
+
+         if (access (exec_file, X_OK) != 0)
+           printf ("The file \"%s\" is not executable.\n", exec_file);
+         else
+           /* I don't think we should ever get here.
+              wait_for_inferior now ignores SIGSEGV's which happen in
+              the shell (since the Bourne shell (/bin/sh) has some
+              rather, er, uh, *unorthodox* memory management
+              involving catching SIGSEGV).  */
+           printf ("\
+You have just encountered a bug in \"sh\".  GDB starts your program\n\
+by running \"sh\" with a command to exec your program.\n\
+This is so that \"sh\" will process wildcards and I/O redirection.\n\
+This time, \"sh\" crashed.\n\
+\n\
+One known bug in \"sh\" bites when the environment takes up a lot of space.\n\
+Try \"info env\" to see the environment; then use \"delete env\" to kill\n\
+some variables whose values are large; then do \"run\" again.\n\
+\n\
+If that works, you might want to put those \"delete env\" commands\n\
+into a \".gdbinit\" file in this directory so they will happen every time.\n");
+       }
+      /* Don't confuse user with his program's symbols on sh's data.  */
+      stop_print_frame = 0;
+    }
+
+  if (inferior_pid == 0)
+    return;
+
+  /* Select innermost stack frame except on return from a stack dummy routine,
+     or if the program has exited.  */
+  if (!stop_stack_dummy)
+    {
+      select_frame (get_current_frame (), 0);
+
+      if (stop_print_frame)
+       {
+         if (stop_breakpoint > 0)
+           printf ("\nBpt %d, ", stop_breakpoint);
+         print_sel_frame (stop_step
+                          && step_frame_address == stop_frame_address
+                          && step_start_function == find_pc_function (stop_pc));
+         /* Display the auto-display expressions.  */
+         do_displays ();
+       }
+    }
+
+  if (stop_stack_dummy)
+    {
+      /* Pop the empty frame that contains the stack dummy.
+         POP_FRAME ends with a setting of the current frame, so we
+        can use that next. */
+#ifndef NEW_CALL_FUNCTION
+      POP_FRAME;
+#endif
+      select_frame (get_current_frame (), 0);
+    }
+}
+\f
+static void
+insert_step_breakpoint ()
+{
+  if (step_resume_break_address && !step_resume_break_duplicate)
+    {
+      read_memory (step_resume_break_address,
+                  step_resume_break_shadow, sizeof break_insn);
+      write_memory (step_resume_break_address,
+                   break_insn, sizeof break_insn);
+    }
+}
+
+static void
+remove_step_breakpoint ()
+{
+  if (step_resume_break_address && !step_resume_break_duplicate)
+    write_memory (step_resume_break_address, step_resume_break_shadow,
+                 sizeof break_insn);
+}
+\f
+/* Specify how various signals in the inferior should be handled.  */
+
+static void
+handle_command (args, from_tty)
+     char *args;
+     int from_tty;
+{
+  register char *p = args;
+  int signum = 0;
+  register int digits, wordlen;
+
+  if (!args)
+    error_no_arg ("signal to handle");
+
+  while (*p)
+    {
+      /* Find the end of the next word in the args.  */
+      for (wordlen = 0; p[wordlen] && p[wordlen] != ' ' && p[wordlen] != '\t';
+          wordlen++);
+      for (digits = 0; p[digits] >= '0' && p[digits] <= '9'; digits++);
+
+      /* If it is all digits, it is signal number to operate on.  */
+      if (digits == wordlen)
+       {
+         signum = atoi (p);
+         if (signum <= 0 || signum >= NSIG)
+           {
+             p[wordlen] = '\0';
+             error ("Invalid signal %s given as argument to \"handle\".", p);
+           }
+         if (signum == SIGTRAP || signum == SIGINT)
+           {
+             if (!query ("Signal %d is used by the debugger.\nAre you sure you want to change it? ", signum))
+               error ("Not confirmed.");
+           }
+       }
+      else if (signum == 0)
+       error ("First argument is not a signal number.");
+
+      /* Else, if already got a signal number, look for flag words
+        saying what to do for it.  */
+      else if (!strncmp (p, "stop", wordlen))
+       {
+         signal_stop[signum] = 1;
+         signal_print[signum] = 1;
+       }
+      else if (wordlen >= 2 && !strncmp (p, "print", wordlen))
+       signal_print[signum] = 1;
+      else if (wordlen >= 2 && !strncmp (p, "pass", wordlen))
+       signal_program[signum] = 1;
+      else if (!strncmp (p, "ignore", wordlen))
+       signal_program[signum] = 0;
+      else if (wordlen >= 3 && !strncmp (p, "nostop", wordlen))
+       signal_stop[signum] = 0;
+      else if (wordlen >= 4 && !strncmp (p, "noprint", wordlen))
+       {
+         signal_print[signum] = 0;
+         signal_stop[signum] = 0;
+       }
+      else if (wordlen >= 4 && !strncmp (p, "nopass", wordlen))
+       signal_program[signum] = 0;
+      else if (wordlen >= 3 && !strncmp (p, "noignore", wordlen))
+       signal_program[signum] = 1;
+      /* Not a number and not a recognized flag word => complain.  */
+      else
+       {
+         p[wordlen] = 0;
+         error ("Unrecognized flag word: \"%s\".", p);
+       }
+
+      /* Find start of next word.  */
+      p += wordlen;
+      while (*p == ' ' || *p == '\t') p++;
+    }
+
+  if (from_tty)
+    {
+      /* Show the results.  */
+      printf ("Number\tStop\tPrint\tPass to program\tDescription\n");
+      printf ("%d\t", signum);
+      printf ("%s\t", signal_stop[signum] ? "Yes" : "No");
+      printf ("%s\t", signal_print[signum] ? "Yes" : "No");
+      printf ("%s\t\t", signal_program[signum] ? "Yes" : "No");
+      printf ("%s\n", sys_siglist[signum]);
+    }
+}
+
+/* Print current contents of the tables set by the handle command.  */
+
+static void
+signals_info (signum_exp)
+     char *signum_exp;
+{
+  register int i;
+  printf_filtered ("Number\tStop\tPrint\tPass to program\tDescription\n");
+
+  if (signum_exp)
+    {
+      i = parse_and_eval_address (signum_exp);
+      if (i >= NSIG || i < 0)
+       error ("Signal number out of bounds.");
+      printf_filtered ("%d\t", i);
+      printf_filtered ("%s\t", signal_stop[i] ? "Yes" : "No");
+      printf_filtered ("%s\t", signal_print[i] ? "Yes" : "No");
+      printf_filtered ("%s\t\t", signal_program[i] ? "Yes" : "No");
+      printf_filtered ("%s\n", sys_siglist[i]);
+      return;
+    }
+
+  printf_filtered ("\n");
+  for (i = 0; i < NSIG; i++)
+    {
+      QUIT;
+
+      printf_filtered ("%d\t", i);
+      printf_filtered ("%s\t", signal_stop[i] ? "Yes" : "No");
+      printf_filtered ("%s\t", signal_print[i] ? "Yes" : "No");
+      printf_filtered ("%s\t\t", signal_program[i] ? "Yes" : "No");
+      printf_filtered ("%s\n", sys_siglist[i]);
+    }
+
+  printf_filtered ("\nUse the \"handle\" command to change these tables.\n");
+}
+\f
+/* Save all of the information associated with the inferior<==>gdb
+   connection.  INF_STATUS is a pointer to a "struct inferior_status"
+   (defined in inferior.h).  */
+
+struct command_line *get_breakpoint_commands ();
+
+void
+save_inferior_status (inf_status, restore_stack_info)
+     struct inferior_status *inf_status;
+     int restore_stack_info;
+{
+  inf_status->pc_changed = pc_changed;
+  inf_status->stop_signal = stop_signal;
+  inf_status->stop_pc = stop_pc;
+  inf_status->stop_frame_address = stop_frame_address;
+  inf_status->stop_breakpoint = stop_breakpoint;
+  inf_status->stop_step = stop_step;
+  inf_status->stop_stack_dummy = stop_stack_dummy;
+  inf_status->stopped_by_random_signal = stopped_by_random_signal;
+  inf_status->trap_expected = trap_expected;
+  inf_status->step_range_start = step_range_start;
+  inf_status->step_range_end = step_range_end;
+  inf_status->step_frame_address = step_frame_address;
+  inf_status->step_over_calls = step_over_calls;
+  inf_status->step_resume_break_address = step_resume_break_address;
+  inf_status->stop_after_trap = stop_after_trap;
+  inf_status->stop_after_attach = stop_after_attach;
+  inf_status->breakpoint_commands = get_breakpoint_commands ();
+  inf_status->restore_stack_info = restore_stack_info;
+  
+  read_register_bytes(0, inf_status->register_context, REGISTER_BYTES);
+  record_selected_frame (&(inf_status->selected_frame_address),
+                        &(inf_status->selected_level));
+  return;
+}
+
+void
+restore_inferior_status (inf_status)
+     struct inferior_status *inf_status;
+{
+  FRAME fid;
+  int level = inf_status->selected_level;
+
+  pc_changed = inf_status->pc_changed;
+  stop_signal = inf_status->stop_signal;
+  stop_pc = inf_status->stop_pc;
+  stop_frame_address = inf_status->stop_frame_address;
+  stop_breakpoint = inf_status->stop_breakpoint;
+  stop_step = inf_status->stop_step;
+  stop_stack_dummy = inf_status->stop_stack_dummy;
+  stopped_by_random_signal = inf_status->stopped_by_random_signal;
+  trap_expected = inf_status->trap_expected;
+  step_range_start = inf_status->step_range_start;
+  step_range_end = inf_status->step_range_end;
+  step_frame_address = inf_status->step_frame_address;
+  step_over_calls = inf_status->step_over_calls;
+  step_resume_break_address = inf_status->step_resume_break_address;
+  stop_after_trap = inf_status->stop_after_trap;
+  stop_after_attach = inf_status->stop_after_attach;
+  set_breakpoint_commands (inf_status->breakpoint_commands);
+
+  write_register_bytes(0, inf_status->register_context, REGISTER_BYTES);
+
+  /* The inferior can be gone if the user types "print exit(0)"
+     (and perhaps other times).  */
+  if (have_inferior_p() && inf_status->restore_stack_info)
+    {
+      flush_cached_frames();
+      set_current_frame(create_new_frame(read_register (FP_REGNUM), 
+                                        read_pc()));
+
+      fid = find_relative_frame (get_current_frame (), &level);
+
+      if (fid == 0 ||
+         FRAME_FP (fid) != inf_status->selected_frame_address ||
+         level != 0)
+       {
+         /* I'm not sure this error message is a good idea.  I have
+            only seen it occur after "Can't continue previously
+            requested operation" (we get called from do_cleanups), in
+            which case it just adds insult to injury (one confusing
+            error message after another.  Besides which, does the
+            user really care if we can't restore the previously
+            selected frame?  */
+         fprintf (stderr, "Unable to restore previously selected frame.\n");
+         select_frame (get_current_frame (), 0);
+         return;
+       }
+      
+      select_frame (fid, inf_status->selected_level);
+    }
+  return;
+}
+
+\f
+void
+_initialize_infrun ()
+{
+  register int i;
+
+  add_info ("signals", signals_info,
+           "What debugger does when program gets various signals.\n\
+Specify a signal number as argument to print info on that signal only.");
+
+  add_com ("handle", class_run, handle_command,
+          "Specify how to handle a signal.\n\
+Args are signal number followed by flags.\n\
+Flags allowed are \"stop\", \"print\", \"pass\",\n\
+ \"nostop\", \"noprint\" or \"nopass\".\n\
+Print means print a message if this signal happens.\n\
+Stop means reenter debugger if this signal happens (implies print).\n\
+Pass means let program see this signal; otherwise program doesn't know.\n\
+Pass and Stop may be combined.");
+
+  for (i = 0; i < NSIG; i++)
+    {
+      signal_stop[i] = 1;
+      signal_print[i] = 1;
+      signal_program[i] = 1;
+    }
+
+  /* Signals caused by debugger's own actions
+     should not be given to the program afterwards.  */
+  signal_program[SIGTRAP] = 0;
+  signal_program[SIGINT] = 0;
+
+  /* Signals that are not errors should not normally enter the debugger.  */
+#ifdef SIGALRM
+  signal_stop[SIGALRM] = 0;
+  signal_print[SIGALRM] = 0;
+#endif /* SIGALRM */
+#ifdef SIGVTALRM
+  signal_stop[SIGVTALRM] = 0;
+  signal_print[SIGVTALRM] = 0;
+#endif /* SIGVTALRM */
+#ifdef SIGPROF
+  signal_stop[SIGPROF] = 0;
+  signal_print[SIGPROF] = 0;
+#endif /* SIGPROF */
+#ifdef SIGCHLD
+  signal_stop[SIGCHLD] = 0;
+  signal_print[SIGCHLD] = 0;
+#endif /* SIGCHLD */
+#ifdef SIGCLD
+  signal_stop[SIGCLD] = 0;
+  signal_print[SIGCLD] = 0;
+#endif /* SIGCLD */
+#ifdef SIGIO
+  signal_stop[SIGIO] = 0;
+  signal_print[SIGIO] = 0;
+#endif /* SIGIO */
+#ifdef SIGURG
+  signal_stop[SIGURG] = 0;
+  signal_print[SIGURG] = 0;
+#endif /* SIGURG */
+}
+
diff --git a/usr/src/usr.bin/gdb/printcmd.c b/usr/src/usr.bin/gdb/printcmd.c
new file mode 100644 (file)
index 0000000..6edd7bd
--- /dev/null
@@ -0,0 +1,1867 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)printcmd.c 6.5 (Berkeley) 5/8/91";
+#endif /* not lint */
+
+/* Print values for GNU debugger GDB.
+   Copyright (C) 1986, 1987, 1988, 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING.  If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#include <stdio.h>
+#include "defs.h"
+#include "param.h"
+#include "frame.h"
+#include "symtab.h"
+#include "value.h"
+#include "expression.h"
+
+struct format_data
+{
+  int count;
+  char format;
+  char size;
+};
+
+/* Last specified output format.  */
+
+static char last_format = 'x';
+
+/* Last specified examination size.  'b', 'h', 'w' or `q'.  */
+
+static char last_size = 'w';
+
+/* Default address to examine next.  */
+
+static CORE_ADDR next_address;
+
+/* Last address examined.  */
+
+static CORE_ADDR last_examine_address;
+
+/* Contents of last address examined.
+   This is not valid past the end of the `x' command!  */
+
+static value last_examine_value;
+
+/* Number of auto-display expression currently being displayed.
+   So that we can deleted it if we get an error or a signal within it.
+   -1 when not doing one.  */
+
+int current_display_number;
+
+static void do_one_display ();
+
+void do_displays ();
+void print_address ();
+void print_floating ();
+void print_scalar_formatted ();
+void print_formatted_address ();
+
+\f
+/* Decode a format specification.  *STRING_PTR should point to it.
+   OFORMAT and OSIZE are used as defaults for the format and size
+   if none are given in the format specification.
+   If OSIZE is zero, then the size field of the returned value
+   should be set only if a size is explicitly specified by the
+   user.
+   The structure returned describes all the data
+   found in the specification.  In addition, *STRING_PTR is advanced
+   past the specification and past all whitespace following it.  */
+
+struct format_data
+decode_format (string_ptr, oformat, osize)
+     char **string_ptr;
+     char oformat;
+     char osize;
+{
+  struct format_data val;
+  register char *p = *string_ptr;
+
+  val.format = '?';
+  val.size = '?';
+  val.count = 1;
+
+  if (*p >= '0' && *p <= '9')
+    val.count = atoi (p);
+  while (*p >= '0' && *p <= '9') p++;
+
+  /* Now process size or format letters that follow.  */
+
+  while (1)
+    {
+      if (*p == 'b' || *p == 'h' || *p == 'w' || *p == 'g')
+       val.size = *p++;
+#ifdef LONG_LONG
+      else if (*p == 'l')
+       {
+         val.size = 'g';
+         p++;
+       }
+#endif
+      else if ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z'))
+       val.format = *p++;
+      else
+       break;
+    }
+
+#ifndef LONG_LONG
+  /* Make sure 'g' size is not used on integer types.
+     Well, actually, we can handle hex.  */
+  if (val.size == 'g' && val.format != 'f' && val.format != 'x')
+    val.size = 'w';
+#endif
+
+  while (*p == ' ' || *p == '\t') p++;
+  *string_ptr = p;
+
+  /* Set defaults for format and size if not specified.  */
+  if (val.format == '?')
+    {
+      if (val.size == '?')
+       {
+         /* Neither has been specified.  */
+         val.format = oformat;
+         val.size = osize;
+       }
+      else
+       /* If a size is specified, any format makes a reasonable
+          default except 'i'.  */
+       val.format = oformat == 'i' ? 'x' : oformat;
+    }
+  else if (val.size == '?')
+    switch (val.format)
+      {
+      case 'a':
+      case 's':
+      case 'A':
+       /* Addresses must be words.  */
+       val.size = osize ? 'w' : osize;
+       break;
+      case 'f':
+       /* Floating point has to be word or giantword.  */
+       if (osize == 'w' || osize == 'g')
+         val.size = osize;
+       else
+         /* Default it to giantword if the last used size is not
+            appropriate.  */
+         val.size = osize ? 'g' : osize;
+       break;
+      case 'c':
+       /* Characters default to one byte.  */
+       val.size = osize ? 'b' : osize;
+       break;
+      default:
+       /* The default is the size most recently specified.  */
+       val.size = osize;
+      }
+
+  return val;
+}
+\f
+/* Print value VAL on stdout according to FORMAT, a letter or 0.
+   Do not end with a newline.
+   0 means print VAL according to its own type.
+   SIZE is the letter for the size of datum being printed.
+   This is used to pad hex numbers so they line up.  */
+
+static void
+print_formatted (val, format, size)
+     register value val;
+     register char format;
+     char size;
+{
+  int len = TYPE_LENGTH (VALUE_TYPE (val));
+
+  if (VALUE_LVAL (val) == lval_memory)
+    next_address = VALUE_ADDRESS (val) + len;
+
+  switch (format)
+    {
+    case 's':
+      next_address = VALUE_ADDRESS (val)
+       + value_print (value_addr (val), stdout, 0, Val_pretty_default);
+      break;
+
+    case 'i':
+      next_address = VALUE_ADDRESS (val)
+       + print_insn (VALUE_ADDRESS (val), stdout);
+      break;
+
+    default:
+      if (format == 0
+         || TYPE_CODE (VALUE_TYPE (val)) == TYPE_CODE_ARRAY
+         || TYPE_CODE (VALUE_TYPE (val)) == TYPE_CODE_STRUCT
+         || TYPE_CODE (VALUE_TYPE (val)) == TYPE_CODE_UNION
+         || VALUE_REPEATED (val))
+       value_print (val, stdout, format, Val_pretty_default);
+      else
+       print_scalar_formatted (VALUE_CONTENTS (val), VALUE_TYPE (val),
+                               format, size, stdout);
+    }
+}
+
+/* Print a scalar of data of type TYPE, pointed to in GDB by VALADDR,
+   according to letters FORMAT and SIZE on STREAM.
+   FORMAT may not be zero.  Formats s and i are not supported at this level.
+
+   This is how the elements of an array or structure are printed
+   with a format.  */
+
+void
+print_scalar_formatted (valaddr, type, format, size, stream)
+     char *valaddr;
+     struct type *type;
+     char format;
+     int size;
+     FILE *stream;
+{
+  LONGEST val_long;
+  int len = TYPE_LENGTH (type);
+
+  if (size == 'g' && sizeof (LONGEST) < 8
+      && format == 'x')
+    {
+      /* ok, we're going to have to get fancy here.  Assumption: a
+         long is four bytes.  */
+      unsigned long v1, v2, tmp;
+
+      v1 = unpack_long (builtin_type_long, valaddr);
+      v2 = unpack_long (builtin_type_long, valaddr + 4);
+
+#ifdef BYTES_BIG_ENDIAN
+#else
+      /* Little endian -- swap the two for printing */
+      tmp = v1;
+      v1 = v2;
+      v2 = tmp;
+#endif
+  
+      switch (format)
+       {
+       case 'x':
+         fprintf_filtered (stream, "0x%08x%08x", v1, v2);
+         break;
+       default:
+         error ("Output size \"g\" unimplemented for format \"%c\".",
+                format);
+       }
+      return;
+    }
+      
+  val_long = unpack_long (type, valaddr);
+
+  /* If value is unsigned, truncate it in case negative.  */
+  if (format != 'd')
+    {
+      if (len == sizeof (char))
+       val_long &= (1 << 8 * sizeof(char)) - 1;
+      else if (len == sizeof (short))
+       val_long &= (1 << 8 * sizeof(short)) - 1;
+      else if (len == sizeof (long))
+       val_long &= (unsigned long) - 1;
+    }
+
+  switch (format)
+    {
+    case 'x':
+#ifdef LONG_LONG
+      if (!size)
+       size = (len < sizeof (long long) ? 'w' : 'g');
+      switch (size)
+       {
+       case 'b':
+         fprintf_filtered (stream, "0x%02llx", val_long);
+         break;
+       case 'h':
+         fprintf_filtered (stream, "0x%04llx", val_long);
+         break;
+       case 0:         /* no size specified, like in print */
+       case 'w':
+         fprintf_filtered (stream, "0x%08llx", val_long);
+         break;
+       case 'g':
+         fprintf_filtered (stream, "0x%016llx", val_long);
+         break;
+       default:
+         error ("Undefined output size \"%c\".", size);
+       }
+#else 
+      switch (size)
+       {
+       case 'b':
+         fprintf_filtered (stream, "0x%02x", val_long);
+         break;
+       case 'h':
+         fprintf_filtered (stream, "0x%04x", val_long);
+         break;
+       case 0:         /* no size specified, like in print */
+       case 'w':
+         fprintf_filtered (stream, "0x%08x", val_long);
+         break;
+       case 'g':
+         fprintf_filtered (stream, "0x%o16x", val_long);
+         break;
+       default:
+         error ("Undefined output size \"%c\".", size);
+       }
+#endif /* not LONG_LONG */
+      break;
+
+    case 'd':
+#ifdef LONG_LONG
+      fprintf_filtered (stream, "%lld", val_long);
+#else
+      fprintf_filtered (stream, "%d", val_long);
+#endif
+      break;
+
+    case 'u':
+#ifdef LONG_LONG
+      fprintf_filtered (stream, "%llu", val_long);
+#else
+      fprintf_filtered (stream, "%u", val_long);
+#endif
+      break;
+
+    case 'o':
+      if (val_long)
+#ifdef LONG_LONG
+       fprintf_filtered (stream, "0%llo", val_long);
+#else
+       fprintf_filtered (stream, "0%o", val_long);
+#endif
+      else
+       fprintf_filtered (stream, "0");
+      break;
+
+    case 'a':
+      print_address ((CORE_ADDR) val_long, stream);
+      break;
+
+    case 'A':
+      print_formatted_address ((CORE_ADDR) val_long, stream);
+      break;
+
+    case 'c':
+      value_print (value_from_long (builtin_type_char, val_long), stream, 0,
+                  Val_pretty_default);
+      break;
+
+    case 'f':
+      if (len == sizeof (float))
+       type = builtin_type_float;
+      else if (len == sizeof (double))
+       type = builtin_type_double;
+      print_floating(valaddr, type, stream);
+      break;
+
+    case 0:
+      abort ();
+
+    default:
+      error ("Undefined output format \"%c\".", format);
+    }
+}
+
+/* Print a floating point value of type TYPE, pointed to in GDB by VALADDR,
+   on STREAM.  */
+
+void
+print_floating(valaddr, type, stream)
+     char *valaddr;
+     struct type *type;
+     FILE *stream;
+{
+  double doub;
+  int inv;
+  int len = TYPE_LENGTH (type);
+  
+  doub = unpack_double (type, valaddr, &inv);
+  if (inv)
+    fprintf_filtered (stream, "Invalid float value");
+  else if (doub != doub)
+    {
+      /* Surely it is an IEEE floating point NaN. */
+
+      long low, high, *arg = (long *)valaddr;  /* ASSUMED 32 BITS */
+      int nonneg;
+
+      if (len <= sizeof(float))
+       {
+         /* It's single precision. */
+         low = *arg;
+         nonneg  =  low >= 0;
+         low &= 0x7fffff;
+         high = 0;
+       }
+      else
+       {
+         /* It's double precision.
+            Get the high and low words of the fraction.
+            Distinguish big and little-endian machines.  */
+#ifdef WORDS_BIG_ENDIAN
+         low = arg[1], high = arg[0];
+#else
+         low = arg[0], high = arg[1];
+#endif
+         nonneg  =  high >= 0;
+         high &= 0xfffff;
+       }
+      if (high)
+       fprintf_filtered (stream, "-NaN(0x%lx%.8lx)" + nonneg, high, low);
+      else
+       fprintf_filtered (stream, "-NaN(0x%lx)" + nonneg, low);
+    }
+  else
+    fprintf_filtered (stream, len <= sizeof(float) ? "%.6g" : "%.17g", doub);
+}
+
+/* Specify default address for `x' command.
+   `info lines' uses this.  */
+
+void
+set_next_address (addr)
+     CORE_ADDR addr;
+{
+  next_address = addr;
+
+  /* Make address available to the user as $_.  */
+  set_internalvar (lookup_internalvar ("_"),
+                  value_from_long (builtin_type_int, (LONGEST) addr));
+}
+
+/* Optionally print address ADDR symbolically as <SYMBOL+OFFSET> on STREAM. */
+
+void
+print_address_symbolic (addr, stream)
+     CORE_ADDR addr;
+     FILE *stream;
+{
+  register char *format;
+  int name_location;
+  register int i = find_pc_misc_function (addr);
+
+  /* If nothing comes out, don't print anything symbolic.  */
+  if (i < 0) return;
+  name_location = misc_function_vector[i].address;
+
+  if (addr - name_location)
+    format = " <%s+%d>";
+  else
+    format = " <%s>";
+
+  fprintf_filtered (stream, format,
+                   misc_function_vector[i].name, addr - name_location);
+}
+
+/* Print address ADDR symbolically on STREAM.
+   First print it as a number.  Then perhaps print
+   <SYMBOL + OFFSET> after the number.  */
+
+void
+print_address (addr, stream)
+     CORE_ADDR addr;
+     FILE *stream;
+{
+  fprintf_filtered (stream, "0x%x", addr);
+  print_address_symbolic (addr, stream);
+}
+
+/* Like print_address but opnly prints symbolically. */
+
+void
+print_formatted_address (addr, stream)
+     CORE_ADDR addr;
+     FILE *stream;
+{
+  register int i = 0;
+  register char *format;
+  register struct symbol *fs;
+  char *name;
+  int name_location;
+
+  i = find_pc_partial_function (addr, &name, &name_location);
+
+  /* If nothing comes out, don't print anything symbolic.  */
+  
+  if (i == 0)
+    fprintf_filtered (stream, "0x%x", addr);
+  else if (addr - name_location)
+    fprintf_filtered (stream, "%s+%d", name, addr - name_location);
+  else
+    fprintf_filtered (stream, "%s", name);
+}
+\f
+/* Examine data at address ADDR in format FMT.
+   Fetch it from memory and print on stdout.  */
+
+static void
+do_examine (fmt, addr)
+     struct format_data fmt;
+     CORE_ADDR addr;
+{
+  register char format = 0;
+  register char size;
+  register int count = 1;
+  struct type *val_type;
+  register int i;
+  register int maxelts;
+
+  format = fmt.format;
+  size = fmt.size;
+  count = fmt.count;
+  next_address = addr;
+
+  /* String or instruction format implies fetch single bytes
+     regardless of the specified size.  */
+  if (format == 's' || format == 'i')
+    size = 'b';
+
+  if (size == 'b')
+    val_type = builtin_type_char;
+  else if (size == 'h')
+    val_type = builtin_type_short;
+  else if (size == 'w')
+    val_type = builtin_type_long;
+  else if (size == 'g')
+#ifndef LONG_LONG
+    val_type = builtin_type_double;
+#else
+    val_type = builtin_type_long_long;
+#endif
+
+  maxelts = 8;
+  if (size == 'w')
+    maxelts = 4;
+  if (size == 'g')
+    maxelts = 2;
+  if (format == 's' || format == 'i')
+    maxelts = 1;
+
+  /* Print as many objects as specified in COUNT, at most maxelts per line,
+     with the address of the next one at the start of each line.  */
+
+  while (count > 0)
+    {
+      print_address (next_address, stdout);
+      printf_filtered (":");
+      for (i = maxelts;
+          i > 0 && count > 0;
+          i--, count--)
+       {
+         printf_filtered ("\t");
+         /* Note that print_formatted sets next_address for the next
+            object.  */
+         last_examine_address = next_address;
+         last_examine_value = value_at (val_type, next_address);
+         print_formatted (last_examine_value, format, size);
+       }
+      printf_filtered ("\n");
+      fflush (stdout);
+    }
+}
+\f
+static void
+validate_format (fmt, cmdname)
+     struct format_data fmt;
+     char *cmdname;
+{
+  if (fmt.size != 0)
+    error ("Size letters are meaningless in \"%s\" command.", cmdname);
+  if (fmt.count != 1)
+    error ("Item count other than 1 is meaningless in \"%s\" command.",
+          cmdname);
+  if (fmt.format == 'i' || fmt.format == 's')
+    error ("Format letter \"%c\" is meaningless in \"%s\" command.",
+          fmt.format, cmdname);
+}
+
+static void
+print_command (exp)
+     char *exp;
+{
+  struct expression *expr;
+  register struct cleanup *old_chain = 0;
+  register char format = 0;
+  register value val;
+  struct format_data fmt;
+  int histindex;
+  int cleanup = 0;
+
+  if (exp && *exp == '/')
+    {
+      exp++;
+      fmt = decode_format (&exp, last_format, 0);
+      validate_format (fmt, "print");
+      last_format = format = fmt.format;
+    }
+
+  if (exp && *exp)
+    {
+      expr = parse_c_expression (exp);
+      old_chain = make_cleanup (free_current_contents, &expr);
+      cleanup = 1;
+      val = evaluate_expression (expr);
+    }
+  else
+    val = access_value_history (0);
+
+  histindex = record_latest_value (val);
+  if (histindex >= 0) printf_filtered ("$%d = ", histindex);
+
+  print_formatted (val, format, fmt.size);
+  printf_filtered ("\n");
+
+  if (cleanup)
+    do_cleanups (old_chain);
+}
+
+static void
+output_command (exp)
+     char *exp;
+{
+  struct expression *expr;
+  register struct cleanup *old_chain;
+  register char format = 0;
+  register value val;
+  struct format_data fmt;
+
+  if (exp && *exp == '/')
+    {
+      exp++;
+      fmt = decode_format (&exp, 0, 0);
+      validate_format (fmt, "print");
+      format = fmt.format;
+    }
+
+  expr = parse_c_expression (exp);
+  old_chain = make_cleanup (free_current_contents, &expr);
+
+  val = evaluate_expression (expr);
+
+  print_formatted (val, format, fmt.size);
+
+  do_cleanups (old_chain);
+}
+
+static void
+set_command (exp)
+     char *exp;
+{
+  struct expression *expr = parse_c_expression (exp);
+  register struct cleanup *old_chain
+    = make_cleanup (free_current_contents, &expr);
+  evaluate_expression (expr);
+  do_cleanups (old_chain);
+}
+
+static void
+address_info (exp)
+     char *exp;
+{
+  register struct symbol *sym;
+  register CORE_ADDR val;
+  int is_a_field_of_this;      /* C++: lookup_symbol sets this to nonzero
+                                  if exp is a field of `this'. */
+
+  if (exp == 0)
+    error ("Argument required.");
+
+  sym = lookup_symbol (exp, get_selected_block (), VAR_NAMESPACE, 
+                      &is_a_field_of_this);
+  if (sym == 0)
+    {
+      register int i;
+
+      if (is_a_field_of_this)
+       {
+         printf ("Symbol \"%s\" is a field of the local class variable `this'\n", exp);
+         return;
+       }
+
+      for (i = 0; i < misc_function_count; i++)
+       if (!strcmp (misc_function_vector[i].name, exp))
+         break;
+
+      if (i < misc_function_count)
+       printf ("Symbol \"%s\" is at 0x%x in a file compiled without -g.\n",
+               exp, misc_function_vector[i].address);
+      else
+       error ("No symbol \"%s\" in current context.", exp);
+      return;
+    }
+
+  printf ("Symbol \"%s\" is ", SYMBOL_NAME (sym));
+  val = SYMBOL_VALUE (sym);
+
+  switch (SYMBOL_CLASS (sym))
+    {
+    case LOC_CONST:
+    case LOC_CONST_BYTES:
+      printf ("constant");
+      break;
+
+    case LOC_LABEL:
+      printf ("a label at address 0x%x", val);
+      break;
+
+    case LOC_REGISTER:
+      printf ("a variable in register %s", reg_names[val]);
+      break;
+
+    case LOC_STATIC:
+      printf ("static at address 0x%x", val);
+      break;
+
+    case LOC_REGPARM:
+      printf ("an argument in register %s", reg_names[val]);
+      break;
+      
+    case LOC_ARG:
+      printf ("an argument at offset %d", val);
+      break;
+
+    case LOC_LOCAL:
+      printf ("a local variable at frame offset %d", val);
+      break;
+
+    case LOC_REF_ARG:
+      printf ("a reference argument at offset %d", val);
+      break;
+
+    case LOC_TYPEDEF:
+      printf ("a typedef");
+      break;
+
+    case LOC_BLOCK:
+      printf ("a function at address 0x%x",
+             BLOCK_START (SYMBOL_BLOCK_VALUE (sym)));
+      break;
+    }
+  printf (".\n");
+}
+\f
+static void
+x_command (exp, from_tty)
+     char *exp;
+     int from_tty;
+{
+  struct expression *expr;
+  struct format_data fmt;
+  struct cleanup *old_chain;
+  struct value *val;
+
+  fmt.format = last_format;
+  fmt.size = last_size;
+  fmt.count = 1;
+
+  if (exp && *exp == '/')
+    {
+      exp++;
+      fmt = decode_format (&exp, last_format, last_size);
+      last_size = fmt.size;
+      last_format = fmt.format;
+    }
+
+  /* If we have an expression, evaluate it and use it as the address.  */
+
+  if (exp != 0 && *exp != 0)
+    {
+      expr = parse_c_expression (exp);
+      /* Cause expression not to be there any more
+        if this command is repeated with Newline.
+        But don't clobber a user-defined command's definition.  */
+      if (from_tty)
+       *exp = 0;
+      old_chain = make_cleanup (free_current_contents, &expr);
+      val = evaluate_expression (expr);
+      /* In rvalue contexts, such as this, functions are coerced into
+        pointers to functions.  This makes "x/i main" work.  */
+      if (/* last_format == 'i'
+         && */ TYPE_CODE (VALUE_TYPE (val)) == TYPE_CODE_FUNC
+         && VALUE_LVAL (val) == lval_memory)
+       next_address = VALUE_ADDRESS (val);
+      else
+       next_address = (CORE_ADDR) value_as_long (val);
+      do_cleanups (old_chain);
+    }
+
+  do_examine (fmt, next_address);
+
+  /* Set a couple of internal variables if appropriate. */
+  if (last_examine_value)
+    {
+      /* Make last address examined available to the user as $_.  */
+      set_internalvar (lookup_internalvar ("_"),
+                      value_from_long (builtin_type_int, 
+                                       (LONGEST) last_examine_address));
+      
+      /* Make contents of last address examined available to the user as $__.*/
+      set_internalvar (lookup_internalvar ("__"), last_examine_value);
+    }
+}
+\f
+/* Commands for printing types of things.  */
+
+static void
+whatis_command (exp)
+     char *exp;
+{
+  struct expression *expr;
+  register value val;
+  register struct cleanup *old_chain;
+
+  if (exp)
+    {
+      expr = parse_c_expression (exp);
+      old_chain = make_cleanup (free_current_contents, &expr);
+      val = evaluate_type (expr);
+    }
+  else
+    val = access_value_history (0);
+
+  printf_filtered ("type = ");
+  /* Most of the time users do not want to see all the fields
+     in a structure.  If they do they can use the "ptype" command.
+     Hence the "-1" below.  */
+  type_print (VALUE_TYPE (val), "", stdout, -1);
+  printf_filtered ("\n");
+
+  if (exp)
+    do_cleanups (old_chain);
+}
+
+static void
+ptype_command (typename)
+     char *typename;
+{
+  register char *p = typename;
+  register int len;
+  extern struct block *get_current_block ();
+  register struct block *b
+    = (have_inferior_p () || have_core_file_p ()) ? get_current_block () : 0;
+  register struct type *type;
+
+  if (typename == 0)
+    error_no_arg ("type name");
+
+  while (*p && *p != ' ' && *p != '\t') p++;
+  len = p - typename;
+  while (*p == ' ' || *p == '\t') p++;
+
+  if (len == 6 && !strncmp (typename, "struct", 6))
+    type = lookup_struct (p, b);
+  else if (len == 5 && !strncmp (typename, "union", 5))
+    type = lookup_union (p, b);
+  else if (len == 4 && !strncmp (typename, "enum", 4))
+    type = lookup_enum (p, b);
+  else
+    {
+      type = lookup_typename (typename, b, 1);
+      if (type == 0)
+       {
+         register struct symbol *sym
+           = lookup_symbol (typename, b, STRUCT_NAMESPACE, 0);
+         if (sym == 0)
+           error ("No type named %s.", typename);
+         printf_filtered ("No type named %s, but there is a ",
+                 typename);
+         switch (TYPE_CODE (SYMBOL_TYPE (sym)))
+           {
+           case TYPE_CODE_STRUCT:
+             printf_filtered ("struct");
+             break;
+
+           case TYPE_CODE_UNION:
+             printf_filtered ("union");
+             break;
+
+           case TYPE_CODE_ENUM:
+             printf_filtered ("enum");
+           }
+         printf_filtered (" %s.  Type \"help ptype\".\n", typename);
+         type = SYMBOL_TYPE (sym);
+       }
+    }
+
+  type_print (type, "", stdout, 1);
+  printf_filtered ("\n");
+}
+\f
+enum display_status {disabled, enabled};
+
+struct display
+{
+  /* Chain link to next auto-display item.  */
+  struct display *next;
+  /* Expression to be evaluated and displayed.  */
+  struct expression *exp;
+  /* Item number of this auto-display item.  */
+  int number;
+  /* Display format specified.  */
+  struct format_data format;
+  /* Innermost block required by this expression when evaluated */
+  struct block *block;
+  /* Status of this display (enabled or disabled) */
+  enum display_status status;
+};
+
+/* Chain of expressions whose values should be displayed
+   automatically each time the program stops.  */
+
+static struct display *display_chain;
+
+static int display_number;
+
+/* Add an expression to the auto-display chain.
+   Specify the expression.  */
+
+static void
+display_command (exp, from_tty)
+     char *exp;
+     int from_tty;
+{
+  struct format_data fmt;
+  register struct expression *expr;
+  register struct display *new;
+  extern struct block *innermost_block;
+
+  if (exp == 0)
+    {
+      do_displays ();
+      return;
+    }
+
+  if (*exp == '/')
+    {
+      exp++;
+      fmt = decode_format (&exp, 0, 0);
+      if (fmt.size && fmt.format == 0)
+       fmt.format = 'x';
+      if (fmt.format == 'i' || fmt.format == 's')
+       fmt.size = 'b';
+    }
+  else
+    {
+      fmt.format = 0;
+      fmt.size = 0;
+      fmt.count = 0;
+    }
+
+  innermost_block = 0;
+  expr = parse_c_expression (exp);
+
+  new = (struct display *) xmalloc (sizeof (struct display));
+
+  new->exp = expr;
+  new->block = innermost_block;
+  new->next = display_chain;
+  new->number = ++display_number;
+  new->format = fmt;
+  new->status = enabled;
+  display_chain = new;
+
+  if (from_tty && have_inferior_p ())
+    do_one_display (new);
+
+  dont_repeat ();
+}
+
+static void
+free_display (d)
+     struct display *d;
+{
+  free (d->exp);
+  free (d);
+}
+
+/* Clear out the display_chain.
+   Done when new symtabs are loaded, since this invalidates
+   the types stored in many expressions.  */
+
+void
+clear_displays ()
+{
+  register struct display *d;
+
+  while (d = display_chain)
+    {
+      free (d->exp);
+      display_chain = d->next;
+      free (d);
+    }
+}
+
+/* Delete the auto-display number NUM.  */
+
+void
+delete_display (num)
+     int num;
+{
+  register struct display *d1, *d;
+
+  if (!display_chain)
+    error ("No display number %d.", num);
+
+  if (display_chain->number == num)
+    {
+      d1 = display_chain;
+      display_chain = d1->next;
+      free_display (d1);
+    }
+  else
+    for (d = display_chain; ; d = d->next)
+      {
+       if (d->next == 0)
+         error ("No display number %d.", num);
+       if (d->next->number == num)
+         {
+           d1 = d->next;
+           d->next = d1->next;
+           free_display (d1);
+           break;
+         }
+      }
+}
+
+/* Delete some values from the auto-display chain.
+   Specify the element numbers.  */
+
+static void
+undisplay_command (args)
+     char *args;
+{
+  register char *p = args;
+  register char *p1;
+  register int num;
+  register struct display *d, *d1;
+
+  if (args == 0)
+    {
+      if (query ("Delete all auto-display expressions? "))
+       clear_displays ();
+      dont_repeat ();
+      return;
+    }
+
+  while (*p)
+    {
+      p1 = p;
+      while (*p1 >= '0' && *p1 <= '9') p1++;
+      if (*p1 && *p1 != ' ' && *p1 != '\t')
+       error ("Arguments must be display numbers.");
+
+      num = atoi (p);
+
+      delete_display (num);
+
+      p = p1;
+      while (*p == ' ' || *p == '\t') p++;
+    }
+  dont_repeat ();
+}
+
+/* Display a single auto-display.  
+   Do nothing if the display cannot be printed in the current context,
+   or if the display is disabled. */
+
+static void
+do_one_display (d)
+     struct display *d;
+{
+  int within_current_scope;
+
+  if (d->status == disabled)
+    return;
+
+  if (d->block)
+    within_current_scope = contained_in (get_selected_block (), d->block);
+  else
+    within_current_scope = 1;
+  if (!within_current_scope)
+    return;
+
+  current_display_number = d->number;
+
+  printf_filtered ("%d: ", d->number);
+  if (d->format.size)
+    {
+      printf_filtered ("x/");
+      if (d->format.count != 1)
+       printf_filtered ("%d", d->format.count);
+      printf_filtered ("%c", d->format.format);
+      if (d->format.format != 'i' && d->format.format != 's')
+       printf_filtered ("%c", d->format.size);
+      printf_filtered (" ");
+      print_expression (d->exp, stdout);
+      if (d->format.count != 1)
+       printf_filtered ("\n");
+      else
+       printf_filtered ("  ");
+      do_examine (d->format,
+                 (CORE_ADDR) value_as_long (evaluate_expression (d->exp)));
+
+    }
+  else
+    {
+      if (d->format.format)
+       printf_filtered ("/%c ", d->format.format);
+      print_expression (d->exp, stdout);
+      printf_filtered (" = ");
+      print_formatted (evaluate_expression (d->exp),
+                      d->format.format, d->format.size);
+      printf_filtered ("\n");
+    }
+
+  fflush (stdout);
+  current_display_number = -1;
+}
+
+/* Display all of the values on the auto-display chain which can be
+   evaluated in the current scope.  */
+
+void
+do_displays ()
+{
+  register struct display *d;
+
+  for (d = display_chain; d; d = d->next)
+    do_one_display (d);
+}
+
+/* Delete the auto-display which we were in the process of displaying.
+   This is done when there is an error or a signal.  */
+
+void
+disable_display (num)
+     int num;
+{
+  register struct display *d;
+
+  for (d = display_chain; d; d = d->next)
+    if (d->number == num)
+      {
+       d->status = disabled;
+       return;
+      }
+  printf ("No display number %d.\n", num);
+}
+  
+void
+disable_current_display ()
+{
+  if (current_display_number >= 0)
+    {
+      disable_display (current_display_number);
+      fprintf (stderr, "Disabling display %d to avoid infinite recursion.\n",
+              current_display_number);
+    }
+  current_display_number = -1;
+}
+
+static void
+display_info ()
+{
+  register struct display *d;
+
+  if (!display_chain)
+    printf ("There are no auto-display expressions now.\n");
+  else
+      printf_filtered ("Auto-display expressions now in effect:\n\
+Num Enb Expression\n");
+
+  for (d = display_chain; d; d = d->next)
+    {
+      printf_filtered ("%d:   %c  ", d->number, "ny"[(int)d->status]);
+      if (d->format.size)
+       printf_filtered ("/%d%c%c ", d->format.count, d->format.size,
+               d->format.format);
+      else if (d->format.format)
+       printf_filtered ("/%c ", d->format.format);
+      print_expression (d->exp, stdout);
+      if (d->block && !contained_in (get_selected_block (), d->block))
+       printf_filtered (" (cannot be evaluated in the current context)");
+      printf_filtered ("\n");
+      fflush (stdout);
+    }
+}
+
+void
+enable_display (args)
+     char *args;
+{
+  register char *p = args;
+  register char *p1;
+  register int num;
+  register struct display *d;
+
+  if (p == 0)
+    {
+      for (d = display_chain; d; d = d->next)
+       d->status = enabled;
+    }
+  else
+    while (*p)
+      {
+       p1 = p;
+       while (*p1 >= '0' && *p1 <= '9')
+         p1++;
+       if (*p1 && *p1 != ' ' && *p1 != '\t')
+         error ("Arguments must be display numbers.");
+       
+       num = atoi (p);
+       
+       for (d = display_chain; d; d = d->next)
+         if (d->number == num)
+           {
+             d->status = enabled;
+             goto win;
+           }
+       printf ("No display number %d.\n", num);
+      win:
+       p = p1;
+       while (*p == ' ' || *p == '\t')
+         p++;
+      }
+}
+
+void
+disable_display_command (args, from_tty)
+     char *args;
+     int from_tty;
+{
+  register char *p = args;
+  register char *p1;
+  register int num;
+  register struct display *d;
+
+  if (p == 0)
+    {
+      for (d = display_chain; d; d = d->next)
+       d->status = disabled;
+    }
+  else
+    while (*p)
+      {
+       p1 = p;
+       while (*p1 >= '0' && *p1 <= '9')
+         p1++;
+       if (*p1 && *p1 != ' ' && *p1 != '\t')
+         error ("Arguments must be display numbers.");
+       
+       num = atoi (p);
+       
+       disable_display (atoi (p));
+
+       p = p1;
+       while (*p == ' ' || *p == '\t')
+         p++;
+      }
+}
+
+\f
+/* Print the value in stack frame FRAME of a variable
+   specified by a struct symbol.  */
+
+void
+print_variable_value (var, frame, stream)
+     struct symbol *var;
+     FRAME frame;
+     FILE *stream;
+{
+  value val = read_var_value (var, frame);
+  value_print (val, stream, 0, Val_pretty_default);
+}
+
+static int
+compare_ints (i, j)
+     int *i, *j;
+{
+  return *i - *j;
+}
+
+/* Print the arguments of a stack frame, given the function FUNC
+   running in that frame (as a symbol), the info on the frame,
+   and the number of args according to the stack frame (or -1 if unknown).  */
+
+static void print_frame_nameless_args ();
+
+void
+print_frame_args (func, fi, num, stream)
+     struct symbol *func;
+     struct frame_info *fi;
+     int num;
+     FILE *stream;
+{
+  struct block *b;
+  int nsyms = 0;
+  int first = 1;
+  register int i;
+  register int last_regparm = 0;
+  register struct symbol *lastsym, *sym, *nextsym;
+  register value val;
+  /* Offset of stack argument that is at the highest offset.
+     -1 if we haven't come to a stack argument yet.  */
+  CORE_ADDR highest_offset = (CORE_ADDR) -1;
+  register CORE_ADDR addr = FRAME_ARGS_ADDRESS (fi);
+
+  if (func)
+    {
+      b = SYMBOL_BLOCK_VALUE (func);
+      nsyms = BLOCK_NSYMS (b);
+    }
+
+  for (i = 0; i < nsyms; i++)
+    {
+      QUIT;
+      sym = BLOCK_SYM (b, i);
+
+      if (SYMBOL_CLASS (sym) != LOC_REGPARM
+         && SYMBOL_CLASS (sym) != LOC_ARG
+         && SYMBOL_CLASS (sym) != LOC_REF_ARG)
+       continue;
+
+      /* Print the next arg.  */
+      if (SYMBOL_CLASS (sym) == LOC_REGPARM)
+       val = value_from_register (SYMBOL_TYPE (sym),
+                                  SYMBOL_VALUE (sym),
+                                  FRAME_INFO_ID (fi));
+      else
+       {
+         int current_offset = SYMBOL_VALUE (sym);
+         int arg_size = TYPE_LENGTH (SYMBOL_TYPE (sym));
+         
+         if (SYMBOL_CLASS (sym) == LOC_REF_ARG)
+           val = value_at (SYMBOL_TYPE (sym),
+                           read_memory_integer (addr + current_offset,
+                                                sizeof (CORE_ADDR)));
+         else
+           val = value_at (SYMBOL_TYPE (sym), addr + current_offset);
+
+         /* Round up address of next arg to multiple of size of int.  */
+         current_offset
+           = (((current_offset + sizeof (int) - 1) / sizeof (int))
+              * sizeof (int));
+
+         /* If this is the highest offset seen yet, set highest_offset.  */
+         if (highest_offset == (CORE_ADDR)-1
+             || ((current_offset
+                  + (arg_size - sizeof (int) + 3) / (sizeof (int)))
+                 > highest_offset))
+           highest_offset = current_offset;
+       }
+
+      if (! first)
+       fprintf_filtered (stream, ", ");
+      fputs_filtered (SYMBOL_NAME (sym), stream);
+      fputs_filtered ("=", stream);
+
+/* Nonzero if a LOC_ARG which is a struct is useless.  */
+#if !defined (STRUCT_ARG_SYM_GARBAGE)
+#define STRUCT_ARG_SYM_GARBAGE(gcc_p) 0
+#endif
+
+      if (STRUCT_ARG_SYM_GARBAGE (b->gcc_compile_flag)
+         && TYPE_CODE (SYMBOL_TYPE (sym)) == TYPE_CODE_STRUCT
+         && SYMBOL_CLASS (sym) == LOC_ARG)
+       {
+         /* Try looking up that name.  SunOS4 puts out a usable
+            symbol as a local variable (in addition to the one
+            for the arg).  */
+         struct symbol *sym2 =
+           lookup_symbol (SYMBOL_NAME (sym), b, VAR_NAMESPACE, 0);
+
+         if (sym2 != NULL)
+           val = value_of_variable (sym2);
+         else
+           {
+             fputs_filtered ("?", stream);
+             first = 0;
+             continue;
+           }
+       }
+
+      value_print (val, stream, 0, Val_no_prettyprint);
+      first = 0;
+    }
+
+  /* Don't print nameless args in situations where we don't know
+     enough about the stack to find them.  */
+  if (num != -1)
+    {
+      if (highest_offset != (CORE_ADDR) -1
+         && num * sizeof (int) + FRAME_ARGS_SKIP > highest_offset)
+       print_frame_nameless_args (fi, addr,
+                                  highest_offset + sizeof (int),
+                                  num * sizeof (int) + FRAME_ARGS_SKIP,
+                                  stream);
+      else 
+       print_frame_nameless_args (fi, addr, FRAME_ARGS_SKIP,
+                                  num * sizeof (int) + FRAME_ARGS_SKIP,
+                                  stream);
+    }
+}
+
+static void
+print_frame_nameless_args (fi, argsaddr, start, end, stream)
+     struct frame_info *fi;
+     CORE_ADDR argsaddr;
+     int start;
+     int end;
+     FILE *stream;
+{
+       extern void (*default_scalar_print)();
+       LONGEST v;
+       int p = start;
+       char *s = "";
+
+       for (p = start; p < end; p += sizeof(int)) {
+               QUIT;
+#if defined(NAMELESS_ARG)
+               v = NAMELESS_ARG(fi, (p - start) / sizeof(int));
+#else
+               v = read_memory_integer (argsaddr + p, sizeof (int));
+#endif
+               fprintf_filtered (stream, s);
+               s = ", ";
+               (*default_scalar_print) (stream, builtin_type_int, v);
+       }
+}
+\f
+static void
+printf_command (arg)
+     char *arg;
+{
+  register char *f;
+  register char *s = arg;
+  char *string;
+  value *val_args;
+  int nargs = 0;
+  int allocated_args = 20;
+  char *arg_bytes;
+
+  val_args = (value *) xmalloc (allocated_args * sizeof (value));
+
+  if (s == 0)
+    error_no_arg ("format-control string and values to print");
+
+  /* Skip white space before format string */
+  while (*s == ' ' || *s == '\t') s++;
+
+  /* A format string should follow, enveloped in double quotes */
+  if (*s++ != '"')
+    error ("Bad format string, missing '\"'.");
+
+  /* Parse the format-control string and copy it into the string STRING,
+     processing some kinds of escape sequence.  */
+
+  f = string = (char *) alloca (strlen (s) + 1);
+  while (*s != '"')
+    {
+      int c = *s++;
+      switch (c)
+       {
+       case '\0':
+         error ("Bad format string, non-terminated '\"'.");
+         /* doesn't return */
+
+       case '\\':
+         switch (c = *s++)
+           {
+           case '\\':
+             *f++ = '\\';
+             break;
+           case 'n':
+             *f++ = '\n';
+             break;
+           case 't':
+             *f++ = '\t';
+             break;
+           case 'r':
+             *f++ = '\r';
+             break;
+           case '"':
+             *f++ = '"';
+             break;
+           default:
+             /* ??? TODO: handle other escape sequences */
+             error ("Unrecognized \\ escape character in format string.");
+           }
+         break;
+
+       default:
+         *f++ = c;
+       }
+    }
+
+  /* Skip over " and following space and comma.  */
+  s++;
+  *f++ = '\0';
+  while (*s == ' ' || *s == '\t') s++;
+
+  if (*s != ',' && *s != 0)
+    error ("Invalid argument syntax");
+
+  if (*s == ',') s++;
+  while (*s == ' ' || *s == '\t') s++;
+
+  {
+    /* Now scan the string for %-specs and see what kinds of args they want.
+       argclass[I] classifies the %-specs so we can give vprintf something
+       of the right size.  */
+    enum argclass {int_arg, string_arg, double_arg, long_long_arg};
+    enum argclass *argclass;
+    int nargs_wanted;
+    int argindex;
+    int lcount;
+    int i;
+    argclass = (enum argclass *) alloca (strlen (s) * sizeof *argclass);
+    nargs_wanted = 0;
+    f = string;
+    while (*f)
+      if (*f++ == '%')
+       {
+         lcount = 0;
+         while (index ("0123456789.hlL-+ #", *f)) 
+           {
+             if (*f == 'l' || *f == 'L')
+               lcount++;
+             f++;
+           }
+         if (*f == 's')
+           argclass[nargs_wanted++] = string_arg;
+         else if (*f == 'e' || *f == 'f' || *f == 'g')
+           argclass[nargs_wanted++] = double_arg;
+         else if (lcount > 1)
+           argclass[nargs_wanted++] = long_long_arg;
+         else if (*f != '%')
+           argclass[nargs_wanted++] = int_arg;
+         f++;
+       }
+    /* Now, parse all arguments and evaluate them.
+       Store the VALUEs in VAL_ARGS.  */
+    while (*s != '\0')
+      {
+       char *s1;
+       if (nargs == allocated_args)
+         val_args = (value *) xrealloc (val_args,
+                                        (allocated_args *= 2)
+                                        * sizeof (value));
+       s1 = s;
+       val_args[nargs] = parse_to_comma_and_eval (&s1);
+       /* If format string wants a float, unchecked-convert the value to
+          floating point of the same size */
+       if (argclass[nargs] == double_arg)
+         {
+           if (TYPE_LENGTH (VALUE_TYPE (val_args[nargs])) == sizeof (float))
+             VALUE_TYPE (val_args[nargs]) = builtin_type_float;
+           if (TYPE_LENGTH (VALUE_TYPE (val_args[nargs])) == sizeof (double))
+             VALUE_TYPE (val_args[nargs]) = builtin_type_double;
+         }
+       nargs++;
+       s = s1;
+       if (*s == ',')
+         s++;
+      }
+    if (nargs != nargs_wanted)
+      error ("Wrong number of arguments for specified format-string");
+    /* Now lay out an argument-list containing the arguments
+       as doubles, integers and C pointers.  */
+    arg_bytes = (char *) alloca (sizeof (double) * nargs);
+    argindex = 0;
+    for (i = 0; i < nargs; i++)
+      {
+       if (argclass[i] == string_arg)
+         {
+           char *str;
+           int tem, j;
+           tem = value_as_long (val_args[i]);
+           /* This is a %s argument.  Find the length of the string.  */
+           for (j = 0; ; j++)
+             {
+               char c;
+               QUIT;
+               read_memory (tem + j, &c, 1);
+               if (c == 0)
+                 break;
+             }
+           /* Copy the string contents into a string inside GDB.  */
+           str = (char *) alloca (j + 1);
+           read_memory (tem, str, j);
+           str[j] = 0;
+           /* Pass address of internal copy as the arg to vprintf.  */
+           *((int *) &arg_bytes[argindex]) = (int) str;
+           argindex += sizeof (int);
+         }
+       else if (VALUE_TYPE (val_args[i])->code == TYPE_CODE_FLT)
+         {
+           *((double *) &arg_bytes[argindex]) = value_as_double (val_args[i]);
+           argindex += sizeof (double);
+         }
+       else
+#ifdef LONG_LONG
+         if (argclass[i] == long_long_arg)
+           {
+             *(long long *) &arg_bytes[argindex] = value_as_long (val_args[i]);
+             argindex += sizeof (long long);
+           }
+         else
+#endif
+           {
+             *((int *) &arg_bytes[argindex]) = value_as_long (val_args[i]);
+             argindex += sizeof (int);
+           }
+      }
+  }
+  vprintf (string, arg_bytes);
+}
+\f
+/* Helper function for asdump_command.  Finds the bounds of a function
+   for a specified section of text.  PC is an address within the
+   function which you want bounds for; *LOW and *HIGH are set to the
+   beginning (inclusive) and end (exclusive) of the function.  This
+   function returns 1 on success and 0 on failure.  */
+
+static int
+containing_function_bounds (pc, low, high)
+     CORE_ADDR pc, *low, *high;
+{
+  int scan;
+
+  if (!find_pc_partial_function (pc, 0, low))
+    return 0;
+
+  scan = *low;
+  do {
+    scan++;
+    if (!find_pc_partial_function (scan, 0, high))
+      return 0;
+  } while (*low == *high);
+
+  return 1;
+}
+
+/* Dump a specified section of assembly code.  With no command line
+   arguments, this command will dump the assembly code for the
+   function surrounding the pc value in the selected frame.  With one
+   argument, it will dump the assembly code surrounding that pc value.
+   Two arguments are interpeted as bounds within which to dump
+   assembly.  */
+
+static void
+disassemble_command (arg, from_tty)
+     char *arg;
+     int from_tty;
+{
+  CORE_ADDR low, high;
+  CORE_ADDR pc;
+  char *space_index;
+
+  if (!arg)
+    {
+      if (!selected_frame)
+       error ("No frame selected.\n");
+
+      pc = get_frame_pc (selected_frame);
+      if (!containing_function_bounds (pc, &low, &high))
+       error ("No function contains pc specified by selected frame.\n");
+    }
+  else if (!(space_index = (char *) index (arg, ' ')))
+    {
+      /* One argument.  */
+      pc = parse_and_eval_address (arg);
+      if (!containing_function_bounds (pc, &low, &high))
+       error ("No function contains specified pc.\n");
+    }
+  else
+    {
+      /* Two arguments.  */
+      *space_index = '\0';
+      low = parse_and_eval_address (arg);
+      high = parse_and_eval_address (space_index + 1);
+    }
+
+  printf_filtered ("Dump of assembler code ");
+  if (!space_index)
+    {
+      char *name;
+      find_pc_partial_function (pc, &name, 0);
+      printf_filtered ("for function %s:\n", name);
+    }
+  else
+    printf_filtered ("from 0x%x to 0x%x:\n", low, high);
+
+  /* Dump the specified range.  */
+  for (pc = low; pc < high; )
+    {
+      QUIT;
+      print_address (pc, stdout);
+      printf_filtered (":\t");
+      pc += print_insn (pc, stdout);
+      printf_filtered ("\n");
+    }
+  printf_filtered ("End of assembler dump.\n");
+  fflush (stdout);
+}
+
+\f
+extern struct cmd_list_element *enablelist, *disablelist, *deletelist;
+extern struct cmd_list_element *cmdlist, *setlist;
+
+void
+_initialize_printcmd ()
+{
+  current_display_number = -1;
+
+  add_info ("address", address_info,
+          "Describe where variable VAR is stored.");
+
+  add_com ("x", class_vars, x_command,
+          "Examine memory: x/FMT ADDRESS.\n\
+ADDRESS is an expression for the memory address to examine.\n\
+FMT is a repeat count followed by a format letter and a size letter.\n\
+Format letters are o(octal), x(hex), d(decimal), u(unsigned decimal),\n\
+ f(float), a(address), i(instruction), c(char) and s(string).\n\
+Size letters are b(byte), h(halfword), w(word), g(giant, 8 bytes).\n\
+  g is meaningful only with f, for type double.\n\
+The specified number of objects of the specified size are printed\n\
+according to the format.\n\n\
+Defaults for format and size letters are those previously used.\n\
+Default count is 1.  Default address is following last thing printed\n\
+with this command or \"print\".");
+
+  add_com ("disassemble", class_vars, disassemble_command,
+          "Disassemble a specified section of memory.\n\
+Default is the function surrounding the pc of the selected frame.\n\
+With a single argument, the function surrounding that address is dumped.\n\
+Two arguments are taken as a range of memory to dump.");
+
+  add_com ("ptype", class_vars, ptype_command,
+          "Print definition of type TYPE.\n\
+Argument may be a type name defined by typedef, or \"struct STRUCTNAME\"\n\
+or \"union UNIONNAME\" or \"enum ENUMNAME\".\n\
+The selected stack frame's lexical context is used to look up the name.");
+
+  add_com ("whatis", class_vars, whatis_command,
+          "Print data type of expression EXP.");
+
+  add_info ("display", display_info,
+           "Expressions to display when program stops, with code numbers.");
+
+  add_cmd ("undisplay", class_vars, undisplay_command,
+          "Cancel some expressions to be displayed when program stops.\n\
+Arguments are the code numbers of the expressions to stop displaying.\n\
+No argument means cancel all automatic-display expressions.\n\
+\"delete display\" has the same effect as this command.\n\
+Do \"info display\" to see current list of code numbers.",
+                 &cmdlist);
+
+  add_com ("display", class_vars, display_command,
+          "Print value of expression EXP each time the program stops.\n\
+/FMT may be used before EXP as in the \"print\" command.\n\
+/FMT \"i\" or \"s\" or including a size-letter is allowed,\n\
+as in the \"x\" command, and then EXP is used to get the address to examine\n\
+and examining is done as in the \"x\" command.\n\n\
+With no argument, display all currently requested auto-display expressions.\n\
+Use \"undisplay\" to cancel display requests previously made.");
+
+  add_cmd ("display", class_vars, enable_display, 
+          "Enable some expressions to be displayed when program stops.\n\
+Arguments are the code numbers of the expressions to resume displaying.\n\
+No argument means enable all automatic-display expressions.\n\
+Do \"info display\" to see current list of code numbers.", &enablelist);
+
+  add_cmd ("display", class_vars, disable_display_command, 
+          "Disable some expressions to be displayed when program stops.\n\
+Arguments are the code numbers of the expressions to stop displaying.\n\
+No argument means disable all automatic-display expressions.\n\
+Do \"info display\" to see current list of code numbers.", &disablelist);
+
+  add_cmd ("display", class_vars, undisplay_command, 
+          "Cancel some expressions to be displayed when program stops.\n\
+Arguments are the code numbers of the expressions to stop displaying.\n\
+No argument means cancel all automatic-display expressions.\n\
+Do \"info display\" to see current list of code numbers.", &deletelist);
+
+  add_com ("printf", class_vars, printf_command,
+       "printf \"printf format string\", arg1, arg2, arg3, ..., argn\n\
+This is useful for formatted output in user-defined commands.");
+  add_com ("output", class_vars, output_command,
+          "Like \"print\" but don't put in value history and don't print newline.\n\
+This is useful in user-defined commands.");
+
+  add_prefix_cmd ("set", class_vars, set_command,
+"Perform an assignment VAR = EXP.\n\
+You must type the \"=\".  VAR may be a debugger \"convenience\" variable\n\
+(names starting with $), a register (a few standard names starting with $),\n\
+or an actual variable in the program being debugged.  EXP is any expression.\n\
+Use \"set variable\" for variables with names identical to set subcommands.\n\
+\nWith a subcommand, this command modifies parts of the gdb environment",
+                  &setlist, "set ", 1, &cmdlist);
+
+  add_cmd ("variable", class_vars, set_command,
+           "Perform an assignment VAR = EXP.\n\
+You must type the \"=\".  VAR may be a debugger \"convenience\" variable\n\
+(names starting with $), a register (a few standard names starting with $),\n\
+or an actual variable in the program being debugged.  EXP is any expression.\n\
+This may usually be abbreviated to simply \"set\".",
+           &setlist);
+
+  add_com ("print", class_vars, print_command,
+          concat ("Print value of expression EXP.\n\
+Variables accessible are those of the lexical environment of the selected\n\
+stack frame, plus all those whose scope is global or an entire file.\n\
+\n\
+$NUM gets previous value number NUM.  $ and $$ are the last two values.\n\
+$$NUM refers to NUM'th value back from the last one.\n\
+Names starting with $ refer to registers (with the values they would have\n\
+if the program were to return to the stack frame now selected, restoring\n\
+all registers saved by frames farther in) or else to debugger\n\
+\"convenience\" variables (any such name not a known register).\n\
+Use assignment expressions to give values to convenience variables.\n",
+                  "\n\
+\{TYPE}ADREXP refers to a datum of data type TYPE, located at address ADREXP.\n\
+@ is a binary operator for treating consecutive data objects\n\
+anywhere in memory as an array.  FOO@NUM gives an array whose first\n\
+element is FOO, whose second element is stored in the space following\n\
+where FOO is stored, etc.  FOO must be an expression whose value\n\
+resides in memory.\n",
+                  "\n\
+EXP may be preceded with /FMT, where FMT is a format letter\n\
+but no count or size letter (see \"x\" command)."));
+  add_com_alias ("p", "print", class_vars, 1);
+}
diff --git a/usr/src/usr.bin/gdb/remote.c b/usr/src/usr.bin/gdb/remote.c
new file mode 100644 (file)
index 0000000..b3fd206
--- /dev/null
@@ -0,0 +1,626 @@
+/*-
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Van Jacobson and Steven McCanne of Lawrence Berkeley Laboratory.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Header: remote.c,v 1.24 91/03/22 15:35:15 mccanne Exp $;
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)remote.c   6.5 (Berkeley) 5/8/91";
+#endif /* not lint */
+
+#include "param.h"
+
+#include <stdio.h>
+#include <varargs.h>
+
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+
+#include "defs.h"
+#include "frame.h"
+#include "inferior.h"
+#include "wait.h"
+
+#include "kgdb_proto.h"
+
+static FILE *kiodebug;
+static int icache = 1;
+extern int kernel_debugging;
+
+static int remote_cache_valid;
+static int remote_instub;
+
+static void remote_signal();
+static void remote_debug();
+static void print_msg();
+
+static int remote_mtu;
+static int (*send_msg)();
+static int (*recv_msg)();
+static void (*closelink)();
+
+static u_char *inbuffer;
+static u_char *outbuffer;
+
+/*
+ * Statistics.
+ */
+static int remote_ierrs;
+static int remote_oerrs;
+static int remote_seqerrs;
+static int remote_spurious;
+
+#define PUTCMD(cmd) m_xchg(cmd, (u_char *)0, 0, (u_char *)0, (int *)0)
+
+/*
+ * Send an outbound message to the remote machine and read the reply.
+ * Either or both message buffers may be NULL.
+ */
+static int
+m_xchg(type, out, outlen, in, inlen)
+       int type;
+       u_char *out;
+       int outlen;
+       u_char *in;
+       int *inlen;
+{
+       register int err, (*send)() = send_msg, (*recv)() = recv_msg;
+       int ack;
+       static int seqbit = 0;
+
+       if (!remote_instub) {
+               remote_instub = 1;
+               PUTCMD(KGDB_EXEC);
+       }
+
+       seqbit ^= KGDB_SEQ;
+       while (1) {
+               err = (*send)(type | seqbit, out, outlen);
+               if (err) {
+                       ++remote_oerrs;
+                       if (kiodebug)
+                               remote_debug("send error %d\n", err);
+               }
+               if (kiodebug)
+                       print_msg(type | seqbit, out, outlen, 'O');
+
+       recv:
+               err = (*recv)(&ack, in, inlen);
+               if (err) {
+                       ++remote_ierrs;
+                       if (kiodebug)
+                               remote_debug("recv error %d\n", err);
+                       remote_cache_valid = 0;
+               } else if (kiodebug)
+                       print_msg(ack, in, inlen ? *inlen : 0, 'I');
+
+               if (err)
+                       continue;
+
+               if ((ack & KGDB_ACK) == 0 || KGDB_CMD(ack) != KGDB_CMD(type)) {
+                       ++remote_spurious;
+                       continue;
+               }
+               if ((ack & KGDB_SEQ) ^ seqbit) {
+                       ++remote_seqerrs;
+                       goto recv;
+               }
+               return ack;
+       }
+}
+
+/*
+ * Wait for the specified message type.  Discard anything else.
+ * (this is used by 'remote-signal' to help us resync with other side.)
+ */
+static void
+m_recv(type, in, inlen)
+       int type;
+       u_char *in;
+       int *inlen;
+{
+       int reply, err;
+
+       while (1) {
+               err = (*recv_msg)(&reply, in, inlen);
+               if (err) {
+                       ++remote_ierrs;
+                       if (kiodebug)
+                               remote_debug("recv error %d\n", err);
+               } else if (kiodebug)
+                       print_msg(reply, in, inlen ? *inlen : 0, 'I');
+
+               if (KGDB_CMD(reply) == type)
+                       return;
+               ++remote_spurious;
+       }
+}
+
+/*
+ * Send a message.  Do not wait for *any* response from the other side.
+ * Some other thread of control will pick up the ack that will be generated.
+ */
+static void
+m_send(type, buf, len)
+       int type;
+       u_char *buf;
+       int len;
+{
+       int err;
+
+       if (!remote_instub) {
+               remote_instub = 1;
+               PUTCMD(KGDB_EXEC);
+       }
+
+       err = (*send_msg)(type, buf, len);
+       if (err) {
+               ++remote_ierrs;
+               if (kiodebug)
+                       remote_debug("[send error %d] ", err);
+       }
+       if (kiodebug)
+               print_msg(type, buf, len, 'O');
+}
+
+/*
+ * Open a connection to a remote debugger.  
+ * NAME is the filename used for communication.  
+ */
+void
+remote_open(name, from_tty)
+       char *name;
+       int from_tty;
+{
+       int bufsize;
+
+       remote_debugging = 0;
+       if (sl_open(name, &send_msg, &recv_msg, &closelink, &remote_mtu,
+                   &bufsize))
+               return;
+       if (from_tty)
+               printf("Remote debugging using %s\n", name);
+       remote_debugging = 1;
+
+       remote_cache_valid = 0;
+
+       inbuffer = (u_char *)malloc(bufsize);
+       outbuffer = (u_char *)malloc(bufsize);
+
+       remote_signal();
+
+       remote_ierrs = 0;
+       remote_oerrs = 0;
+       remote_spurious = 0;
+}
+
+/*
+ * Close the open connection to the remote debugger. Use this when you want
+ * to detach and do something else with your gdb.  
+ */
+void
+remote_close(from_tty)
+       int from_tty;
+{
+       if (!remote_debugging)
+               error("remote debugging not enabled");
+
+       remote_debugging = 0;
+       /*
+        * Take remote machine out of debug mode.
+        */
+       (void)PUTCMD(KGDB_KILL);
+       (*closelink)();
+       if (from_tty)
+               printf("Ending remote debugging\n");
+
+       free((char *)inbuffer);
+       free((char *)outbuffer);
+}
+
+/*
+ * Tell the remote machine to resume.
+ */
+int
+remote_resume(step, signal)
+       int step, signal;
+{
+       if (!step) {
+               (void)PUTCMD(KGDB_CONT);
+               remote_instub = 0;
+       } else {
+#ifdef NO_SINGLE_STEP
+               single_step(0);
+#else
+               (void)PUTCMD(KGDB_STEP);
+#endif
+       }
+}
+
+/*
+ * Wait until the remote machine stops, then return, storing status in STATUS
+ * just as `wait' would.
+ */
+int
+remote_wait(status)
+       WAITTYPE *status;
+{
+       int len;
+
+       WSETEXIT((*status), 0);
+       /*
+        * When the machine stops, it will send us a KGDB_SIGNAL message,
+        * so we wait for one of these.
+        */
+       m_recv(KGDB_SIGNAL, inbuffer, &len);
+       WSETSTOP((*status), inbuffer[0]);
+}
+
+/*
+ * Register context as of last remote_fetch_registers().
+ */
+static char reg_cache[REGISTER_BYTES];
+
+/*
+ * Read the remote registers into the block REGS.
+ */
+void
+remote_fetch_registers(regs)
+       char *regs;
+{
+       int regno, len, rlen, ack;
+       u_char *cp, *ep;
+
+       regno = -1;
+       do {
+               outbuffer[0] = regno + 1;
+               ack = m_xchg(remote_cache_valid ? 
+                                KGDB_REG_R|KGDB_DELTA : KGDB_REG_R,
+                            outbuffer, 1, inbuffer, &len);
+               cp = inbuffer;
+               ep = cp + len;
+               while (cp < ep) {
+                       regno = *cp++;
+                       rlen = REGISTER_RAW_SIZE(regno);
+                       bcopy((char *)cp, 
+                             &reg_cache[REGISTER_BYTE(regno)], rlen);
+                       cp += rlen;
+               }
+       } while (ack & KGDB_MORE);
+
+       remote_cache_valid = 1;
+       bcopy(reg_cache, regs, REGISTER_BYTES);
+}
+
+/*
+ * Store the remote registers from the contents of the block REGS.
+ */
+void
+remote_store_registers(regs)
+       char *regs;
+{
+       u_char *cp, *ep;
+       int regno, off, rlen;
+
+       cp = outbuffer;
+       ep = cp + remote_mtu;
+
+       for (regno = 0; regno < NUM_REGS; ++regno) {
+               off = REGISTER_BYTE(regno);
+               rlen = REGISTER_RAW_SIZE(regno);
+               if (!remote_cache_valid ||
+                   bcmp(&regs[off], &reg_cache[off], rlen) != 0) {
+                       if (cp + rlen + 1 >= ep) {
+                               (void)m_xchg(KGDB_REG_W, 
+                                            outbuffer, cp - outbuffer, 
+                                            (u_char *)0, (int *)0);
+                               cp = outbuffer;
+                       }
+                       *cp++ = regno;
+                       bcopy(&regs[off], cp, rlen);
+                       cp += rlen;
+               }
+       }
+       if (cp != outbuffer)
+               (void)m_xchg(KGDB_REG_W, outbuffer, cp - outbuffer, 
+                            (u_char *)0, (int *)0);
+       bcopy(regs, reg_cache, REGISTER_BYTES);
+}
+
+/*
+ * Store a chunk of memory into the remote host.
+ * 'remote_addr' is the address in the remote memory space.
+ * 'cp' is the address of the buffer in our space, and 'len' is
+ * the number of bytes.  Returns an errno status.
+ */
+int
+remote_write_inferior_memory(remote_addr, cp, len)
+       CORE_ADDR remote_addr;
+       u_char *cp;
+       int len;
+{
+       int cnt;
+
+       while (len > 0) {
+               cnt = min(len, remote_mtu - 4);
+               bcopy((char *)&remote_addr, outbuffer, 4);
+               bcopy(cp, outbuffer + 4, cnt);
+               (void)m_xchg(KGDB_MEM_W, outbuffer, cnt + 4, inbuffer, &len);
+
+               if (inbuffer[0])
+                       return inbuffer[0];
+
+               remote_addr += cnt;
+               cp += cnt;
+               len -= cnt;
+       }
+       return 0;
+}
+
+/*
+ * Read memory data directly from the remote machine.
+ * 'remote_addr' is the address in the remote memory space.
+ * 'cp' is the address of the buffer in our space, and 'len' is
+ * the number of bytes.  Returns an errno status.
+ */
+static int
+remote_read_memory(remote_addr, cp, len)
+       CORE_ADDR remote_addr;
+       u_char *cp;
+       int len;
+{
+       int cnt, inlen;
+
+       while (len > 0) {
+               cnt = min(len, remote_mtu - 1);
+               outbuffer[0] = cnt;
+               bcopy((char *)&remote_addr, (char *)&outbuffer[1], 4);
+
+               (void)m_xchg(KGDB_MEM_R, outbuffer, 5, inbuffer, &inlen);
+
+               if (inbuffer[0] != 0)
+                       return inbuffer[0];
+
+               if (cnt != inlen - 1)
+                       /* XXX */
+                       error("remote_read_memory() request botched");
+
+               bcopy((char *)&inbuffer[1], (char *)cp, cnt);
+
+               remote_addr += cnt;
+               cp += cnt;
+               len -= cnt;
+       }
+       return 0;
+}
+
+int
+remote_read_inferior_memory(remote_addr, cp, len)
+       CORE_ADDR remote_addr;
+       char *cp;
+       int len;
+{
+       int stat = 0;
+
+       if (icache) {
+               extern CORE_ADDR text_start, text_end;
+               CORE_ADDR xferend = remote_addr + len;
+
+               if (remote_addr < text_end && text_start < xferend) {
+                       /*
+                        * at least part of this xfer is in the text
+                        * space -- xfer the overlap from the exec file.
+                        */
+                       if (remote_addr >= text_start && xferend < text_end)
+                               return (xfer_core_file(remote_addr, cp, len));
+                       if (remote_addr >= text_start) {
+                               int i = text_end - remote_addr;
+
+                               if (stat = xfer_core_file(remote_addr, cp, i))
+                                       return (stat);
+                               remote_addr += i;
+                               cp += i;
+                               len -= i;
+                       } else if (xferend <= text_end) {
+                               int i = xferend - text_start;
+
+                               len = text_start - remote_addr;
+                               if (stat = xfer_core_file(text_start,
+                                                         cp + len, i))
+                                       return (stat);
+                       }
+               }
+       }
+       return remote_read_memory(remote_addr, cp, len);
+}
+
+/*
+ * Signal the remote machine.  The remote end might be idle or it might
+ * already be in debug mode -- we need to handle both case.  Thus, we use
+ * the framing character as the wakeup byte, and send a SIGNAL packet.
+ * If the remote host is idle, the framing character will wake it up.
+ * If it is in the kgdb stub, then we will get a SIGNAL reply.
+ */
+static void
+remote_signal()
+{
+       if (!remote_debugging)
+               printf("Remote debugging not enabled.\n");
+       else {
+               remote_instub = 0;
+               m_send(KGDB_SIGNAL, (u_char *)0, 0);
+       }
+}
+
+static void
+remote_signal_command()
+{
+       extern int stop_after_attach;
+
+       if (!remote_debugging)
+               error("Not debugging remote.");
+       remote_cache_valid = 0;
+       remote_signal();
+       restart_remote();
+}
+
+/*
+ * Print a message for debugging.
+ */
+static void
+print_msg(type, buf, len, dir)
+       int type;
+       u_char *buf;
+       int len;
+       int dir;
+{
+       int i;
+       char *s;
+
+       switch (KGDB_CMD(type)) {
+       case KGDB_MEM_R:        s = "memr"; break;
+       case KGDB_MEM_W:        s = "memw"; break;
+       case KGDB_REG_R:        s = "regr"; break;
+       case KGDB_REG_W:        s = "regw"; break;
+       case KGDB_CONT:         s = "cont"; break;
+       case KGDB_STEP:         s = "step"; break;
+       case KGDB_KILL:         s = "kill"; break;
+       case KGDB_SIGNAL:       s = "sig "; break;
+       case KGDB_EXEC:         s = "exec"; break;
+       default:                s = "unk "; break;
+       }
+       remote_debug("%c %c%c%c%c %s (%02x): ", dir,
+                    (type & KGDB_ACK) ? 'A' : '.',
+                    (type & KGDB_DELTA) ? 'D' : '.',
+                    (type & KGDB_MORE) ? 'M' : '.',
+                    (type & KGDB_SEQ) ? '-' : '+',
+                    s, type);
+       if (buf)
+               for (i = 0; i < len; ++i)
+                       remote_debug("%02x", buf[i]);
+       remote_debug("\n");
+}
+
+static void
+set_remote_text_refs_command(arg, from_tty)
+       char *arg;
+       int from_tty;
+{
+       icache = !parse_binary_operation("set remote-text-refs", arg);
+}
+
+static void
+remote_debug_command(arg, from_tty)
+       char *arg;
+       int from_tty;
+{
+       char *name;
+
+       if (kiodebug != 0 && kiodebug != stderr)
+               (void)fclose(kiodebug);
+
+       if (arg == 0) {
+               kiodebug = 0;
+               printf("Remote debugging off.\n");
+               return;
+       }
+       if (arg[0] == '-') {
+               kiodebug = stderr;
+               name = "stderr";
+       } else {
+               kiodebug = fopen(arg, "w");
+               if (kiodebug == 0) {
+                       printf("Cannot open '%s'.\n", arg);
+                       return;
+               }
+               name = arg;
+       }
+       printf("Remote debugging output routed to %s.\n", name);
+}
+
+/* ARGSUSED */
+static void
+remote_info(arg, from_tty)
+       char *arg;
+       int from_tty;
+{
+       printf("Using %s for text references.\n",
+               icache? "local executable" : "remote");
+       printf("Protocol debugging is %s.\n", kiodebug? "on" : "off");
+       printf("%d spurious input messages.\n", remote_spurious);
+       printf("%d input errors; %d output errors; %d sequence errors.\n", 
+              remote_ierrs, remote_oerrs, remote_seqerrs);
+}
+
+/* VARARGS */
+static void
+remote_debug(va_alist)
+       va_dcl
+{
+       register char *cp;
+       va_list ap;
+
+       va_start(ap);
+       cp = va_arg(ap, char *);
+       (void)vfprintf(kiodebug, cp, ap);
+       va_end(ap);
+       fflush(kiodebug);
+}
+
+extern struct cmd_list_element *setlist;
+
+void
+_initialize_remote()
+{
+       add_com("remote-signal", class_run, remote_signal_command,
+               "If remote debugging, send interrupt signal to remote.");
+       add_cmd("remote-text-refs", class_support, 
+               set_remote_text_refs_command,
+"Enable/disable use of local executable for text segment references.\n\
+If on, all memory read/writes go to remote.\n\
+If off, text segment reads use the local executable.",
+               &setlist);
+
+       add_com("remote-debug", class_run, remote_debug_command,
+"With a file name argument, enables output of remote protocol debugging\n\
+messages to said file.  If file is `-', stderr is used.\n\
+With no argument, remote debugging is disabled.");
+
+       add_info("remote", remote_info,
+                "Show current settings of remote debugging options.");
+}
+
diff --git a/usr/src/usr.bin/gdb/source.c b/usr/src/usr.bin/gdb/source.c
new file mode 100644 (file)
index 0000000..d9ae20f
--- /dev/null
@@ -0,0 +1,1166 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)source.c   6.3 (Berkeley) 5/8/91";
+#endif /* not lint */
+
+/* List lines of source files for GDB, the GNU debugger.
+   Copyright (C) 1986, 1987, 1988, 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING.  If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#include <stdio.h>
+#include "defs.h"
+#include "symtab.h"
+#include "param.h"
+
+#ifdef USG
+#include <sys/types.h>
+#include <fcntl.h>
+#endif
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+
+/* Path of directories to search for source files.
+   Same format as the PATH environment variable's value.  */
+
+static char *source_path;
+
+/* Symtab of default file for listing lines of.  */
+
+struct symtab *current_source_symtab;
+
+/* Default next line to list.  */
+
+int current_source_line;
+
+/* Line number of last line printed.  Default for various commands.
+   current_source_line is usually, but not always, the same as this.  */
+
+static int last_line_listed;
+
+/* First line number listed by last listing command.  */
+
+static int first_line_listed;
+
+\f
+struct symtab *psymtab_to_symtab ();
+
+/* Set the source file default for the "list" command, specifying a
+   symtab.  Sigh.  Behaivior specification: If it is called with a
+   non-zero argument, that is the symtab to select.  If it is not,
+   first lookup "main"; if it exists, use the symtab and line it
+   defines.  If not, take the last symtab in the symtab_list (if it
+   exists) or the last symtab in the psytab_list (if *it* exists).  If
+   none of this works, report an error.   */
+
+void
+select_source_symtab (s)
+     register struct symtab *s;
+{
+  struct symtabs_and_lines sals;
+  struct symtab_and_line sal;
+  struct partial_symtab *ps, *cs_pst;
+  
+  if (s)
+    {
+      current_source_symtab = s;
+      current_source_line = 1;
+      return;
+    }
+
+  /* Make the default place to list be the function `main'
+     if one exists.  */
+  if (lookup_symbol ("main", 0, VAR_NAMESPACE, 0))
+    {
+      sals = decode_line_spec ("main", 1);
+      sal = sals.sals[0];
+      free (sals.sals);
+      current_source_symtab = sal.symtab;
+      current_source_line = max (sal.line - 9, 1);
+      return;
+    }
+  
+  /* All right; find the last file in the symtab list (ignoring .h's).  */
+
+  if (s = symtab_list)
+    {
+      do
+       {
+         char *name = s->filename;
+         int len = strlen (name);
+         if (! (len > 2 && !strcmp (&name[len - 2], ".h")))
+           current_source_symtab = s;
+         s = s->next;
+       }
+      while (s);
+      current_source_line = 1;
+    }
+  else if (partial_symtab_list)
+    {
+      ps = partial_symtab_list;
+      while (ps)
+       {
+         char *name = ps->filename;
+         int len = strlen (name);
+         if (! (len > 2 && !strcmp (&name[len - 2], ".h")))
+           cs_pst = ps;
+         ps = ps->next;
+       }
+      if (cs_pst)
+       if (cs_pst->readin)
+         fatal ("Internal: select_source_symtab: readin pst found and no symtabs.");
+       else
+         current_source_symtab = psymtab_to_symtab (cs_pst);
+      else
+       current_source_symtab = 0;
+      current_source_line = 1;
+    }
+}
+\f
+static void
+directories_info ()
+{
+  printf ("Source directories searched: %s\n", source_path);
+}
+
+void
+init_source_path ()
+{
+  register struct symtab *s;
+
+  source_path = savestring (current_directory, strlen (current_directory));
+
+  /* Forget what we learned about line positions in source files;
+     must check again now since files may be found in
+     a different directory now.  */
+  for (s = symtab_list; s; s = s->next)
+    if (s->line_charpos != 0)
+      {
+       free (s->line_charpos);
+       s->line_charpos = 0;
+      }
+}
+
+void
+directory_command (dirname, from_tty)
+     char *dirname;
+     int from_tty;
+{
+  char *old = source_path;
+
+  dont_repeat ();
+
+  if (dirname == 0)
+    {
+      if (query ("Reinitialize source path to %s? ", current_directory))
+       {
+         init_source_path ();
+         free (old);
+       }
+    }
+  else
+    {
+      dirname = tilde_expand (dirname);
+      make_cleanup (free, dirname);
+
+      do
+       {
+         extern char *index ();
+         char *name = dirname;
+         register char *p;
+         struct stat st;
+
+         {
+           char *colon = index (name, ':');
+           char *space = index (name, ' ');
+           char *tab = index (name, '\t');
+           if (colon == 0 && space == 0 && tab ==  0)
+             p = dirname = name + strlen (name);
+           else
+             {
+               p = 0;
+               if (colon != 0 && (p == 0 || colon < p))
+                 p = colon;
+               if (space != 0 && (p == 0 || space < p))
+                 p = space;
+               if (tab != 0 && (p == 0 || tab < p))
+                 p = tab;
+               dirname = p + 1;
+               while (*dirname == ':' || *dirname == ' ' || *dirname == '\t')
+                 ++dirname;
+             }
+         }
+
+         if (p[-1] == '/')
+           /* Sigh. "foo/" => "foo" */
+           --p;
+         *p = '\0';
+
+         while (p[-1] == '.')
+           {
+             if (p - name == 1)
+               {
+                 /* "." => getwd ().  */
+                 name = current_directory;
+                 goto append;
+               }
+             else if (p[-2] == '/')
+               {
+                 if (p - name == 2)
+                   {
+                     /* "/." => "/".  */
+                     *--p = '\0';
+                     goto append;
+                   }
+                 else
+                   {
+                     /* "...foo/." => "...foo".  */
+                     p -= 2;
+                     *p = '\0';
+                     continue;
+                   }
+               }
+             else
+               break;
+           }
+
+         if (*name != '/')
+           name = concat (current_directory, "/", name);
+         else
+           name = savestring (name, p - name);
+         make_cleanup (free, name);
+
+         if (stat (name, &st) < 0)
+           perror_with_name (name);
+         if ((st.st_mode & S_IFMT) != S_IFDIR)
+           error ("%s is not a directory.", name);
+
+       append:
+         {
+           register unsigned int len = strlen (name);
+
+           p = source_path;
+           while (1)
+             {
+               if (!strncmp (p, name, len)
+                   && (p[len] == '\0' || p[len] == ':'))
+                 {
+                   if (from_tty)
+                     printf ("\"%s\" is already in the source path.\n", name);
+                   break;
+                 }
+               p = index (p, ':');
+               if (p != 0)
+                 ++p;
+               else
+                 break;
+             }
+           if (p == 0)
+             {
+               source_path = concat (old, ":", name);
+               free (old);
+               old = source_path;
+             }
+         }
+       } while (*dirname != '\0');
+      if (from_tty)
+       directories_info ();
+    }
+}
+\f
+/* Open a file named STRING, searching path PATH (dir names sep by colons)
+   using mode MODE and protection bits PROT in the calls to open.
+   If TRY_CWD_FIRST, try to open ./STRING before searching PATH.
+   (ie pretend the first element of PATH is ".")
+   If FILENAMED_OPENED is non-null, set it to a newly allocated string naming
+   the actual file opened (this string will always start with a "/"
+
+   If a file is found, return the descriptor.
+   Otherwise, return -1, with errno set for the last name we tried to open.  */
+
+/*  >>>> This should only allow files of certain types,
+    >>>>  eg executable, non-directory */
+int
+openp (path, try_cwd_first, string, mode, prot, filename_opened)
+     char *path;
+     int try_cwd_first;
+     char *string;
+     int mode;
+     int prot;
+     char **filename_opened;
+{
+  register int fd;
+  register char *filename;
+  register char *p, *p1;
+  register int len;
+
+  if (!path)
+    path = ".";
+
+  /* ./foo => foo */
+  while (string[0] == '.' && string[1] == '/')
+    string += 2;
+
+  if (try_cwd_first || string[0] == '/')
+    {
+      filename = string;
+      fd = open (filename, mode, prot);
+      if (fd >= 0 || string[0] == '/')
+       goto done;
+    }
+
+  filename = (char *) alloca (strlen (path) + strlen (string) + 2);
+  fd = -1;
+  for (p = path; p; p = p1 ? p1 + 1 : 0)
+    {
+      p1 = (char *) index (p, ':');
+      if (p1)
+       len = p1 - p;
+      else
+       len = strlen (p);
+
+      strncpy (filename, p, len);
+      filename[len] = 0;
+      strcat (filename, "/");
+      strcat (filename, string);
+
+      fd = open (filename, mode, prot);
+      if (fd >= 0) break;
+    }
+
+ done:
+  if (filename_opened)
+    if (fd < 0)
+      *filename_opened = (char *) 0;
+    else if (filename[0] == '/')
+      *filename_opened = savestring (filename, strlen (filename));
+    else
+      {
+       *filename_opened = concat (current_directory, "/", filename);
+      }
+
+  return fd;
+}
+\f
+/* Create and initialize the table S->line_charpos that records
+   the positions of the lines in the source file, which is assumed
+   to be open on descriptor DESC.
+   All set S->nlines to the number of such lines.  */
+
+static void
+find_source_lines (s, desc)
+     struct symtab *s;
+     int desc;
+{
+  struct stat st;
+  register char *data, *p, *end;
+  int nlines = 0;
+  int lines_allocated = 1000;
+  int *line_charpos = (int *) xmalloc (lines_allocated * sizeof (int));
+  extern int exec_mtime;
+
+  if (fstat (desc, &st) < 0)
+    perror_with_name (s->filename);
+  if (get_exec_file (0) != 0 && exec_mtime < st.st_mtime)
+    printf ("Source file is more recent than executable.\n");
+
+  data = (char *) alloca (st.st_size);
+  if (myread (desc, data, st.st_size) < 0)
+    perror_with_name (s->filename);
+  end = data + st.st_size;
+  p = data;
+  line_charpos[0] = 0;
+  nlines = 1;
+  while (p != end)
+    {
+      if (*p++ == '\n'
+         /* A newline at the end does not start a new line.  */
+         && p != end)
+       {
+         if (nlines == lines_allocated)
+           {
+             lines_allocated *= 2;
+             line_charpos = (int *) xrealloc (line_charpos,
+                                              sizeof (int) * lines_allocated);
+           }
+         line_charpos[nlines++] = p - data;
+       }
+    }
+  s->nlines = nlines;
+  s->line_charpos = (int *) xrealloc (line_charpos, nlines * sizeof (int));
+}
+
+/* Return the character position of a line LINE in symtab S.
+   Return 0 if anything is invalid.  */
+
+int
+source_line_charpos (s, line)
+     struct symtab *s;
+     int line;
+{
+  if (!s) return 0;
+  if (!s->line_charpos || line <= 0) return 0;
+  if (line > s->nlines)
+    line = s->nlines;
+  return s->line_charpos[line - 1];
+}
+
+/* Return the line number of character position POS in symtab S.  */
+
+int
+source_charpos_line (s, chr)
+    register struct symtab *s;
+    register int chr;
+{
+  register int line = 0;
+  register int *lnp;
+    
+  if (s == 0 || s->line_charpos == 0) return 0;
+  lnp = s->line_charpos;
+  /* Files are usually short, so sequential search is Ok */
+  while (line < s->nlines  && *lnp <= chr)
+    {
+      line++;
+      lnp++;
+    }
+  if (line >= s->nlines)
+    line = s->nlines;
+  return line;
+}
+\f
+/* Get full pathname and line number positions for a symtab.
+   Return nonzero if line numbers may have changed.
+   Set *FULLNAME to actual name of the file as found by `openp',
+   or to 0 if the file is not found.  */
+
+int
+get_filename_and_charpos (s, line, fullname)
+     struct symtab *s;
+     int line;
+     char **fullname;
+{
+  register int desc, linenums_changed = 0;
+  
+  desc = openp (source_path, 0, s->filename, O_RDONLY, 0, &s->fullname);
+  if (desc < 0)
+    {
+      if (fullname)
+       *fullname = NULL;
+      return 0;
+    }  
+  if (fullname)
+    *fullname = s->fullname;
+  if (s->line_charpos == 0) linenums_changed = 1;
+  if (linenums_changed) find_source_lines (s, desc);
+  close (desc);
+  return linenums_changed;
+}
+
+/* Print text describing the full name of the source file S
+   and the line number LINE and its corresponding character position.
+   The text starts with two Ctrl-z so that the Emacs-GDB interface
+   can easily find it.
+
+   MID_STATEMENT is nonzero if the PC is not at the beginning of that line.
+
+   Return 1 if successful, 0 if could not find the file.  */
+
+int
+identify_source_line (s, line, mid_statement)
+     struct symtab *s;
+     int line;
+     int mid_statement;
+{
+  if (s->line_charpos == 0)
+    get_filename_and_charpos (s, line, 0);
+  if (s->fullname == 0)
+    return 0;
+  printf ("\032\032%s:%d:%d:%s:0x%x\n", s->fullname,
+         line, s->line_charpos[line - 1],
+         mid_statement ? "middle" : "beg",
+         get_frame_pc (get_current_frame()));
+  current_source_line = line;
+  first_line_listed = line;
+  last_line_listed = line;
+  current_source_symtab = s;
+  return 1;
+}
+\f
+/* Print source lines from the file of symtab S,
+   starting with line number LINE and stopping before line number STOPLINE.  */
+
+void
+print_source_lines (s, line, stopline, noerror)
+     struct symtab *s;
+     int line, stopline;
+     int noerror;
+{
+  register int c;
+  register int desc;
+  register FILE *stream;
+  int nlines = stopline - line;
+
+  desc = openp (source_path, 0, s->filename, O_RDONLY, 0, &s->fullname);
+  if (desc < 0)
+    {
+      extern int errno;
+      if (noerror && line + 1 == stopline)
+       {
+         /* can't find the file - tell user where we are anyway */
+         current_source_symtab = s;
+         current_source_line = line;
+         first_line_listed = line;
+         last_line_listed = line;
+         printf_filtered ("%d\t(%s)\n", current_source_line++, s->filename);
+       }
+      else
+       {
+         if (! noerror)
+           perror_with_name (s->filename);
+         print_sys_errmsg (s->filename, errno);
+       }
+      return;
+    }
+
+  if (s->line_charpos == 0)
+    find_source_lines (s, desc);
+
+  if (line < 1 || line > s->nlines)
+    {
+      close (desc);
+      error ("Line number %d out of range; %s has %d lines.",
+            line, s->filename, s->nlines);
+    }
+
+  if (lseek (desc, s->line_charpos[line - 1], 0) < 0)
+    {
+      close (desc);
+      perror_with_name (s->filename);
+    }
+
+  current_source_symtab = s;
+  current_source_line = line;
+  first_line_listed = line;
+  
+  stream = fdopen (desc, "r");
+  clearerr (stream);
+
+  while (nlines-- > 0)
+    {
+      c = fgetc (stream);
+      if (c == EOF) break;
+      last_line_listed = current_source_line;
+      printf_filtered ("%d\t", current_source_line++);
+      do
+       {
+         if (c < 040 && c != '\t' && c != '\n')
+             printf_filtered ("^%c", c + 0100);
+         else if (c == 0177)
+           printf_filtered ("^?");
+         else
+           printf_filtered ("%c", c);
+       } while (c != '\n' && (c = fgetc (stream)) >= 0);
+    }
+
+  fclose (stream);
+}
+\f
+
+
+/* 
+  C++
+  Print a list of files and line numbers which a user may choose from
+  in order to list a function which was specified ambiguously
+  (as with `list classname::overloadedfuncname', for example).
+  The vector in SALS provides the filenames and line numbers.
+  */
+static void
+ambiguous_line_spec (sals)
+     struct symtabs_and_lines *sals;
+{
+  int i;
+
+  for (i = 0; i < sals->nelts; ++i)
+    printf("file: \"%s\", line number: %d\n",
+          sals->sals[i].symtab->filename, sals->sals[i].line);
+}
+
+
+static void
+file_command(arg, from_tty)
+  char *arg;
+  int from_tty;
+{
+  struct symtabs_and_lines sals;
+  struct symtab_and_line sal;
+  struct symbol *sym;
+  char *arg1;
+  int linenum_beg = 0;
+  char *p;
+
+  if (symtab_list == 0 && partial_symtab_list == 0)
+    error ("No symbol table is loaded.  Use the \"symbol-file\" command.");
+
+  /* Pull in a current source symtab if necessary */
+  if (arg == 0 || arg[0] == 0) {
+         if (current_source_symtab == 0)
+                 select_source_symtab(0);
+         else
+                 printf("%s\n", current_source_symtab->filename);
+         return;
+  }
+  arg1 = arg;
+  sals = decode_line_1 (&arg1, 0, 0, 0);
+
+  if (! sals.nelts)
+    return;  /*  C++  */
+
+  if (sals.nelts > 1)
+    {
+      ambiguous_line_spec (&sals);
+      free (sals.sals);
+      return;
+    }
+
+  sal = sals.sals[0];
+  free (sals.sals);
+
+  /* Record whether the BEG arg is all digits.  */
+
+  for (p = arg; p != arg1 && *p >= '0' && *p <= '9'; ++p)
+    ;
+  linenum_beg = (p == arg1);
+
+  /* if line was specified by address,
+     print exactly which line, and which file.
+     In this case, sal.symtab == 0 means address is outside
+     of all known source files, not that user failed to give a filename.  */
+  if (*arg == '*')
+    {
+      if (sal.symtab == 0)
+       error ("No source file for address 0x%x.", sal.pc);
+      sym = find_pc_function (sal.pc);
+      if (sym)
+       printf ("0x%x is in %s (%s, line %d).\n",
+               sal.pc, SYMBOL_NAME (sym), sal.symtab->filename, sal.line);
+      else
+       printf ("0x%x is in %s, line %d.\n",
+               sal.pc, sal.symtab->filename, sal.line);
+    }
+
+  /* If line was not specified by just a line number,
+     and it does not imply a symtab, it must be an undebuggable symbol
+     which means no source code.  */
+
+  if (sal.symtab == 0)
+    {
+      if (! linenum_beg)
+       error ("No line number known for %s.", arg);
+      else
+       error ("No default source file yet.  Do \"help list\".");
+    }
+  else
+    {
+      current_source_symtab = sal.symtab;
+      current_source_line = sal.line;
+      first_line_listed = sal.line;
+    }
+}
+
+#define PUSH_STACK_SIZE 32
+static struct {
+       struct symtab *symtab;
+       int line;
+} push_stack[PUSH_STACK_SIZE];
+
+static unsigned int push_stack_ptr;
+
+static void
+push_to_file_command (arg, from_tty)
+       char *arg;
+       int from_tty;
+{
+       struct symtab *cursym = current_source_symtab;
+       int curline = current_source_line;
+       register unsigned int i;
+
+       file_command(arg, from_tty);
+
+       /* if we got back, command was successful */
+       i = push_stack_ptr;
+       push_stack[i].symtab = cursym;
+       push_stack[i].line = curline;
+       push_stack_ptr = (i + 1) & (PUSH_STACK_SIZE - 1);
+}
+
+static void
+pop_file_command (arg, from_tty)
+       char *arg;
+       int from_tty;
+{
+       register unsigned int i = push_stack_ptr;
+
+       /* if there's something on the stack, pop it & clear the slot. */
+       i = (i + (PUSH_STACK_SIZE - 1)) & (PUSH_STACK_SIZE - 1);
+       if (push_stack[i].symtab) {
+               current_source_symtab = push_stack[i].symtab;
+               first_line_listed = current_source_line = push_stack[i].line;
+               push_stack[i].symtab = NULL;
+               push_stack_ptr = i;
+       }
+}
+
+
+static void
+list_command (arg, from_tty)
+     char *arg;
+     int from_tty;
+{
+  struct symtabs_and_lines sals, sals_end;
+  struct symtab_and_line sal, sal_end;
+  struct symbol *sym;
+  char *arg1;
+  int no_end = 1;
+  int dummy_end = 0;
+  int dummy_beg = 0;
+  int linenum_beg = 0;
+  char *p;
+
+  if (symtab_list == 0 && partial_symtab_list == 0)
+    error ("No symbol table is loaded.  Use the \"symbol-file\" command.");
+
+  /* Pull in a current source symtab if necessary */
+  if (current_source_symtab == 0 &&
+      (arg == 0 || arg[0] == '+' || arg[0] == '-'))
+    select_source_symtab (0);
+
+  /* "l" or "l +" lists next ten lines.  */
+
+  if (arg == 0 || !strcmp (arg, "+"))
+    {
+      if (current_source_symtab == 0)
+       error ("No default source file yet.  Do \"help list\".");
+      print_source_lines (current_source_symtab, current_source_line,
+                         current_source_line + 10, 0);
+      return;
+    }
+
+  /* "l -" lists previous ten lines, the ones before the ten just listed.  */
+  if (!strcmp (arg, "-"))
+    {
+      if (current_source_symtab == 0)
+       error ("No default source file yet.  Do \"help list\".");
+      print_source_lines (current_source_symtab,
+                         max (first_line_listed - 10, 1),
+                         first_line_listed, 0);
+      return;
+    }
+
+  /* Now if there is only one argument, decode it in SAL
+     and set NO_END.
+     If there are two arguments, decode them in SAL and SAL_END
+     and clear NO_END; however, if one of the arguments is blank,
+     set DUMMY_BEG or DUMMY_END to record that fact.  */
+
+  arg1 = arg;
+  if (*arg1 == ',')
+    dummy_beg = 1;
+  else
+    {
+      sals = decode_line_1 (&arg1, 0, 0, 0);
+
+      if (! sals.nelts) return;  /*  C++  */
+      if (sals.nelts > 1)
+       {
+         ambiguous_line_spec (&sals);
+         free (sals.sals);
+         return;
+       }
+
+      sal = sals.sals[0];
+      free (sals.sals);
+    }
+
+  /* Record whether the BEG arg is all digits.  */
+
+  for (p = arg; p != arg1 && *p >= '0' && *p <= '9'; p++);
+  linenum_beg = (p == arg1);
+
+  while (*arg1 == ' ' || *arg1 == '\t')
+    arg1++;
+  if (*arg1 == ',')
+    {
+      no_end = 0;
+      arg1++;
+      while (*arg1 == ' ' || *arg1 == '\t')
+       arg1++;
+      if (*arg1 == 0)
+       dummy_end = 1;
+      else
+       {
+         if (dummy_beg)
+           sals_end = decode_line_1 (&arg1, 0, 0, 0);
+         else
+           sals_end = decode_line_1 (&arg1, 0, sal.symtab, sal.line);
+         if (sals_end.nelts == 0) 
+           return;
+         if (sals_end.nelts > 1)
+           {
+             ambiguous_line_spec (&sals_end);
+             free (sals_end.sals);
+             return;
+           }
+         sal_end = sals_end.sals[0];
+         free (sals_end.sals);
+       }
+    }
+
+  if (*arg1)
+    error ("Junk at end of line specification.");
+
+  if (!no_end && !dummy_beg && !dummy_end
+      && sal.symtab != sal_end.symtab)
+    error ("Specified start and end are in different files.");
+  if (dummy_beg && dummy_end)
+    error ("Two empty args do not say what lines to list.");
+  /* if line was specified by address,
+     first print exactly which line, and which file.
+     In this case, sal.symtab == 0 means address is outside
+     of all known source files, not that user failed to give a filename.  */
+  if (*arg == '*')
+    {
+      if (sal.symtab == 0)
+       error ("No source file for address 0x%x.", sal.pc);
+      sym = find_pc_function (sal.pc);
+      if (sym)
+       printf ("0x%x is in %s (%s, line %d).\n",
+               sal.pc, SYMBOL_NAME (sym), sal.symtab->filename, sal.line);
+      else
+       printf ("0x%x is in %s, line %d.\n",
+               sal.pc, sal.symtab->filename, sal.line);
+    }
+
+  /* If line was not specified by just a line number,
+     and it does not imply a symtab, it must be an undebuggable symbol
+     which means no source code.  */
+
+  if (! linenum_beg && sal.symtab == 0)
+    error ("No line number known for %s.", arg);
+
+  /* If this command is repeated with RET,
+     turn it into the no-arg variant.  */
+
+  if (from_tty)
+    *arg = 0;
+
+  if (dummy_beg && sal_end.symtab == 0)
+    error ("No default source file yet.  Do \"help list\".");
+  if (dummy_beg)
+    print_source_lines (sal_end.symtab, max (sal_end.line - 9, 1),
+                       sal_end.line + 1, 0);
+  else if (sal.symtab == 0)
+    error ("No default source file yet.  Do \"help list\".");
+  else if (no_end)
+    print_source_lines (sal.symtab, max (sal.line - 5, 1), sal.line + 5, 0);
+  else
+    print_source_lines (sal.symtab, sal.line,
+                       dummy_end ? sal.line + 10 : sal_end.line + 1,
+                       0);
+}
+\f
+/* Print info on range of pc's in a specified line.  */
+
+static void
+line_info (arg, from_tty)
+     char *arg;
+     int from_tty;
+{
+  struct symtabs_and_lines sals;
+  struct symtab_and_line sal;
+  int start_pc, end_pc;
+  int i;
+
+  if (arg == 0)
+    {
+      sal.symtab = current_source_symtab;
+      sal.line = last_line_listed;
+      sals.nelts = 1;
+      sals.sals = (struct symtab_and_line *)
+       xmalloc (sizeof (struct symtab_and_line));
+      sals.sals[0] = sal;
+    }
+  else
+    {
+      sals = decode_line_spec_1 (arg, 0);
+      
+      /* If this command is repeated with RET,
+        turn it into the no-arg variant.  */
+      if (from_tty)
+       *arg = 0;
+    }
+
+  /* C++  More than one line may have been specified, as when the user
+     specifies an overloaded function name. Print info on them all. */
+  for (i = 0; i < sals.nelts; i++)
+    {
+      sal = sals.sals[i];
+      
+      if (sal.symtab == 0)
+       error ("No source file specified.");
+
+      if (sal.line > 0
+         && find_line_pc_range (sal.symtab, sal.line, &start_pc, &end_pc))
+       {
+         if (start_pc == end_pc)
+           printf ("Line %d of \"%s\" is at pc 0x%x but contains no code.\n",
+                   sal.line, sal.symtab->filename, start_pc);
+         else
+           printf ("Line %d of \"%s\" starts at pc 0x%x and ends at 0x%x.\n",
+                   sal.line, sal.symtab->filename, start_pc, end_pc);
+         /* x/i should display this line's code.  */
+         set_next_address (start_pc);
+         /* Repeating "info line" should do the following line.  */
+         last_line_listed = sal.line + 1;
+       }
+      else
+       printf ("Line number %d is out of range for \"%s\".\n",
+               sal.line, sal.symtab->filename);
+    }
+}
+\f
+/* Commands to search the source file for a regexp.  */
+
+static void
+forward_search_command (regex, from_tty)
+     char *regex;
+{
+  register int c;
+  register int desc;
+  register FILE *stream;
+  int line = last_line_listed + 1;
+  char *msg;
+
+  msg = (char *) re_comp (regex);
+  if (msg)
+    error (msg);
+
+  if (current_source_symtab == 0)
+    select_source_symtab (0);
+
+  /* Search from last_line_listed+1 in current_source_symtab */
+
+  desc = openp (source_path, 0, current_source_symtab->filename,
+               O_RDONLY, 0, &current_source_symtab->fullname);
+  if (desc < 0)
+    perror_with_name (current_source_symtab->filename);
+
+  if (current_source_symtab->line_charpos == 0)
+    find_source_lines (current_source_symtab, desc);
+
+  if (line < 1 || line > current_source_symtab->nlines)
+    {
+      close (desc);
+      error ("Expression not found");
+    }
+
+  if (lseek (desc, current_source_symtab->line_charpos[line - 1], 0) < 0)
+    {
+      close (desc);
+      perror_with_name (current_source_symtab->filename);
+    }
+
+  stream = fdopen (desc, "r");
+  clearerr (stream);
+  while (1) {
+    char buf[4096];            /* Should be reasonable??? */
+    register char *p = buf;
+
+    c = fgetc (stream);
+    if (c == EOF)
+      break;
+    do {
+      *p++ = c;
+    } while (c != '\n' && (c = fgetc (stream)) >= 0);
+
+    /* we now have a source line in buf, null terminate and match */
+    *p = 0;
+    if (re_exec (buf) > 0)
+      {
+       /* Match! */
+       fclose (stream);
+       print_source_lines (current_source_symtab,
+                          line, line+1, 0);
+       current_source_line = max (line - 5, 1);
+       return;
+      }
+    line++;
+  }
+
+  printf ("Expression not found\n");
+  fclose (stream);
+}
+
+static void
+reverse_search_command (regex, from_tty)
+     char *regex;
+{
+  register int c;
+  register int desc;
+  register FILE *stream;
+  int line = last_line_listed - 1;
+  char *msg;
+
+  msg = (char *) re_comp (regex);
+  if (msg)
+    error (msg);
+
+  if (current_source_symtab == 0)
+    select_source_symtab (0);
+
+  /* Search from last_line_listed-1 in current_source_symtab */
+
+  desc = openp (source_path, 0, current_source_symtab->filename,
+               O_RDONLY, 0, &current_source_symtab->fullname);
+  if (desc < 0)
+    perror_with_name (current_source_symtab->filename);
+
+  if (current_source_symtab->line_charpos == 0)
+    find_source_lines (current_source_symtab, desc);
+
+  if (line < 1 || line > current_source_symtab->nlines)
+    {
+      close (desc);
+      error ("Expression not found");
+    }
+
+  if (lseek (desc, current_source_symtab->line_charpos[line - 1], 0) < 0)
+    {
+      close (desc);
+      perror_with_name (current_source_symtab->filename);
+    }
+
+  stream = fdopen (desc, "r");
+  clearerr (stream);
+  while (1)
+    {
+      char buf[4096];          /* Should be reasonable??? */
+      register char *p = buf;
+
+      c = fgetc (stream);
+      if (c == EOF)
+       break;
+      do {
+       *p++ = c;
+      } while (c != '\n' && (c = fgetc (stream)) >= 0);
+
+      /* We now have a source line in buf; null terminate and match.  */
+      *p = 0;
+      if (re_exec (buf) > 0)
+       {
+         /* Match! */
+         fclose (stream);
+         print_source_lines (current_source_symtab,
+                             line, line+1, 0);
+         current_source_line = max (line - 5, 1);
+         return;
+       }
+      line--;
+      if (fseek (stream, current_source_symtab->line_charpos[line - 1], 0) < 0)
+       {
+         fclose (stream);
+         perror_with_name (current_source_symtab->filename);
+       }
+    }
+
+  printf ("Expression not found\n");
+  fclose (stream);
+  return;
+}
+\f
+void
+_initialize_source ()
+{
+  current_source_symtab = 0;
+  init_source_path ();
+
+  add_com ("directory", class_files, directory_command,
+          "Add directory DIR to end of search path for source files.\n\
+With no argument, reset the search path to just the working directory\n\
+and forget cached info on line positions in source files.");
+
+  add_info ("directories", directories_info,
+           "Current search path for finding source files.");
+
+  add_info ("line", line_info,
+           "Core addresses of the code for a source line.\n\
+Line can be specified as\n\
+  LINENUM, to list around that line in current file,\n\
+  FILE:LINENUM, to list around that line in that file,\n\
+  FUNCTION, to list around beginning of that function,\n\
+  FILE:FUNCTION, to distinguish among like-named static functions.\n\
+Default is to describe the last source line that was listed.\n\n\
+This sets the default address for \"x\" to the line's first instruction\n\
+so that \"x/i\" suffices to start examining the machine code.\n\
+The address is also stored as the value of \"$_\".");
+
+  add_com ("forward-search", class_files, forward_search_command,
+          "Search for regular expression (see regex(3)) from last line listed.");
+  add_com_alias ("search", "forward-search", class_files, 0);
+
+  add_com ("reverse-search", class_files, reverse_search_command,
+          "Search backward for regular expression (see regex(3)) from last line listed.");
+
+  add_com ("list", class_files, list_command,
+          "List specified function or line.\n\
+With no argument, lists ten more lines after or around previous listing.\n\
+\"list -\" lists the ten lines before a previous ten-line listing.\n\
+One argument specifies a line, and ten lines are listed around that line.\n\
+Two arguments with comma between specify starting and ending lines to list.\n\
+Lines can be specified in these ways:\n\
+  LINENUM, to list around that line in current file,\n\
+  FILE:LINENUM, to list around that line in that file,\n\
+  FUNCTION, to list around beginning of that function,\n\
+  FILE:FUNCTION, to distinguish among like-named static functions.\n\
+  *ADDRESS, to list around the line containing that address.\n\
+With two args if one is empty it stands for ten lines away from the other arg.");
+  add_com ("file", class_files, file_command,
+          "Select current file, function and line for display or list.\n\
+Specification can have the form:\n\
+  LINENUM, to select that line in current file,\n\
+  FILE:LINENUM, to select that line in that file,\n\
+  FUNCTION, to select beginning of that function,\n\
+  FILE:FUNCTION, to distinguish among like-named static functions.\n\
+  *ADDRESS, to select the line containing that address.");
+  add_com ("push-to-file", class_files, push_to_file_command,
+          "Like \"file\" command but remembers current file & line on a stack.\n\
+Can later return to current file with \"pop-file\" command.\n\
+Up to 32 file positions can be pushed on stack.");
+  add_com ("pop-file", class_files, pop_file_command,
+          "Pops back to file position saved by most recent \"push-to-file\".\n\
+If everything has been popped from stack, command does nothing.");
+}
+
diff --git a/usr/src/usr.bin/gdb/stack.c b/usr/src/usr.bin/gdb/stack.c
new file mode 100644 (file)
index 0000000..91218aa
--- /dev/null
@@ -0,0 +1,960 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)stack.c    6.3 (Berkeley) 5/8/91";
+#endif /* not lint */
+
+/* Print and select stack frames for GDB, the GNU debugger.
+   Copyright (C) 1986, 1987, 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING.  If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+/* modified by rjc Thu Nov  1 16:46:57 1990, fixed return_command so that
+   it can return values, it still has problems when running on pmax,
+   cannot write register 65 */
+
+#include <stdio.h>
+
+#include "defs.h"
+#include "param.h"
+#include "symtab.h"
+#include "frame.h"
+#include "value.h"
+
+
+/* Thie "selected" stack frame is used by default for local and arg access.
+   May be zero, for no selected frame.  */
+
+FRAME selected_frame;
+
+/* Level of the selected frame:
+   0 for innermost, 1 for its caller, ...
+   or -1 for frame specified by address with no defined level.  */
+
+int selected_frame_level;
+
+/* Nonzero means print the full filename and linenumber
+   when a frame is printed, and do so in a format programs can parse.  */
+
+int frame_file_full_name = 0;
+
+static void select_calling_frame ();
+
+void print_frame_info ();
+\f
+/* Print a stack frame briefly.  FRAME should be the frame id
+   and LEVEL should be its level in the stack (or -1 for level not defined).
+   This prints the level, the function executing, the arguments,
+   and the file name and line number.
+   If the pc is not at the beginning of the source line,
+   the actual pc is printed at the beginning.
+
+   If SOURCE is 1, print the source line as well.
+   If SOURCE is -1, print ONLY the source line.  */
+
+static void
+print_stack_frame (frame, level, source)
+     FRAME frame;
+     int level;
+     int source;
+{
+  struct frame_info *fi;
+
+  fi = get_frame_info (frame);
+
+  print_frame_info (fi, level, source, 1);
+}
+
+/* Flag which will indicate when the frame has been changed
+   by and "up" or "down" command.  */
+static int frame_changed;
+
+void
+print_frame_info (fi, level, source, args)
+     struct frame_info *fi;
+     register int level;
+     int source;
+     int args;
+{
+  struct symtab_and_line sal;
+  struct symbol *func;
+  register char *funname = 0;
+  int numargs;
+  struct partial_symtab *pst;
+
+  /* Don't give very much information if we haven't readin the
+     symbol table yet.  */
+  pst = find_pc_psymtab (fi->pc);
+  if (pst && !pst->readin)
+    {
+      /* Abbreviated information.  */
+      char *fname;
+
+      if (!find_pc_partial_function (fi->pc, &fname, 0))
+       fname = "??";
+       
+      printf_filtered ("#%-2d ", level);
+      printf_filtered ("0x%x in ", fi->pc);
+
+      fputs_demangled(fname, stdout, -1);
+      fputs_filtered(" (...)\n", stdout);
+      
+      return;
+    }
+
+  sal = find_pc_line (fi->pc, fi->next_frame);
+  func = find_pc_function (fi->pc);
+  if (func)
+    {
+      /* In certain pathological cases, the symtabs give the wrong
+        function (when we are in the first function in a file which
+        is compiled without debugging symbols, the previous function
+        is compiled with debugging symbols, and the "foo.o" symbol
+        that is supposed to tell us where the file with debugging symbols
+        ends has been truncated by ar because it is longer than 15
+        characters).
+
+        So look in the misc_function_vector as well, and if it comes
+        up with a larger address for the function use that instead.
+        I don't think this can ever cause any problems;
+        there shouldn't be any
+        misc_function_vector symbols in the middle of a function.  */
+      int misc_index = find_pc_misc_function (fi->pc);
+      if (misc_index >= 0
+         && (misc_function_vector[misc_index].address
+             > BLOCK_START (SYMBOL_BLOCK_VALUE (func))))
+       {
+         /* In this case we have no way of knowing the source file
+            and line number, so don't print them.  */
+         sal.symtab = 0;
+         /* We also don't know anything about the function besides
+            its address and name.  */
+         func = 0;
+         funname = misc_function_vector[misc_index].name;
+       }
+      else
+       funname = SYMBOL_NAME (func);
+    }
+  else
+    {
+      register int misc_index = find_pc_misc_function (fi->pc);
+      if (misc_index >= 0)
+       funname = misc_function_vector[misc_index].name;
+    }
+
+  if (frame_changed || source >= 0 || !sal.symtab)
+    {
+      if (level >= 0)
+       printf_filtered ("#%-2d ", level);
+      else if (frame_changed)
+       printf ("#%-2d ", 0);
+      if (fi->pc != sal.pc || !sal.symtab)
+       printf_filtered ("0x%x in ", fi->pc);
+      fputs_demangled(funname ? funname : "??", stdout, -1);
+      printf_filtered(" (");
+      if (args)
+        {
+         if (func)
+           numargs = -1;
+         else
+           FRAME_NUM_ARGS (numargs, fi);
+
+         print_frame_args (func, fi, numargs, stdout);
+       }
+      printf_filtered (")");
+      if (sal.symtab)
+       printf_filtered (" (%s line %d)", sal.symtab->filename, sal.line);
+      printf_filtered ("\n");
+    }
+
+  if ((frame_changed || source != 0) && sal.symtab)
+    {
+      int done = 0;
+      int mid_statement = source < 0 && fi->pc != sal.pc;
+      if (frame_file_full_name)
+       done = identify_source_line (sal.symtab, sal.line, mid_statement);
+      if (!done)
+       {
+         if (mid_statement)
+           printf_filtered ("0x%x\t", fi->pc);
+         print_source_lines (sal.symtab, sal.line, sal.line + 1, 1);
+       }
+      current_source_line = max (sal.line - 5, 1);
+    }
+  frame_changed = 0;
+  if (source != 0)
+    set_default_breakpoint (1, fi->pc, sal.symtab, sal.line);
+
+  fflush (stdout);
+}
+
+/* Call here to print info on selected frame, after a trap.  */
+
+void
+print_sel_frame (just_source)
+     int just_source;
+{
+  print_stack_frame (selected_frame, -1, just_source ? -1 : 1);
+}
+
+/* Print info on the selected frame, including level number
+   but not source.  */
+
+void
+print_selected_frame ()
+{
+  print_stack_frame (selected_frame, selected_frame_level, 0);
+}
+
+void flush_cached_frames ();
+
+#ifdef FRAME_SPECIFICATION_DYADIC
+extern FRAME setup_arbitrary_frame ();
+#endif
+
+/*
+ * Read a frame specification in whatever the appropriate format is.
+ */
+static FRAME
+parse_frame_specification (frame_exp)
+     char *frame_exp;
+{
+  int numargs = 0;
+  int arg1, arg2;
+  
+  if (frame_exp)
+    {
+      char *addr_string, *p;
+      struct cleanup *tmp_cleanup;
+      struct frame_info *fci;
+
+      while (*frame_exp == ' ') frame_exp++;
+      for (p = frame_exp; *p && *p != ' '; p++)
+       ;
+
+      if (*frame_exp)
+       {
+         numargs = 1;
+         addr_string = savestring(frame_exp, p - frame_exp);
+
+         {
+           tmp_cleanup = make_cleanup (free, addr_string);
+           arg1 = parse_and_eval_address (addr_string);
+           do_cleanups (tmp_cleanup);
+         }
+
+         while (*p == ' ') p++;
+         
+         if (*p)
+           {
+             numargs = 2;
+             arg2 = parse_and_eval_address (p);
+           }
+       }
+    }
+
+  switch (numargs)
+    {
+    case 0:
+      return selected_frame;
+      /* NOTREACHED */
+    case 1:
+      {
+       int level = arg1;
+       FRAME fid = find_relative_frame (get_current_frame (), &level);
+       FRAME tfid;
+
+       if (level == 0)
+         /* find_relative_frame was successful */
+         return fid;
+
+       /* If (s)he specifies the frame with an address, he deserves what
+          (s)he gets.  Still, give the highest one that matches.  */
+
+       for (fid = get_current_frame ();
+            fid && FRAME_FP (fid) != arg1;
+            fid = get_prev_frame (fid))
+         ;
+
+       if (fid)
+         while ((tfid = get_prev_frame (fid)) &&
+                (FRAME_FP (tfid) == arg1))
+           fid = tfid;
+         
+#ifdef FRAME_SPECIFICATION_DYADIC
+       if (!fid)
+         error ("Incorrect number of args in frame specification");
+
+       return fid;
+#else
+       return create_new_frame (arg1, 0);
+#endif
+      }
+      /* NOTREACHED */
+    case 2:
+      /* Must be addresses */
+#ifndef FRAME_SPECIFICATION_DYADIC
+      error ("Incorrect number of args in frame specification");
+#else
+      return setup_arbitrary_frame (arg1, arg2);
+#endif
+      /* NOTREACHED */
+    }
+  fatal ("Internal: Error in parsing in parse_frame_specification");
+  /* NOTREACHED */
+}
+
+/* FRAME_ARGS_ADDRESS_CORRECT is just like FRAME_ARGS_ADDRESS except
+   that if it is unsure about the answer, it returns Frame_unknown
+   instead of guessing (this happens on the VAX, for example).
+
+   On most machines, we never have to guess about the args address,
+   so FRAME_ARGS_ADDRESS{,_CORRECT} are the same.  */
+#if !defined (FRAME_ARGS_ADDRESS_CORRECT)
+#define FRAME_ARGS_ADDRESS_CORRECT FRAME_ARGS_ADDRESS
+#endif
+
+/* Print verbosely the selected frame or the frame at address ADDR.
+   This means absolutely all information in the frame is printed.  */
+
+static void
+frame_info (addr_exp)
+     char *addr_exp;
+{
+  FRAME frame;
+  struct frame_info *fi;
+  struct frame_saved_regs fsr;
+  struct symtab_and_line sal;
+  struct symbol *func;
+  FRAME calling_frame;
+  int i, count;
+  char *funname = 0;
+
+  if (!(have_inferior_p () || have_core_file_p ()))
+    error ("No inferior or core file.");
+
+  frame = parse_frame_specification (addr_exp);
+  if (!frame)
+    error ("Invalid frame specified.");
+
+  fi = get_frame_info (frame);
+  get_frame_saved_regs (fi, &fsr);
+  sal = find_pc_line (fi->pc, fi->next_frame);
+  func = get_frame_function (frame);
+  if (func)
+    funname = SYMBOL_NAME (func);
+  else
+    {
+      register int misc_index = find_pc_misc_function (fi->pc);
+      if (misc_index >= 0)
+       funname = misc_function_vector[misc_index].name;
+    }
+  calling_frame = get_prev_frame (frame);
+
+  if (!addr_exp && selected_frame_level >= 0)
+    printf ("Stack level %d, frame at 0x%x:\n pc = 0x%x",
+           selected_frame_level, FRAME_FP(frame), fi->pc);
+  else
+    printf ("Stack frame at 0x%x:\n pc = 0x%x",
+           FRAME_FP(frame), fi->pc);
+
+  if (funname)
+    printf (" in %s", funname);
+  if (sal.symtab)
+    printf (" (%s line %d)", sal.symtab->filename, sal.line);
+  printf ("; saved pc 0x%x\n", FRAME_SAVED_PC (frame));
+  if (calling_frame)
+    printf (" called by frame at 0x%x", FRAME_FP (calling_frame));
+  if (fi->next_frame && calling_frame)
+    printf (",");
+  if (fi->next_frame)
+    printf (" caller of frame at 0x%x", fi->next_frame);
+  if (fi->next_frame || calling_frame)
+    printf ("\n");
+
+  {
+    /* Address of the argument list for this frame, or Frame_unknown.  */
+    CORE_ADDR arg_list = FRAME_ARGS_ADDRESS_CORRECT (fi);
+    /* Number of args for this frame, or -1 if unknown.  */
+    int numargs;
+
+    if (arg_list != Frame_unknown)
+      {
+       printf (" Arglist at 0x%x,", arg_list);
+
+       FRAME_NUM_ARGS (numargs, fi);
+       if (numargs < 0)
+         printf (" args: ");
+       else if (numargs == 0)
+         printf (" no args.");
+       else if (numargs == 1)
+         printf (" 1 arg: ");
+       else
+         printf (" %d args: ", numargs);
+       print_frame_args (func, fi, numargs, stdout);
+       printf ("\n");
+      }
+  }
+  
+  /* The sp is special; what's returned isn't the save address, but
+     actually the value of the previous frame's sp.  */
+  printf (" Previous frame's sp is 0x%x\n", fsr.regs[SP_REGNUM]);
+  count = 0;
+  for (i = 0; i < NUM_REGS; i++)
+    if (fsr.regs[i] && i != SP_REGNUM)
+      {
+       if (count % 4 != 0)
+         printf (", ");
+       else
+         {
+           if (count == 0)
+             printf (" Saved registers:");
+           printf ("\n  ");
+         }
+       printf ("%s at 0x%x", reg_names[i], fsr.regs[i]);
+       count++;
+      }
+  if (count)
+    printf ("\n");
+}
+
+#if 0
+/* Set a limit on the number of frames printed by default in a
+   backtrace.  */
+
+static int backtrace_limit;
+
+static void
+set_backtrace_limit_command (count_exp, from_tty)
+     char *count_exp;
+     int from_tty;
+{
+  int count = parse_and_eval_address (count_exp);
+
+  if (count < 0)
+    error ("Negative argument not meaningful as backtrace limit.");
+
+  backtrace_limit = count;
+}
+
+static void
+backtrace_limit_info (arg, from_tty)
+     char *arg;
+     int from_tty;
+{
+  if (arg)
+    error ("\"Info backtrace-limit\" takes no arguments.");
+
+  printf ("Backtrace limit: %d.\n", backtrace_limit);
+}
+#endif
+
+/* Print briefly all stack frames or just the innermost COUNT frames.  */
+
+static void
+backtrace_command (count_exp)
+     char *count_exp;
+{
+  struct frame_info *fi;
+  register int count;
+  register FRAME frame;
+  register int i;
+  register FRAME trailing;
+  register int trailing_level;
+
+  /* The following code must do two things.  First, it must
+     set the variable TRAILING to the frame from which we should start
+     printing.  Second, it must set the variable count to the number
+     of frames which we should print, or -1 if all of them.  */
+  trailing = get_current_frame ();
+  trailing_level = 0;
+  if (count_exp)
+    {
+      count = parse_and_eval_address (count_exp);
+      if (count < 0)
+       {
+         FRAME current;
+
+         count = -count;
+
+         current = trailing;
+         while (current && count--)
+           current = get_prev_frame (current);
+         
+         /* Will stop when CURRENT reaches the top of the stack.  TRAILING
+            will be COUNT below it.  */
+         while (current)
+           {
+             trailing = get_prev_frame (trailing);
+             current = get_prev_frame (current);
+             trailing_level++;
+           }
+         
+         count = -1;
+       }
+    }
+  else
+    count = -1;
+
+  for (i = 0, frame = trailing;
+       frame && count--;
+       i++, frame = get_prev_frame (frame))
+    {
+      QUIT;
+      fi = get_frame_info (frame);
+      print_frame_info (fi, trailing_level + i, 0, 1);
+    }
+
+  /* If we've stopped before the end, mention that.  */
+  if (frame)
+    printf_filtered ("(More stack frames follow...)\n");
+}
+\f
+/* Print the local variables of a block B active in FRAME.
+   Return 1 if any variables were printed; 0 otherwise.  */
+
+static int
+print_block_frame_locals (b, frame, stream)
+     struct block *b;
+     register FRAME frame;
+     register FILE *stream;
+{
+  int nsyms;
+  register int i;
+  register struct symbol *sym;
+  register int values_printed = 0;
+
+  nsyms = BLOCK_NSYMS (b);
+
+  for (i = 0; i < nsyms; i++)
+    {
+      sym = BLOCK_SYM (b, i);
+      if (SYMBOL_CLASS (sym) == LOC_LOCAL
+         || SYMBOL_CLASS (sym) == LOC_REGISTER
+         || SYMBOL_CLASS (sym) == LOC_STATIC)
+       {
+         values_printed = 1;
+         fputs_filtered (SYMBOL_NAME (sym), stream);
+         fputs_filtered (" = ", stream);
+         print_variable_value (sym, frame, stream);
+         fprintf_filtered (stream, "\n");
+         fflush (stream);
+       }
+    }
+  return values_printed;
+}
+
+/* Print on STREAM all the local variables in frame FRAME,
+   including all the blocks active in that frame
+   at its current pc.
+
+   Returns 1 if the job was done,
+   or 0 if nothing was printed because we have no info
+   on the function running in FRAME.  */
+
+static int
+print_frame_local_vars (frame, stream)
+     register FRAME frame;
+     register FILE *stream;
+{
+  register struct block *block = get_frame_block (frame);
+  register int values_printed = 0;
+
+  if (block == 0)
+    {
+      fprintf_filtered (stream, "No symbol table info available.\n");
+      fflush (stream);
+      return 0;
+    }
+  
+  while (block != 0)
+    {
+      if (print_block_frame_locals (block, frame, stream))
+       values_printed = 1;
+      /* After handling the function's top-level block, stop.
+        Don't continue to its superblock, the block of
+        per-file symbols.  */
+      if (BLOCK_FUNCTION (block))
+       break;
+      block = BLOCK_SUPERBLOCK (block);
+    }
+
+  if (!values_printed)
+    {
+      fprintf_filtered (stream, "No locals.\n");
+      fflush (stream);
+    }
+  
+  return 1;
+}
+
+static void
+locals_info ()
+{
+  if (!have_inferior_p () && !have_core_file_p ())
+    error ("No inferior or core file.");
+
+  print_frame_local_vars (selected_frame, stdout);
+}
+
+static int
+print_frame_arg_vars (frame, stream)
+     register FRAME frame;
+     register FILE *stream;
+{
+  struct symbol *func = get_frame_function (frame);
+  register struct block *b;
+  int nsyms;
+  register int i;
+  register struct symbol *sym;
+  register int values_printed = 0;
+
+  if (func == 0)
+    {
+      fprintf_filtered (stream, "No symbol table info available.\n");
+      fflush (stream);
+      return 0;
+    }
+
+  b = SYMBOL_BLOCK_VALUE (func);
+  nsyms = BLOCK_NSYMS (b);
+
+  for (i = 0; i < nsyms; i++)
+    {
+      sym = BLOCK_SYM (b, i);
+      if (SYMBOL_CLASS (sym) == LOC_ARG
+         || SYMBOL_CLASS (sym) == LOC_REF_ARG
+         || SYMBOL_CLASS (sym) == LOC_REGPARM)
+       {
+         values_printed = 1;
+         fputs_filtered (SYMBOL_NAME (sym), stream);
+         fputs_filtered (" = ", stream);
+         print_variable_value (sym, frame, stream);
+         fprintf_filtered (stream, "\n");
+         fflush (stream);
+       }
+    }
+
+  if (!values_printed)
+    {
+      fprintf_filtered (stream, "No arguments.\n");
+      fflush (stream);
+    }
+
+  return 1;
+}
+
+static void
+args_info ()
+{
+  if (!have_inferior_p () && !have_core_file_p ())
+    error ("No inferior or core file.");
+  print_frame_arg_vars (selected_frame, stdout);
+}
+\f
+/* Select frame FRAME, and note that its stack level is LEVEL.
+   LEVEL may be -1 if an actual level number is not known.  */
+
+void
+select_frame (frame, level)
+     FRAME frame;
+     int level;
+{
+  selected_frame = frame;
+  selected_frame_level = level;
+  /* Ensure that symbols for this frame are readin.  */
+  if (frame)
+    find_pc_symtab (get_frame_info (frame)->pc);
+}
+
+/* Store the selected frame and its level into *FRAMEP and *LEVELP.  */
+
+void
+record_selected_frame (frameaddrp, levelp)
+     FRAME_ADDR *frameaddrp;
+     int *levelp;
+{
+  *frameaddrp = FRAME_FP (selected_frame);
+  *levelp = selected_frame_level;
+}
+
+/* Return the symbol-block in which the selected frame is executing.
+   Can return zero under various legitimate circumstances.  */
+
+struct block *
+get_selected_block ()
+{
+  if (!have_inferior_p () && !have_core_file_p ())
+    return 0;
+
+  if (!selected_frame)
+    return get_current_block ();
+  return get_frame_block (selected_frame);
+}
+
+/* Find a frame a certain number of levels away from FRAME.
+   LEVEL_OFFSET_PTR points to an int containing the number of levels.
+   Positive means go to earlier frames (up); negative, the reverse.
+   The int that contains the number of levels is counted toward
+   zero as the frames for those levels are found.
+   If the top or bottom frame is reached, that frame is returned,
+   but the final value of *LEVEL_OFFSET_PTR is nonzero and indicates
+   how much farther the original request asked to go.  */
+
+FRAME
+find_relative_frame (frame, level_offset_ptr)
+     register FRAME frame;
+     register int* level_offset_ptr;
+{
+  register FRAME prev;
+  register FRAME frame1, frame2;
+
+  /* Going up is simple: just do get_prev_frame enough times
+     or until initial frame is reached.  */
+  while (*level_offset_ptr > 0)
+    {
+      prev = get_prev_frame (frame);
+      if (prev == 0)
+       break;
+      (*level_offset_ptr)--;
+      frame = prev;
+    }
+  /* Going down could be done by iterating get_frame_info to
+     find the next frame, but that would be quadratic
+     since get_frame_info must scan all the way from the current frame.
+     The following algorithm is linear.  */
+  if (*level_offset_ptr < 0)
+    {
+      /* First put frame1 at innermost frame
+        and frame2 N levels up from there.  */
+      frame1 = get_current_frame ();
+      frame2 = frame1;
+      while (*level_offset_ptr < 0 && frame2 != frame)
+       {
+         frame2 = get_prev_frame (frame2);
+         (*level_offset_ptr) ++;
+       }
+      /* Then slide frame1 and frame2 up in synchrony
+        and when frame2 reaches our starting point
+        frame1 must be N levels down from there.  */
+      while (frame2 != frame)
+       {
+         frame1 = get_prev_frame (frame1);
+         frame2 = get_prev_frame (frame2);
+       }
+      return frame1;
+    }
+  return frame;
+}
+
+/* The "frame" command.  With no arg, print selected frame briefly.
+   With arg LEVEL_EXP, select the frame at level LEVEL if it is a
+   valid level.  Otherwise, treat level_exp as an address expression
+   and print it.  See parse_frame_specification for more info on proper
+   frame expressions. */
+
+static void
+frame_command (level_exp, from_tty)
+     char *level_exp;
+     int from_tty;
+{
+  register FRAME frame, frame1;
+  unsigned int level = 0;
+
+  if (!have_inferior_p () && ! have_core_file_p ())
+    error ("No inferior or core file.");
+
+  frame = parse_frame_specification (level_exp);
+
+  for (frame1 = get_prev_frame (0);
+       frame1 && frame1 != frame;
+       frame1 = get_prev_frame (frame1))
+    level++;
+
+  if (!frame1)
+    level = 0;
+
+  frame_changed = level;
+  select_frame (frame, level);
+
+  if (!from_tty)
+    return;
+
+  print_stack_frame (selected_frame, selected_frame_level, 1);
+}
+
+/* Select the frame up one or COUNT stack levels
+   from the previously selected frame, and print it briefly.  */
+
+static void
+up_command (count_exp)
+     char *count_exp;
+{
+  register FRAME frame;
+  int count = 1, count1;
+  if (count_exp)
+    count = parse_and_eval_address (count_exp);
+  count1 = count;
+  
+  if (!have_inferior_p () && !have_core_file_p ())
+    error ("No inferior or core file.");
+
+  frame = find_relative_frame (selected_frame, &count1);
+  if (count1 != 0 && count_exp == 0)
+    error ("Initial frame selected; you cannot go up.");
+  select_frame (frame, selected_frame_level + count - count1);
+
+  print_stack_frame (selected_frame, selected_frame_level, 1);
+  frame_changed++;
+}
+
+/* Select the frame down one or COUNT stack levels
+   from the previously selected frame, and print it briefly.  */
+
+static void
+down_command (count_exp)
+     char *count_exp;
+{
+  register FRAME frame;
+  int count = -1, count1;
+  if (count_exp)
+    count = - parse_and_eval_address (count_exp);
+  count1 = count;
+  
+  frame = find_relative_frame (selected_frame, &count1);
+  if (count1 != 0 && count_exp == 0)
+    error ("Bottom (i.e., innermost) frame selected; you cannot go down.");
+  select_frame (frame, selected_frame_level + count - count1);
+
+  print_stack_frame (selected_frame, selected_frame_level, 1);
+  frame_changed--;
+}
+\f
+static void
+return_command (retval_exp, from_tty)
+     char *retval_exp;
+     int from_tty;
+{
+    value return_value;
+  struct symbol *thisfun = get_frame_function (selected_frame);
+  FRAME_ADDR selected_frame_addr = FRAME_FP (selected_frame);
+
+  /* If interactive, require confirmation.  */
+
+  if (from_tty)
+    {
+      if (thisfun != 0)
+       {
+         if (!query ("Make %s return now? ", SYMBOL_NAME (thisfun)))
+           error ("Not confirmed.");
+       }
+      else
+       if (!query ("Make selected stack frame return now? "))
+         error ("Not confirmed.");
+    }
+
+  /* Do the real work.  Pop until the specified frame is current.  We
+     use this method because the selected_frame is not valid after
+     a POP_FRAME.  Note that this will not work if the selected frame
+     shares it's fp with another frame.  */
+
+  while (selected_frame_addr != FRAME_FP (get_current_frame()))
+    POP_FRAME;
+
+  /* get the return value while still in this frame */
+  if (retval_exp) 
+      return_value = parse_and_eval (retval_exp);
+
+  /* Then pop that frame.  */
+  POP_FRAME;
+
+  /* Store the return value if there was one */
+
+  if (retval_exp)
+    set_return_value (return_value);
+
+  /* If interactive, print the frame that is now current.  */
+
+  if (from_tty)
+    frame_command ("0", 1);
+}
+\f
+extern struct cmd_list_element *setlist;
+
+void
+_initialize_stack ()
+{
+#if 0  
+  backtrace_limit = 30;
+#endif
+
+  add_com ("return", class_stack, return_command,
+          "Make selected stack frame return to its caller.\n\
+Control remains in the debugger, but when you continue\n\
+execution will resume in the frame above the one now selected.\n\
+If an argument is given, it is an expression for the value to return.");
+
+  add_com ("up", class_stack, up_command,
+          "Select and print stack frame that called this one.\n\
+An argument says how many frames up to go.");
+
+  add_com ("down", class_stack, down_command,
+          "Select and print stack frame called by this one.\n\
+An argument says how many frames down to go.");
+  add_com_alias ("do", "down", class_stack, 1);
+
+  add_com ("frame", class_stack, frame_command,
+          "Select and print a stack frame.\n\
+With no argument, print the selected stack frame.  (See also \"info frame\").\n\
+An argument specifies the frame to select.\n\
+It can be a stack frame number or the address of the frame.\n\
+With argument, nothing is printed if input is coming from\n\
+a command file or a user-defined command.");
+
+  add_com_alias ("f", "frame", class_stack, 1);
+
+  add_com ("backtrace", class_stack, backtrace_command,
+          "Print backtrace of all stack frames, or innermost COUNT frames.\n\
+With a negative argument, print outermost -COUNT frames.");
+  add_com_alias ("bt", "backtrace", class_stack, 0);
+  add_com_alias ("where", "backtrace", class_alias, 0);
+  add_info ("stack", backtrace_command,
+           "Backtrace of the stack, or innermost COUNT frames.");
+  add_info_alias ("s", "stack", 1);
+  add_info ("frame", frame_info,
+           "All about selected stack frame, or frame at ADDR.");
+  add_info_alias ("f", "frame", 1);
+  add_info ("locals", locals_info,
+           "Local variables of current stack frame.");
+  add_info ("args", args_info,
+           "Argument variables of current stack frame.");
+
+#if 0
+  add_cmd ("backtrace-limit", class_stack, set_backtrace_limit_command, 
+          "Specify maximum number of frames for \"backtrace\" to print by default.",
+          &setlist);
+  add_info ("backtrace-limit", backtrace_limit_info,
+           "The maximum number of frames for \"backtrace\" to print by default.");
+#endif
+}
+
diff --git a/usr/src/usr.bin/gdb/symtab.c b/usr/src/usr.bin/gdb/symtab.c
new file mode 100644 (file)
index 0000000..4dfe50f
--- /dev/null
@@ -0,0 +1,2473 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory.
+ *
+ * $Header: symtab.c,v 1.9 91/03/05 13:39:59 mccanne Exp $;
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)symtab.c   6.3 (Berkeley) 5/8/91";
+#endif /* not lint */
+
+/* Symbol table lookup for the GNU debugger, GDB.
+   Copyright (C) 1986, 1987, 1988, 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING.  If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#include <stdio.h>
+#include "defs.h"
+#include "symtab.h"
+#include "param.h"
+
+#include <obstack.h>
+#include <assert.h>
+
+char *index ();
+extern char *cplus_demangle ();
+extern struct value * value_of_this ();
+
+/* Allocate an obstack to hold objects that should be freed
+   when we load a new symbol table.
+   This includes the symbols made by dbxread
+   and the types that are not permanent.  */
+
+struct obstack obstack1;
+
+struct obstack *symbol_obstack = &obstack1;
+
+/* This obstack will be used for partial_symbol objects.  It can
+   probably actually be the same as the symbol_obstack above, but I'd
+   like to keep them seperate for now.  If I want to later, I'll
+   replace one with the other.  */
+
+struct obstack obstack2;
+
+struct obstack *psymbol_obstack = &obstack2;
+
+/* These variables point to the objects
+   representing the predefined C data types.  */
+
+struct type *builtin_type_void;
+struct type *builtin_type_char;
+struct type *builtin_type_short;
+struct type *builtin_type_int;
+struct type *builtin_type_long;
+#ifdef LONG_LONG
+struct type *builtin_type_long_long;
+#endif
+struct type *builtin_type_unsigned_char;
+struct type *builtin_type_unsigned_short;
+struct type *builtin_type_unsigned_int;
+struct type *builtin_type_unsigned_long;
+#ifdef LONG_LONG
+struct type *builtin_type_unsigned_long_long;
+#endif
+struct type *builtin_type_float;
+struct type *builtin_type_double;
+
+/* Block in which the most recently searched-for symbol was found.
+   Might be better to make this a parameter to lookup_symbol and 
+   value_of_this. */
+struct block *block_found;
+
+/* Functions */
+static int find_line_common ();
+int lookup_misc_func ();
+struct partial_symtab *lookup_partial_symtab ();
+struct symtab *psymtab_to_symtab ();
+static struct partial_symbol *lookup_partial_symbol ();
+
+/* Check for a symtab of a specific name; first in symtabs, then in
+   psymtabs.  *If* there is no '/' in the name, a match after a '/'
+   in the symtab filename will also work.  */
+
+static struct symtab *
+lookup_symtab_1 (name)
+     char *name;
+{
+  register struct symtab *s;
+  register struct partial_symtab *ps;
+  register char *slash = index (name, '/');
+  register int len = strlen (name);
+
+  for (s = symtab_list; s; s = s->next)
+    if (!strcmp (name, s->filename))
+      return s;
+
+  for (ps = partial_symtab_list; ps; ps = ps->next)
+    if (!strcmp (name, ps->filename))
+      {
+       if (ps->readin)
+         fatal ("Internal: readin pst found when no symtab found.");
+       s = psymtab_to_symtab (ps);
+       return s;
+      }
+
+  if (!slash)
+    {
+      for (s = symtab_list; s; s = s->next)
+       {
+         int l = strlen (s->filename);
+
+         if (s->filename[l - len -1] == '/'
+             && !strcmp (s->filename + l - len, name))
+           return s;
+       }
+
+      for (ps = partial_symtab_list; ps; ps = ps->next)
+       {
+         int l = strlen (ps->filename);
+
+         if (ps->filename[l - len - 1] == '/'
+             && !strcmp (ps->filename + l - len, name))
+           {
+             if (ps->readin)
+               fatal ("Internal: readin pst found when no symtab found.");
+             s = psymtab_to_symtab (ps);
+             return s;
+           }
+       }
+    }
+  return 0;
+}
+
+/* Lookup the symbol table of a source file named NAME.  Try a couple
+   of variations if the first lookup doesn't work.  */
+
+struct symtab *
+lookup_symtab (name)
+     char *name;
+{
+  register struct symtab *s;
+  register char *copy;
+
+  s = lookup_symtab_1 (name);
+  if (s) return s;
+
+  /* If name not found as specified, see if adding ".c" helps.  */
+
+  copy = (char *) alloca (strlen (name) + 3);
+  strcpy (copy, name);
+  strcat (copy, ".c");
+  s = lookup_symtab_1 (copy);
+  if (s) return s;
+
+  /* We didn't find anything; die.  */
+  return 0;
+}
+
+/* Lookup the partial symbol table of a source file named NAME.  This
+   only returns true on an exact match (ie. this semantics are
+   different from lookup_symtab.  */
+
+struct partial_symtab *
+lookup_partial_symtab (name)
+char *name;
+{
+  register struct partial_symtab *s;
+  register char *copy;
+  
+  for (s = partial_symtab_list; s; s = s->next)
+    if (!strcmp (name, s->filename))
+      return s;
+  
+  return 0;
+}
+\f
+/* Lookup a typedef or primitive type named NAME,
+   visible in lexical block BLOCK.
+   If NOERR is nonzero, return zero if NAME is not suitably defined.  */
+
+struct type *
+lookup_typename (name, block, noerr)
+     char *name;
+     struct block *block;
+     int noerr;
+{
+  register struct symbol *sym = lookup_symbol (name, block, VAR_NAMESPACE, 0);
+  if (sym == 0 || SYMBOL_CLASS (sym) != LOC_TYPEDEF)
+    {
+      if (!strcmp (name, "int"))
+       return builtin_type_int;
+      if (!strcmp (name, "long"))
+       return builtin_type_long;
+      if (!strcmp (name, "short"))
+       return builtin_type_short;
+      if (!strcmp (name, "char"))
+       return builtin_type_char;
+      if (!strcmp (name, "float"))
+       return builtin_type_float;
+      if (!strcmp (name, "double"))
+       return builtin_type_double;
+      if (!strcmp (name, "void"))
+       return builtin_type_void;
+
+      if (noerr)
+       return 0;
+      error ("No type named %s.", name);
+    }
+  return SYMBOL_TYPE (sym);
+}
+
+struct type *
+lookup_unsigned_typename (name)
+     char *name;
+{
+  if (!strcmp (name, "int"))
+    return builtin_type_unsigned_int;
+  if (!strcmp (name, "long"))
+    return builtin_type_unsigned_long;
+  if (!strcmp (name, "short"))
+    return builtin_type_unsigned_short;
+  if (!strcmp (name, "char"))
+    return builtin_type_unsigned_char;
+  error ("No type named unsigned %s.", name);
+}
+
+/* Lookup a structure type named "struct NAME",
+   visible in lexical block BLOCK.  */
+
+struct type *
+lookup_struct (name, block)
+     char *name;
+     struct block *block;
+{
+  register struct symbol *sym 
+    = lookup_symbol (name, block, STRUCT_NAMESPACE, 0);
+
+  if (sym == 0)
+    error ("No struct type named %s.", name);
+  if (TYPE_CODE (SYMBOL_TYPE (sym)) != TYPE_CODE_STRUCT)
+    error ("This context has class, union or enum %s, not a struct.", name);
+  return SYMBOL_TYPE (sym);
+}
+
+/* Lookup a union type named "union NAME",
+   visible in lexical block BLOCK.  */
+
+struct type *
+lookup_union (name, block)
+     char *name;
+     struct block *block;
+{
+  register struct symbol *sym 
+    = lookup_symbol (name, block, STRUCT_NAMESPACE, 0);
+
+  if (sym == 0)
+    error ("No union type named %s.", name);
+  if (TYPE_CODE (SYMBOL_TYPE (sym)) != TYPE_CODE_UNION)
+    error ("This context has class, struct or enum %s, not a union.", name);
+  return SYMBOL_TYPE (sym);
+}
+
+/* Lookup an enum type named "enum NAME",
+   visible in lexical block BLOCK.  */
+
+struct type *
+lookup_enum (name, block)
+     char *name;
+     struct block *block;
+{
+  register struct symbol *sym 
+    = lookup_symbol (name, block, STRUCT_NAMESPACE, 0);
+  if (sym == 0)
+    error ("No enum type named %s.", name);
+  if (TYPE_CODE (SYMBOL_TYPE (sym)) != TYPE_CODE_ENUM)
+    error ("This context has class, struct or union %s, not an enum.", name);
+  return SYMBOL_TYPE (sym);
+}
+
+/* Given a type TYPE, lookup the type of the component of type named
+   NAME.  */
+
+struct type *
+lookup_struct_elt_type (type, name)
+     struct type *type;
+     char *name;
+{
+  struct type *t;
+  int i;
+  char *errmsg;
+
+  if (TYPE_CODE (type) != TYPE_CODE_STRUCT
+      && TYPE_CODE (type) != TYPE_CODE_UNION)
+    {
+      terminal_ours ();
+      fflush (stdout);
+      fprintf (stderr, "Type ");
+      type_print (type, "", stderr, -1);
+      fprintf (stderr, " is not a structure or union type.\n");
+      return_to_top_level ();
+    }
+
+  for (i = TYPE_NFIELDS (type) - 1; i >= 0; i--)
+    if (!strcmp (TYPE_FIELD_NAME (type, i), name))
+      return TYPE_FIELD_TYPE (type, i);
+
+  terminal_ours ();
+  fflush (stdout);
+  fprintf (stderr, "Type ");
+  type_print (type, "", stderr, -1);
+  fprintf (stderr, " has no component named %s\n", name);
+  return_to_top_level ();
+}
+
+/* Given a type TYPE, return a type of pointers to that type.
+   May need to construct such a type if this is the first use.
+
+   C++: use TYPE_MAIN_VARIANT and TYPE_CHAIN to keep pointer
+   to member types under control.  */
+
+struct type *
+lookup_pointer_type (type)
+     struct type *type;
+{
+  register struct type *ptype = TYPE_POINTER_TYPE (type);
+  if (ptype) return TYPE_MAIN_VARIANT (ptype);
+
+  /* This is the first time anyone wanted a pointer to a TYPE.  */
+  if (TYPE_FLAGS (type) & TYPE_FLAG_PERM)
+    ptype  = (struct type *) xmalloc (sizeof (struct type));
+  else
+    ptype  = (struct type *) obstack_alloc (symbol_obstack,
+                                           sizeof (struct type));
+
+  bzero (ptype, sizeof (struct type));
+  TYPE_MAIN_VARIANT (ptype) = ptype;
+  TYPE_TARGET_TYPE (ptype) = type;
+  TYPE_POINTER_TYPE (type) = ptype;
+  /* New type is permanent if type pointed to is permanent.  */
+  if (TYPE_FLAGS (type) & TYPE_FLAG_PERM)
+    TYPE_FLAGS (ptype) |= TYPE_FLAG_PERM;
+  /* We assume the machine has only one representation for pointers!  */
+  TYPE_LENGTH (ptype) = sizeof (char *);
+  TYPE_CODE (ptype) = TYPE_CODE_PTR;
+  return ptype;
+}
+
+struct type *
+lookup_reference_type (type)
+     struct type *type;
+{
+  register struct type *rtype = TYPE_REFERENCE_TYPE (type);
+  if (rtype) return TYPE_MAIN_VARIANT (rtype);
+
+  /* This is the first time anyone wanted a pointer to a TYPE.  */
+  if (TYPE_FLAGS (type) & TYPE_FLAG_PERM)
+    rtype  = (struct type *) xmalloc (sizeof (struct type));
+  else
+    rtype  = (struct type *) obstack_alloc (symbol_obstack,
+                                           sizeof (struct type));
+
+  bzero (rtype, sizeof (struct type));
+  TYPE_MAIN_VARIANT (rtype) = rtype;
+  TYPE_TARGET_TYPE (rtype) = type;
+  TYPE_REFERENCE_TYPE (type) = rtype;
+  /* New type is permanent if type pointed to is permanent.  */
+  if (TYPE_FLAGS (type) & TYPE_FLAG_PERM)
+    TYPE_FLAGS (rtype) |= TYPE_FLAG_PERM;
+  /* We assume the machine has only one representation for pointers!  */
+  TYPE_LENGTH (rtype) = sizeof (char *);
+  TYPE_CODE (rtype) = TYPE_CODE_REF;
+  return rtype;
+}
+
+
+/* Implement direct support for MEMBER_TYPE in GNU C++.
+   May need to construct such a type if this is the first use.
+   The TYPE is the type of the member.  The DOMAIN is the type
+   of the aggregate that the member belongs to.  */
+
+struct type *
+lookup_member_type (type, domain)
+     struct type *type, *domain;
+{
+  register struct type *mtype = TYPE_MAIN_VARIANT (type);
+  struct type *main_type;
+
+  main_type = mtype;
+  while (mtype)
+    {
+      if (TYPE_DOMAIN_TYPE (mtype) == domain)
+       return mtype;
+      mtype = TYPE_NEXT_VARIANT (mtype);
+    }
+
+  /* This is the first time anyone wanted this member type.  */
+  if (TYPE_FLAGS (type) & TYPE_FLAG_PERM)
+    mtype  = (struct type *) xmalloc (sizeof (struct type));
+  else
+    mtype  = (struct type *) obstack_alloc (symbol_obstack,
+                                           sizeof (struct type));
+
+  bzero (mtype, sizeof (struct type));
+  if (main_type == 0)
+    main_type = mtype;
+  else
+    {
+      TYPE_NEXT_VARIANT (mtype) = TYPE_NEXT_VARIANT (main_type);
+      TYPE_NEXT_VARIANT (main_type) = mtype;
+    }
+  TYPE_MAIN_VARIANT (mtype) = main_type;
+  TYPE_TARGET_TYPE (mtype) = type;
+  TYPE_DOMAIN_TYPE (mtype) = domain;
+  /* New type is permanent if type pointed to is permanent.  */
+  if (TYPE_FLAGS (type) & TYPE_FLAG_PERM)
+    TYPE_FLAGS (mtype) |= TYPE_FLAG_PERM;
+
+  /* In practice, this is never used.  */
+  TYPE_LENGTH (mtype) = 1;
+  TYPE_CODE (mtype) = TYPE_CODE_MEMBER;
+
+#if 0
+  /* Now splice in the new member pointer type.  */
+  if (main_type)
+    {
+      /* This type was not "smashed".  */
+      TYPE_CHAIN (mtype) = TYPE_CHAIN (main_type);
+      TYPE_CHAIN (main_type) = mtype;
+    }
+#endif
+
+  return mtype;
+}
+
+struct type *
+lookup_method_type (type, domain, args)
+     struct type *type, *domain, **args;
+{
+  register struct type *mtype = TYPE_MAIN_VARIANT (type);
+  struct type *main_type;
+
+  main_type = mtype;
+  while (mtype)
+    {
+      if (TYPE_DOMAIN_TYPE (mtype) == domain)
+       {
+         struct type **t1 = args;
+         struct type **t2 = TYPE_ARG_TYPES (mtype);
+         if (t2)
+           {
+             int i;
+             for (i = 0; t1[i] != 0 && t1[i]->code != TYPE_CODE_VOID; i++)
+               if (t1[i] != t2[i])
+                 break;
+             if (t1[i] == t2[i])
+               return mtype;
+           }
+       }
+      mtype = TYPE_NEXT_VARIANT (mtype);
+    }
+
+  /* This is the first time anyone wanted this member type.  */
+  if (TYPE_FLAGS (type) & TYPE_FLAG_PERM)
+    mtype  = (struct type *) xmalloc (sizeof (struct type));
+  else
+    mtype  = (struct type *) obstack_alloc (symbol_obstack,
+                                           sizeof (struct type));
+
+  bzero (mtype, sizeof (struct type));
+  if (main_type == 0)
+    main_type = mtype;
+  else
+    {
+      TYPE_NEXT_VARIANT (mtype) = TYPE_NEXT_VARIANT (main_type);
+      TYPE_NEXT_VARIANT (main_type) = mtype;
+    }
+  TYPE_MAIN_VARIANT (mtype) = main_type;
+  TYPE_TARGET_TYPE (mtype) = type;
+  TYPE_DOMAIN_TYPE (mtype) = domain;
+  TYPE_ARG_TYPES (mtype) = args;
+  /* New type is permanent if type pointed to is permanent.  */
+  if (TYPE_FLAGS (type) & TYPE_FLAG_PERM)
+    TYPE_FLAGS (mtype) |= TYPE_FLAG_PERM;
+
+  /* In practice, this is never used.  */
+  TYPE_LENGTH (mtype) = 1;
+  TYPE_CODE (mtype) = TYPE_CODE_METHOD;
+
+#if 0
+  /* Now splice in the new member pointer type.  */
+  if (main_type)
+    {
+      /* This type was not "smashed".  */
+      TYPE_CHAIN (mtype) = TYPE_CHAIN (main_type);
+      TYPE_CHAIN (main_type) = mtype;
+    }
+#endif
+
+  return mtype;
+}
+
+/* Given a type TYPE, return a type which has offset OFFSET,
+   via_virtual VIA_VIRTUAL, and via_public VIA_PUBLIC.
+   May need to construct such a type if none exists.  */
+struct type *
+lookup_basetype_type (type, offset, via_virtual, via_public)
+     struct type *type;
+     int offset;
+     int via_virtual, via_public;
+{
+  register struct type *btype = TYPE_MAIN_VARIANT (type);
+  struct type *main_type;
+
+  if (offset != 0)
+    {
+      printf ("Internal error: type offset non-zero in lookup_basetype_type");
+      offset = 0;
+    }
+
+  main_type = btype;
+  while (btype)
+    {
+      if (/* TYPE_OFFSET (btype) == offset
+         && */ TYPE_VIA_PUBLIC (btype) == via_public
+         && TYPE_VIA_VIRTUAL (btype) == via_virtual)
+       return btype;
+      btype = TYPE_NEXT_VARIANT (btype);
+    }
+
+  /* This is the first time anyone wanted this member type.  */
+  if (TYPE_FLAGS (type) & TYPE_FLAG_PERM)
+    btype  = (struct type *) xmalloc (sizeof (struct type));
+  else
+    btype  = (struct type *) obstack_alloc (symbol_obstack,
+                                           sizeof (struct type));
+
+  if (main_type == 0)
+    {
+      main_type = btype;
+      bzero (btype, sizeof (struct type));
+      TYPE_MAIN_VARIANT (btype) = main_type;
+    }
+  else
+    {
+      bcopy (main_type, btype, sizeof (struct type));
+      TYPE_NEXT_VARIANT (main_type) = btype;
+    }
+/* TYPE_OFFSET (btype) = offset; */
+  if (via_public)
+    TYPE_FLAGS (btype) |= TYPE_FLAG_VIA_PUBLIC;
+  if (via_virtual)
+    TYPE_FLAGS (btype) |= TYPE_FLAG_VIA_VIRTUAL;
+  /* New type is permanent if type pointed to is permanent.  */
+  if (TYPE_FLAGS (type) & TYPE_FLAG_PERM)
+    TYPE_FLAGS (btype) |= TYPE_FLAG_PERM;
+
+  /* In practice, this is never used.  */
+  TYPE_LENGTH (btype) = 1;
+  TYPE_CODE (btype) = TYPE_CODE_STRUCT;
+
+  return btype;
+}
+
+/* Given a type TYPE, return a type of functions that return that type.
+   May need to construct such a type if this is the first use.  */
+
+struct type *
+lookup_function_type (type)
+     struct type *type;
+{
+  register struct type *ptype = TYPE_FUNCTION_TYPE (type);
+  if (ptype) return ptype;
+
+  /* This is the first time anyone wanted a function returning a TYPE.  */
+  if (TYPE_FLAGS (type) & TYPE_FLAG_PERM)
+    ptype  = (struct type *) xmalloc (sizeof (struct type));
+  else
+    ptype  = (struct type *) obstack_alloc (symbol_obstack,
+                                           sizeof (struct type));
+
+  bzero (ptype, sizeof (struct type));
+  TYPE_TARGET_TYPE (ptype) = type;
+  TYPE_FUNCTION_TYPE (type) = ptype;
+  /* New type is permanent if type returned is permanent.  */
+  if (TYPE_FLAGS (type) & TYPE_FLAG_PERM)
+    TYPE_FLAGS (ptype) |= TYPE_FLAG_PERM;
+  TYPE_LENGTH (ptype) = 1;
+  TYPE_CODE (ptype) = TYPE_CODE_FUNC;
+  TYPE_NFIELDS (ptype) = 0;
+  return ptype;
+}
+\f
+/* Create an array type.  Elements will be of type TYPE, and there will
+   be NUM of them.
+
+   Eventually this should be extended to take two more arguments which
+   specify the bounds of the array and the type of the index.
+   It should also be changed to be a "lookup" function, with the
+   appropriate data structures added to the type field.
+   Then read array type should call here.  */
+
+struct type *
+create_array_type (element_type, number)
+     struct type *element_type;
+     int number;
+{
+  struct type *result_type = (struct type *)
+    obstack_alloc (symbol_obstack, sizeof (struct type));
+
+  bzero (result_type, sizeof (struct type));
+
+  TYPE_CODE (result_type) = TYPE_CODE_ARRAY;
+  TYPE_TARGET_TYPE (result_type) = element_type;
+  TYPE_LENGTH (result_type) = number * TYPE_LENGTH (element_type);
+  TYPE_NFIELDS (result_type) = 1;
+  TYPE_FIELDS (result_type) =
+    (struct field *) obstack_alloc (symbol_obstack, sizeof (struct field));
+  TYPE_FIELD_TYPE (result_type, 0) = builtin_type_int;
+  TYPE_VPTR_FIELDNO (result_type) = -1;
+
+  return result_type;
+}
+
+\f
+/* Smash TYPE to be a type of pointers to TO_TYPE.
+   If TO_TYPE is not permanent and has no pointer-type yet,
+   record TYPE as its pointer-type.  */
+
+void
+smash_to_pointer_type (type, to_type)
+     struct type *type, *to_type;
+{
+  int type_permanent = (TYPE_FLAGS (type) & TYPE_FLAG_PERM);
+  
+  bzero (type, sizeof (struct type));
+  TYPE_TARGET_TYPE (type) = to_type;
+  /* We assume the machine has only one representation for pointers!  */
+  TYPE_LENGTH (type) = sizeof (char *);
+  TYPE_CODE (type) = TYPE_CODE_PTR;
+
+  TYPE_MAIN_VARIANT (type) = type;
+
+  if (type_permanent)
+    TYPE_FLAGS (type) |= TYPE_FLAG_PERM;
+
+  if (TYPE_POINTER_TYPE (to_type) == 0
+      && (!(TYPE_FLAGS (to_type) & TYPE_FLAG_PERM)
+         || type_permanent))
+    {
+      TYPE_POINTER_TYPE (to_type) = type;
+    }
+}
+
+/* Smash TYPE to be a type of members of DOMAIN with type TO_TYPE.  */
+
+void
+smash_to_member_type (type, domain, to_type)
+     struct type *type, *domain, *to_type;
+{
+  bzero (type, sizeof (struct type));
+  TYPE_TARGET_TYPE (type) = to_type;
+  TYPE_DOMAIN_TYPE (type) = domain;
+
+  /* In practice, this is never needed.  */
+  TYPE_LENGTH (type) = 1;
+  TYPE_CODE (type) = TYPE_CODE_MEMBER;
+
+  TYPE_MAIN_VARIANT (type) = lookup_member_type (domain, to_type);
+}
+
+/* Smash TYPE to be a type of method of DOMAIN with type TO_TYPE.  */
+
+void
+smash_to_method_type (type, domain, to_type, args)
+     struct type *type, *domain, *to_type, **args;
+{
+  bzero (type, sizeof (struct type));
+  TYPE_TARGET_TYPE (type) = to_type;
+  TYPE_DOMAIN_TYPE (type) = domain;
+  TYPE_ARG_TYPES (type) = args;
+
+  /* In practice, this is never needed.  */
+  TYPE_LENGTH (type) = 1;
+  TYPE_CODE (type) = TYPE_CODE_METHOD;
+
+  TYPE_MAIN_VARIANT (type) = lookup_method_type (domain, to_type, args);
+}
+
+/* Smash TYPE to be a type of reference to TO_TYPE.
+   If TO_TYPE is not permanent and has no pointer-type yet,
+   record TYPE as its pointer-type.  */
+
+void
+smash_to_reference_type (type, to_type)
+     struct type *type, *to_type;
+{
+  int type_permanent = (TYPE_FLAGS (type) & TYPE_FLAG_PERM);
+
+  bzero (type, sizeof (struct type));
+  TYPE_TARGET_TYPE (type) = to_type;
+  /* We assume the machine has only one representation for pointers!  */
+  TYPE_LENGTH (type) = sizeof (char *);
+  TYPE_CODE (type) = TYPE_CODE_REF;
+
+  TYPE_MAIN_VARIANT (type) = type;
+
+  if (type_permanent)
+    TYPE_FLAGS (type) |= TYPE_FLAG_PERM;
+
+  if (TYPE_REFERENCE_TYPE (to_type) == 0
+      && (!(TYPE_FLAGS (to_type) & TYPE_FLAG_PERM)
+         || type_permanent))
+    {
+      TYPE_REFERENCE_TYPE (to_type) = type;
+    }
+}
+
+/* Smash TYPE to be a type of functions returning TO_TYPE.
+   If TO_TYPE is not permanent and has no function-type yet,
+   record TYPE as its function-type.  */
+
+void
+smash_to_function_type (type, to_type)
+     struct type *type, *to_type;
+{
+  int type_permanent = (TYPE_FLAGS (type) & TYPE_FLAG_PERM);
+
+  bzero (type, sizeof (struct type));
+  TYPE_TARGET_TYPE (type) = to_type;
+  TYPE_LENGTH (type) = 1;
+  TYPE_CODE (type) = TYPE_CODE_FUNC;
+  TYPE_NFIELDS (type) = 0;
+
+  if (type_permanent)
+    TYPE_FLAGS (type) |= TYPE_FLAG_PERM;
+
+  if (TYPE_FUNCTION_TYPE (to_type) == 0
+      && (!(TYPE_FLAGS (to_type) & TYPE_FLAG_PERM)
+         || type_permanent))
+    {
+      TYPE_FUNCTION_TYPE (to_type) = type;
+    }
+}
+\f
+/* Find which partial symtab on the partial_symtab_list contains
+   PC.  Return 0 if none.  */
+
+struct partial_symtab *
+find_pc_psymtab (pc)
+     register CORE_ADDR pc;
+{
+  register struct partial_symtab *ps;
+
+  for (ps = partial_symtab_list; ps; ps = ps->next)
+    if (pc >= ps->textlow && pc < ps->texthigh)
+      return ps;
+
+  return 0;
+}
+
+/* Find which partial symbol within a psymtab contains PC.  Return 0
+   if none.  Check all psymtabs if PSYMTAB is 0.  */
+struct partial_symbol *
+find_pc_psymbol (psymtab, pc)
+     struct partial_symtab *psymtab;
+     CORE_ADDR pc;
+{
+  struct partial_symbol *best, *p;
+  int best_pc;
+  
+  if (!psymtab)
+    psymtab = find_pc_psymtab (pc);
+  if (!psymtab)
+    return 0;
+
+  best_pc = psymtab->textlow - 1;
+
+  for (p = static_psymbols.list + psymtab->statics_offset;
+       (p - (static_psymbols.list + psymtab->statics_offset)
+       < psymtab->n_static_syms);
+       p++)
+    if (SYMBOL_NAMESPACE (p) == VAR_NAMESPACE
+       && SYMBOL_CLASS (p) == LOC_BLOCK
+       && pc >= SYMBOL_VALUE (p)
+       && SYMBOL_VALUE (p) > best_pc)
+      {
+       best_pc = SYMBOL_VALUE (p);
+       best = p;
+      }
+  if (best_pc == psymtab->textlow - 1)
+    return 0;
+  return best;
+}
+
+\f
+static struct symbol *lookup_block_symbol ();
+
+/* Find the definition for a specified symbol name NAME
+   in namespace NAMESPACE, visible from lexical block BLOCK.
+   Returns the struct symbol pointer, or zero if no symbol is found. 
+   C++: if IS_A_FIELD_OF_THIS is nonzero on entry, check to see if
+   NAME is a field of the current implied argument `this'.  If so set
+   *IS_A_FIELD_OF_THIS to 1, otherwise set it to zero. 
+   BLOCK_FOUND is set to the block in which NAME is found (in the case of
+   a field of `this', value_of_this sets BLOCK_FOUND to the proper value.) */
+
+struct symbol *
+lookup_symbol (name, block, namespace, is_a_field_of_this)
+     char *name;
+     register struct block *block;
+     enum namespace namespace;
+     int *is_a_field_of_this;
+{
+  register int i, n;
+  register struct symbol *sym;
+  register struct symtab *s;
+  register struct partial_symtab *ps;
+  register struct partial_symbol *psym;
+  struct blockvector *bv;
+
+  /* Search specified block and its superiors.  */
+
+  while (block != 0)
+    {
+      sym = lookup_block_symbol (block, name, namespace);
+      if (sym) 
+       {
+         block_found = block;
+         return sym;
+       }
+      block = BLOCK_SUPERBLOCK (block);
+    }
+
+  /* C++: If requested to do so by the caller, 
+     check to see if NAME is a field of `this'. */
+  if (is_a_field_of_this)
+    {
+      struct value *v = value_of_this (0);
+      
+      *is_a_field_of_this = 0;
+      if (v && check_field (v, name))
+       {
+         *is_a_field_of_this = 1;          
+         return 0;
+       }
+    }
+
+  /* Now search all global blocks.  Do the symtab's first, then
+     check the psymtab's */
+
+  for (s = symtab_list; s; s = s->next)
+    {
+      bv = BLOCKVECTOR (s);
+      block = BLOCKVECTOR_BLOCK (bv, 0);
+      sym = lookup_block_symbol (block, name, namespace);
+      if (sym) 
+       {
+         block_found = block;
+         return sym;
+       }
+    }
+
+  /* Check for the possibility of the symbol being a global function
+     that is stored on the misc function vector.  Eventually, all
+     global symbols might be resolved in this way.  */
+  
+  if (namespace == VAR_NAMESPACE)
+    {
+      int index = lookup_misc_func (name);
+
+      if (index == -1)
+       { /* Look for a mangled C++ name for NAME. */
+         int name_len = strlen (name);
+         for (index = misc_function_count; --index >= 0; )
+             /* Assume orginal name is prefix of mangled name. */
+             if (!strncmp (misc_function_vector[index].name, name, name_len))
+               {
+                 char *demangled =
+                     cplus_demangle(misc_function_vector[index].name, -1);
+                 if (demangled != NULL)
+                   {
+                     int cond = strcmp (demangled, name);
+                     free (demangled);
+                     if (!cond)
+                         break;
+                   }
+               }
+         /* Loop terminates on no match with index == -1. */
+        }
+
+      if (index != -1)
+       {
+         ps = find_pc_psymtab (misc_function_vector[index].address);
+         if (ps && !ps->readin)
+           {
+             s = psymtab_to_symtab (ps);
+             bv = BLOCKVECTOR (s);
+             block = BLOCKVECTOR_BLOCK (bv, 0);
+             sym = lookup_block_symbol (block, name, namespace);
+             /* sym == 0 if symbol was found in the psymtab but not
+                in the symtab.
+                Return 0 to use the misc_function definition of "foo_".
+
+                This happens for Fortran  "foo_" symbols,
+                which are "foo" in the symtab.
+
+                This can also happen if "asm" is used to make a
+                regular symbol but not a debugging symbol, e.g.
+                asm(".globl _main");
+                asm("_main:");
+                */
+             
+             return sym;
+           }
+       }
+    }
+      
+  if (psym = lookup_partial_symbol (name, 1, namespace))
+    {
+      ps = psym->pst;
+      s = psymtab_to_symtab(ps);
+      bv = BLOCKVECTOR (s);
+      block = BLOCKVECTOR_BLOCK (bv, 0);
+      sym = lookup_block_symbol (block, name, namespace);
+      if (!sym)
+       fatal ("Internal: global symbol found in psymtab but not in symtab");
+      return sym;
+    }
+
+  /* Now search all per-file blocks.
+     Not strictly correct, but more useful than an error.
+     Do the symtabs first, then check the psymtabs */
+
+  for (s = symtab_list; s; s = s->next)
+    {
+      bv = BLOCKVECTOR (s);
+      block = BLOCKVECTOR_BLOCK (bv, 1);
+      sym = lookup_block_symbol (block, name, namespace);
+      if (sym) 
+       {
+         block_found = block;
+         return sym;
+       }
+    }
+
+  if (psym = lookup_partial_symbol(name, 0, namespace))
+    {
+      ps = psym->pst;
+      s = psymtab_to_symtab(ps);
+      bv = BLOCKVECTOR (s);
+      block = BLOCKVECTOR_BLOCK (bv, 1);
+      sym = lookup_block_symbol (block, name, namespace);
+      if (!sym)
+       fatal ("Internal: static symbol found in psymtab but not in symtab");
+      return sym;
+    }
+
+  return 0;
+}
+
+/* Look, in partial_symtab PST, for symbol NAME.  Check the global
+   symbols if GLOBAL, the static symbols if not */
+
+static struct partial_symbol *
+lookup_partial_symbol (name, global, namespace)
+     register char *name;
+     register int global;
+     register enum namespace namespace;
+{
+  register struct partial_symbol *start, *psym;
+  register struct partial_symbol *top, *bottom, *center;
+  register struct partial_symtab *pst;
+  register int length;
+
+  if (global)
+    {
+      start = global_psymbols.list;
+      length = global_psymbols.next - start;
+    }
+  else
+    {
+      start = static_psymbols.list;
+      length = static_psymbols.next - start;
+    }
+
+  if (!length)
+    return (struct partial_symbol *) 0;
+  
+  /* Binary search.  This search is guarranteed to end with center
+     pointing at the earliest partial symbol with the correct
+     name.  At that point *all* partial symbols with that name
+     will be checked against the correct namespace. */
+  bottom = start;
+  top = start + length - 1;
+  while (top > bottom)
+    {
+      center = bottom + (top - bottom) / 2;
+
+      assert (center < top);
+      
+      if (strcmp (SYMBOL_NAME (center), name) >= 0)
+       top = center;
+      else
+       bottom = center + 1;
+    }
+  assert (top == bottom);
+  
+  while (strcmp (SYMBOL_NAME (top), name) == 0)
+    {
+      if (!top->pst->readin && SYMBOL_NAMESPACE (top) == namespace)
+       return top;
+      top ++;
+    }
+
+  return (struct partial_symbol *) 0;
+}
+
+/* Look for a symbol in block BLOCK.  */
+
+static struct symbol *
+lookup_block_symbol (block, name, namespace)
+     register struct block *block;
+     char *name;
+     enum namespace namespace;
+{
+  register int bot, top, inc;
+  register struct symbol *sym, *parameter_sym;
+
+  top = BLOCK_NSYMS (block);
+  bot = 0;
+
+  /* If the blocks's symbols were sorted, start with a binary search.  */
+
+  if (BLOCK_SHOULD_SORT (block))
+    {
+      /* First, advance BOT to not far before
+        the first symbol whose name is NAME.  */
+
+      while (1)
+       {
+         inc = (top - bot + 1);
+         /* No need to keep binary searching for the last few bits worth.  */
+         if (inc < 4)
+           break;
+         inc = (inc >> 1) + bot;
+         sym = BLOCK_SYM (block, inc);
+         if (SYMBOL_NAME (sym)[0] < name[0])
+           bot = inc;
+         else if (SYMBOL_NAME (sym)[0] > name[0])
+           top = inc;
+         else if (strcmp (SYMBOL_NAME (sym), name) < 0)
+           bot = inc;
+         else
+           top = inc;
+       }
+
+      /* Now scan forward until we run out of symbols,
+        find one whose name is greater than NAME,
+        or find one we want.
+        If there is more than one symbol with the right name and namespace,
+        we return the first one.  dbxread.c is careful to make sure
+        that if one is a register then it comes first.  */
+
+      top = BLOCK_NSYMS (block);
+      while (bot < top)
+       {
+         sym = BLOCK_SYM (block, bot);
+         inc = SYMBOL_NAME (sym)[0] - name[0];
+         if (inc == 0)
+           inc = strcmp (SYMBOL_NAME (sym), name);
+         if (inc == 0 && SYMBOL_NAMESPACE (sym) == namespace)
+           return sym;
+         if (inc > 0)
+           return 0;
+         bot++;
+       }
+      return 0;
+    }
+
+  /* Here if block isn't sorted.
+     This loop is equivalent to the loop above,
+     but hacked greatly for speed.
+
+     Note that parameter symbols do not always show up last in the
+     list; this loop makes sure to take anything else other than
+     parameter symbols first; it only uses parameter symbols as a
+     last resort.  Note that this only takes up extra computation
+     time on a match.  */
+
+  parameter_sym = (struct symbol *) 0;
+  top = BLOCK_NSYMS (block);
+  inc = name[0];
+  while (bot < top)
+    {
+      sym = BLOCK_SYM (block, bot);
+      if (SYMBOL_NAME (sym)[0] == inc
+         && !strcmp (SYMBOL_NAME (sym), name)
+         && SYMBOL_NAMESPACE (sym) == namespace)
+       {
+         if (SYMBOL_CLASS (sym) == LOC_ARG
+             || SYMBOL_CLASS (sym) == LOC_REF_ARG
+             || SYMBOL_CLASS (sym) == LOC_REGPARM)
+           parameter_sym = sym;
+         else
+           return sym;
+       }
+      bot++;
+    }
+  return parameter_sym;                /* Will be 0 if not found. */
+}
+\f
+/* Return the symbol for the function which contains a specified
+   lexical block, described by a struct block BL.  */
+
+struct symbol *
+block_function (bl)
+     struct block *bl;
+{
+  while (BLOCK_FUNCTION (bl) == 0 && BLOCK_SUPERBLOCK (bl) != 0)
+    bl = BLOCK_SUPERBLOCK (bl);
+
+  return BLOCK_FUNCTION (bl);
+}
+
+/* Subroutine of find_pc_line */
+
+struct symtab *
+find_pc_symtab (pc)
+     register CORE_ADDR pc;
+{
+  register struct block *b;
+  struct blockvector *bv;
+  register struct symtab *s;
+  register struct partial_symtab *ps;
+
+  /* Search all symtabs for one whose file contains our pc */
+
+  for (s = symtab_list; s; s = s->next)
+    {
+      bv = BLOCKVECTOR (s);
+      b = BLOCKVECTOR_BLOCK (bv, 0);
+      if (BLOCK_START (b) <= pc
+         && BLOCK_END (b) > pc)
+       break;
+    }
+
+  if (!s)
+    {
+      ps = find_pc_psymtab (pc);
+      if (ps && ps->readin)
+       fatal ("Internal error: pc in read in psymtab, but not in symtab.");
+
+      if (ps)
+       s = psymtab_to_symtab (ps);
+    }
+
+  return s;
+}
+
+/* Find the source file and line number for a given PC value.
+   Return a structure containing a symtab pointer, a line number,
+   and a pc range for the entire source line.
+   The value's .pc field is NOT the specified pc.
+   NOTCURRENT nonzero means, if specified pc is on a line boundary,
+   use the line that ends there.  Otherwise, in that case, the line
+   that begins there is used.  */
+
+struct symtab_and_line
+find_pc_line (pc, notcurrent)
+     CORE_ADDR pc;
+     int notcurrent;
+{
+  struct symtab *s;
+  register struct linetable *l;
+  register int len;
+  register int i;
+  register struct linetable_entry *item;
+  struct symtab_and_line value;
+  struct blockvector *bv;
+
+  /* Info on best line seen so far, and where it starts, and its file.  */
+
+  int best_line = 0;
+  CORE_ADDR best_pc = 0;
+  CORE_ADDR best_end = 0;
+  struct symtab *best_symtab = 0;
+
+  /* Store here the first line number
+     of a file which contains the line at the smallest pc after PC.
+     If we don't find a line whose range contains PC,
+     we will use a line one less than this,
+     with a range from the start of that file to the first line's pc.  */
+  int alt_line = 0;
+  CORE_ADDR alt_pc = 0;
+  struct symtab *alt_symtab = 0;
+
+  /* Info on best line seen in this file.  */
+
+  int prev_line;
+  CORE_ADDR prev_pc;
+
+  /* Info on first line of this file.  */
+
+  int first_line;
+  CORE_ADDR first_pc;
+
+  /* If this pc is not from the current frame,
+     it is the address of the end of a call instruction.
+     Quite likely that is the start of the following statement.
+     But what we want is the statement containing the instruction.
+     Fudge the pc to make sure we get that.  */
+
+  if (notcurrent) pc -= 1;
+
+  s = find_pc_symtab (pc);
+  if (s == 0)
+    {
+      value.symtab = 0;
+      value.line = 0;
+      value.pc = pc;
+      value.end = 0;
+      return value;
+    }
+
+  bv = BLOCKVECTOR (s);
+
+  /* Look at all the symtabs that share this blockvector.
+     They all have the same apriori range, that we found was right;
+     but they have different line tables.  */
+
+  for (; s && BLOCKVECTOR (s) == bv; s = s->next)
+    {
+      /* Find the best line in this symtab.  */
+      l = LINETABLE (s);
+      len = l->nitems;
+      prev_line = -1;
+      first_line = -1;
+      for (i = 0; i < len; i++)
+       {
+         item = &(l->item[i]);
+         
+         if (first_line < 0)
+           {
+             first_line = item->line;
+             first_pc = item->pc;
+           }
+         /* Return the last line that did not start after PC.  */
+         if (pc >= item->pc)
+           {
+             prev_line = item->line;
+             prev_pc = item->pc;
+           }
+         else
+           break;
+       }
+
+      /* Is this file's best line closer than the best in the other files?
+        If so, record this file, and its best line, as best so far.  */
+      if (prev_line >= 0 && prev_pc > best_pc)
+       {
+         best_pc = prev_pc;
+         best_line = prev_line;
+         best_symtab = s;
+         if (i < len)
+           best_end = item->pc;
+         else
+           best_end = 0;
+       }
+      /* Is this file's first line closer than the first lines of other files?
+        If so, record this file, and its first line, as best alternate.  */
+      if (first_line >= 0 && first_pc > pc
+         && (alt_pc == 0 || first_pc < alt_pc))
+       {
+         alt_pc = first_pc;
+         alt_line = first_line;
+         alt_symtab = s;
+       }
+    }
+  if (best_symtab == 0)
+    {
+      value.symtab = alt_symtab;
+      value.line = alt_line - 1;
+      value.pc = BLOCK_END (BLOCKVECTOR_BLOCK (bv, 0));
+      value.end = alt_pc;
+    }
+  else
+    {
+      value.symtab = best_symtab;
+      value.line = best_line;
+      value.pc = best_pc;
+      value.end = (best_end ? best_end
+                  : (alt_pc ? alt_pc
+                     : BLOCK_END (BLOCKVECTOR_BLOCK (bv, 0))));
+    }
+  return value;
+}
+\f
+/* Find the PC value for a given source file and line number.
+   Returns zero for invalid line number.
+   The source file is specified with a struct symtab.  */
+
+CORE_ADDR
+find_line_pc (symtab, line)
+     struct symtab *symtab;
+     int line;
+{
+  register struct linetable *l;
+  register int index;
+  int dummy;
+
+  if (symtab == 0)
+    return 0;
+  l = LINETABLE (symtab);
+  index = find_line_common(l, line, &dummy);
+  return index ? l->item[index].pc : 0;
+}
+
+/* Find the range of pc values in a line.
+   Store the starting pc of the line into *STARTPTR
+   and the ending pc (start of next line) into *ENDPTR.
+   Returns 1 to indicate success.
+   Returns 0 if could not find the specified line.  */
+
+int
+find_line_pc_range (symtab, thisline, startptr, endptr)
+     struct symtab *symtab;
+     int thisline;
+     CORE_ADDR *startptr, *endptr;
+{
+  register struct linetable *l;
+  register int index;
+  int exact_match;             /* did we get an exact linenumber match */
+  register CORE_ADDR prev_pc;
+  CORE_ADDR last_pc;
+
+  if (symtab == 0)
+    return 0;
+
+  l = LINETABLE (symtab);
+  index = find_line_common (l, thisline, &exact_match);
+  if (index)
+    {
+      *startptr = l->item[index].pc;
+      /* If we have not seen an entry for the specified line,
+        assume that means the specified line has zero bytes.  */
+      if (!exact_match || index == l->nitems-1)
+       *endptr = *startptr;
+      else
+       /* Perhaps the following entry is for the following line.
+          It's worth a try.  */
+       if (l->item[index+1].line == thisline + 1)
+         *endptr = l->item[index+1].pc;
+       else
+         *endptr = find_line_pc (symtab, thisline+1);
+      return 1;
+    }
+
+  return 0;
+}
+
+/* Given a line table and a line number, return the index into the line
+   table for the pc of the nearest line whose number is >= the specified one.
+   Return 0 if none is found.  The value is never zero is it is an index.
+
+   Set *EXACT_MATCH nonzero if the value returned is an exact match.  */
+
+static int
+find_line_common (l, lineno, exact_match)
+     register struct linetable *l;
+     register int lineno;
+     int *exact_match;
+{
+  register int i;
+  register int len;
+
+  /* BEST is the smallest linenumber > LINENO so far seen,
+     or 0 if none has been seen so far.
+     BEST_INDEX identifies the item for it.  */
+
+  int best_index = 0;
+  int best = 0;
+
+  int nextline = -1;
+
+  if (lineno <= 0)
+    return 0;
+
+  len = l->nitems;
+  for (i = 0; i < len; i++)
+    {
+      register struct linetable_entry *item = &(l->item[i]);
+
+      if (item->line == lineno)
+       {
+         *exact_match = 1;
+         return i;
+       }
+
+      if (item->line > lineno && (best == 0 || item->line < best))
+       {
+         best = item->line;
+         best_index = i;
+       }
+    }
+
+  /* If we got here, we didn't get an exact match.  */
+
+  *exact_match = 0;
+  return best_index;
+}
+
+int
+find_pc_line_pc_range (pc, startptr, endptr)
+     CORE_ADDR pc;
+     CORE_ADDR *startptr, *endptr;
+{
+  struct symtab_and_line sal;
+  sal = find_pc_line (pc, 0);
+  *startptr = sal.pc;
+  *endptr = sal.end;
+  return sal.symtab != 0;
+}
+\f
+/* Parse a string that specifies a line number.
+   Pass the address of a char * variable; that variable will be
+   advanced over the characters actually parsed.
+
+   The string can be:
+
+   LINENUM -- that line number in current file.  PC returned is 0.
+   FILE:LINENUM -- that line in that file.  PC returned is 0.
+   FUNCTION -- line number of openbrace of that function.
+      PC returned is the start of the function.
+   FILE:FUNCTION -- likewise, but prefer functions in that file.
+   *EXPR -- line in which address EXPR appears.
+
+   FUNCTION may be an undebuggable function found in misc_function_vector.
+
+   If the argument FUNFIRSTLINE is nonzero, we want the first line
+   of real code inside a function when a function is specified.
+
+   DEFAULT_SYMTAB specifies the file to use if none is specified.
+   It defaults to current_source_symtab.
+   DEFAULT_LINE specifies the line number to use for relative
+   line numbers (that start with signs).  Defaults to current_source_line.
+
+   Note that it is possible to return zero for the symtab
+   if no file is validly specified.  Callers must check that.
+   Also, the line number returned may be invalid.  */
+
+struct symtabs_and_lines
+decode_line_1 (argptr, funfirstline, default_symtab, default_line)
+     char **argptr;
+     int funfirstline;
+     struct symtab *default_symtab;
+     int default_line;
+{
+  struct symtabs_and_lines decode_line_2 ();
+  struct symtabs_and_lines values;
+  struct symtab_and_line value;
+  register char *p, *p1;
+  register struct symtab *s;
+  register struct symbol *sym;
+  register CORE_ADDR pc;
+  register int i;
+  char *copy;
+  struct symbol *sym_class;
+  char *class_name, *method_name, *phys_name;
+  int method_counter;
+  int i1;
+  struct symbol **sym_arr;
+  struct type *t, *field;
+  char **physnames;
+  
+  /* Defaults have defaults.  */
+
+  if (default_symtab == 0)
+    {
+      default_symtab = current_source_symtab;
+      default_line = current_source_line;
+    }
+
+  /* See if arg is *PC */
+
+  if (**argptr == '*')
+    {
+      (*argptr)++;
+      pc = parse_and_eval_address_1 (argptr);
+      values.sals = (struct symtab_and_line *)
+       malloc (sizeof (struct symtab_and_line));
+      values.nelts = 1;
+      values.sals[0] = find_pc_line (pc, 0);
+      values.sals[0].pc = pc;
+      return values;
+    }
+
+  /* Maybe arg is FILE : LINENUM or FILE : FUNCTION */
+
+  s = 0;
+
+  for (p = *argptr; *p; p++)
+    {
+      if (p[0] == ':' || p[0] == ' ' || p[0] == '\t')
+       break;
+    }
+  while (p[0] == ' ' || p[0] == '\t') p++;
+
+  if (p[0] == ':')
+    {
+
+      /*  C++  */
+      if (p[1] ==':')
+       {
+         /* Extract the class name.  */
+         p1 = p;
+         while (p != *argptr && p[-1] == ' ') --p;
+         copy = (char *) alloca (p - *argptr + 1);
+         bcopy (*argptr, copy, p - *argptr);
+         copy[p - *argptr] = 0;
+
+         /* Discard the class name from the arg.  */
+         p = p1 + 2;
+         while (*p == ' ' || *p == '\t') p++;
+         *argptr = p;
+
+         sym_class = lookup_symbol (copy, 0, STRUCT_NAMESPACE, 0);
+       
+         if (sym_class &&
+             (TYPE_CODE (SYMBOL_TYPE (sym_class)) == TYPE_CODE_STRUCT
+              || TYPE_CODE (SYMBOL_TYPE (sym_class)) == TYPE_CODE_UNION))
+           {
+             /* Arg token is not digits => try it as a function name
+                Find the next token (everything up to end or next whitespace). */
+             p = *argptr;
+             while (*p && *p != ' ' && *p != '\t' && *p != ',' && *p !=':') p++;
+             copy = (char *) alloca (p - *argptr + 1);
+             bcopy (*argptr, copy, p - *argptr);
+             copy[p - *argptr] = '\0';
+
+             /* no line number may be specified */
+             while (*p == ' ' || *p == '\t') p++;
+             *argptr = p;
+
+             sym = 0;
+             i1 = 0;           /*  counter for the symbol array */
+             t = SYMBOL_TYPE (sym_class);
+             sym_arr = (struct symbol **) alloca(TYPE_NFN_FIELDS_TOTAL (t) * sizeof(struct symbol*));
+             physnames = (char **) alloca (TYPE_NFN_FIELDS_TOTAL (t) * sizeof(char*));
+
+             if (destructor_name_p (copy, t))
+               {
+                 /* destructors are a special case.  */
+                 struct fn_field *f = TYPE_FN_FIELDLIST1 (t, 0);
+                 int len = TYPE_FN_FIELDLIST_LENGTH (t, 0) - 1;
+                 phys_name = TYPE_FN_FIELD_PHYSNAME (f, len);
+                 physnames[i1] = (char *)alloca (strlen (phys_name) + 1);
+                 strcpy (physnames[i1], phys_name);
+                 sym_arr[i1] = lookup_symbol (phys_name, SYMBOL_BLOCK_VALUE (sym_class), VAR_NAMESPACE, 0);
+                 if (sym_arr[i1]) i1++;
+               }
+             else while (t)
+               {
+                 class_name = TYPE_NAME (t);
+                 /* Ignore this class if it doesn't have a name.
+                    This prevents core dumps, but is just a workaround
+                    because we might not find the function in
+                    certain cases, such as
+                    struct D {virtual int f();}
+                    struct C : D {virtual int g();}
+                    (in this case g++ 1.35.1- does not put out a name
+                    for D as such, it defines type 19 (for example) in
+                    the same stab as C, and then does a
+                    .stabs "D:T19" and a .stabs "D:t19".
+                    Thus
+                    "break C::f" should not be looking for field f in
+                    the class named D, 
+                    but just for the field f in the baseclasses of C
+                    (no matter what their names).
+
+                    However, I don't know how to replace the code below
+                    that depends on knowing the name of D.  */
+                 if (class_name)
+                   {
+                     /* We just want the class name.  In the context
+                        of C++, stripping off "struct " is always
+                        sensible.  */
+                     if (strncmp("struct ", class_name, 7) == 0)
+                       class_name += 7;
+                     if (strncmp("union ", class_name, 6) == 0)
+                       class_name += 6;
+
+                     sym_class = lookup_symbol (class_name, 0, STRUCT_NAMESPACE, 0);
+                     for (method_counter = TYPE_NFN_FIELDS (SYMBOL_TYPE (sym_class)) - 1;
+                          method_counter >= 0;
+                          --method_counter)
+                       {
+                         int field_counter;
+                         struct fn_field *f =
+                           TYPE_FN_FIELDLIST1 (SYMBOL_TYPE (sym_class), method_counter);
+
+                         method_name = TYPE_FN_FIELDLIST_NAME (SYMBOL_TYPE (sym_class), method_counter);
+                         if (!strcmp (copy, method_name))
+                           /* Find all the fields with that name.  */
+                           for (field_counter = TYPE_FN_FIELDLIST_LENGTH (SYMBOL_TYPE (sym_class), method_counter) - 1;
+                                field_counter >= 0;
+                                --field_counter)
+                             {
+                               phys_name = TYPE_FN_FIELD_PHYSNAME (f, field_counter);
+                               physnames[i1] = (char*) alloca (strlen (phys_name) + 1);
+                               strcpy (physnames[i1], phys_name);
+                               sym_arr[i1] = lookup_symbol (phys_name, SYMBOL_BLOCK_VALUE (sym_class), VAR_NAMESPACE, 0);
+                               if (sym_arr[i1]) i1++;
+                             }
+                       }
+                   }
+                 if (TYPE_N_BASECLASSES (t))
+                   t = TYPE_BASECLASS(t, 1);
+                 else
+                   break;
+               }
+
+             if (i1 == 1)
+               {
+                 /* There is exactly one field with that name.  */
+                 sym = sym_arr[0];
+
+                 if (sym && SYMBOL_CLASS (sym) == LOC_BLOCK)
+                   {
+                     /* Arg is the name of a function */
+                     pc = BLOCK_START (SYMBOL_BLOCK_VALUE (sym)) + FUNCTION_START_OFFSET;
+                     if (funfirstline)
+                       SKIP_PROLOGUE (pc);
+                     values.sals = (struct symtab_and_line *)malloc (sizeof (struct symtab_and_line));
+                     values.nelts = 1;
+                     values.sals[0] = find_pc_line (pc, 0);
+                     values.sals[0].pc = (values.sals[0].end && values.sals[0].pc != pc) ? values.sals[0].end : pc;
+                   }
+                 else
+                   {
+                     values.nelts = 0;
+                   }
+                 return values;
+               }
+             if (i1 > 0)
+               {
+                 /* There is more than one field with that name
+                    (overloaded).  Ask the user which one to use.  */
+                 return decode_line_2 (argptr, sym_arr, physnames,
+                                       i1, funfirstline);
+               }
+             else
+               error ("that class does not have any method named %s",copy);
+           }
+         else
+           error("no class, struct, or union named %s", copy );
+       }
+      /*  end of C++  */
+
+
+      /* Extract the file name.  */
+      p1 = p;
+      while (p != *argptr && p[-1] == ' ') --p;
+      copy = (char *) alloca (p - *argptr + 1);
+      bcopy (*argptr, copy, p - *argptr);
+      copy[p - *argptr] = 0;
+
+      /* Find that file's data.  */
+      s = lookup_symtab (copy);
+      if (s == 0)
+       {
+         if (symtab_list == 0 && partial_symtab_list == 0)
+           error ("No symbol table is loaded.  Use the \"symbol-file\" command.");
+         error ("No source file named %s.", copy);
+       }
+
+      /* Discard the file name from the arg.  */
+      p = p1 + 1;
+      while (*p == ' ' || *p == '\t') p++;
+      *argptr = p;
+    }
+
+  /* S is specified file's symtab, or 0 if no file specified.
+     arg no longer contains the file name.  */
+
+  /* Check whether arg is all digits (and sign) */
+
+  p = *argptr;
+  if (*p == '-' || *p == '+') p++;
+  while (*p >= '0' && *p <= '9')
+    p++;
+
+  if (p != *argptr && (*p == 0 || *p == ' ' || *p == '\t' || *p == ','))
+    {
+      /* We found a token consisting of all digits -- at least one digit.  */
+      enum sign {none, plus, minus} sign = none;
+
+      /* This is where we need to make sure that we have good defaults.
+        We must guarrantee that this section of code is never executed
+        when we are called with just a function name, since
+        select_source_symtab calls us with such an argument  */
+
+      if (s == 0 && default_symtab == 0)
+       {
+         if (symtab_list == 0 && partial_symtab_list == 0)
+           error ("No symbol table is loaded.  Use the \"symbol-file\" command.");
+         select_source_symtab (0);
+         default_symtab = current_source_symtab;
+         default_line = current_source_line;
+       }
+
+      if (**argptr == '+')
+       sign = plus, (*argptr)++;
+      else if (**argptr == '-')
+       sign = minus, (*argptr)++;
+      value.line = atoi (*argptr);
+      switch (sign)
+       {
+       case plus:
+         if (p == *argptr)
+           value.line = 5;
+         if (s == 0)
+           value.line = default_line + value.line;
+         break;
+       case minus:
+         if (p == *argptr)
+           value.line = 15;
+         if (s == 0)
+           value.line = default_line - value.line;
+         else
+           value.line = 1;
+         break;
+       }
+
+      while (*p == ' ' || *p == '\t') p++;
+      *argptr = p;
+      if (s == 0)
+       s = default_symtab;
+      value.symtab = s;
+      value.pc = 0;
+      values.sals = (struct symtab_and_line *)malloc (sizeof (struct symtab_and_line));
+      values.sals[0] = value;
+      values.nelts = 1;
+      return values;
+    }
+
+  /* Arg token is not digits => try it as a function name
+     Find the next token (everything up to end or next whitespace).  */
+  p = *argptr;
+  while (*p && *p != ' ' && *p != '\t' && *p != ',') p++;
+  copy = (char *) alloca (p - *argptr + 1);
+  bcopy (*argptr, copy, p - *argptr);
+  copy[p - *argptr] = 0;
+  while (*p == ' ' || *p == '\t') p++;
+  *argptr = p;
+
+  /* Look up that token as a function.
+     If file specified, use that file's per-file block to start with.  */
+
+  if (s == 0)
+    /* use current file as default if none is specified. */
+    s = default_symtab;
+
+  sym = lookup_symbol (copy, s ? BLOCKVECTOR_BLOCK (BLOCKVECTOR (s), 1) : 0,
+                      VAR_NAMESPACE, 0);
+
+  if (sym && SYMBOL_CLASS (sym) == LOC_BLOCK)
+    {
+      /* Arg is the name of a function */
+      pc = BLOCK_START (SYMBOL_BLOCK_VALUE (sym)) + FUNCTION_START_OFFSET;
+      if (funfirstline)
+       SKIP_PROLOGUE (pc);
+      value = find_pc_line (pc, 0);
+#ifdef PROLOGUE_FIRSTLINE_OVERLAP
+      /* Convex: no need to suppress code on first line, if any */
+      value.pc = pc;
+#else
+      value.pc = (value.end && value.pc != pc) ? value.end : pc;
+#endif
+      values.sals = (struct symtab_and_line *)malloc (sizeof (struct symtab_and_line));
+      values.sals[0] = value;
+      values.nelts = 1;
+      return values;
+    }
+
+  if (sym)
+    error ("%s is not a function.", copy);
+
+  if (symtab_list == 0 && partial_symtab_list == 0)
+    error ("No symbol table is loaded.  Use the \"symbol-file\" command.");
+
+  if ((i = lookup_misc_func (copy)) >= 0)
+    {
+      value.symtab = 0;
+      value.line = 0;
+      value.pc = misc_function_vector[i].address + FUNCTION_START_OFFSET;
+      if (funfirstline)
+       SKIP_PROLOGUE (value.pc);
+      values.sals = (struct symtab_and_line *)malloc (sizeof (struct symtab_and_line));
+      values.sals[0] = value;
+      values.nelts = 1;
+      return values;
+    }
+
+  error ("Function %s not defined.", copy);
+}
+
+struct symtabs_and_lines
+decode_line_spec (string, funfirstline)
+     char *string;
+     int funfirstline;
+{
+  struct symtabs_and_lines sals;
+  if (string == 0)
+    error ("Empty line specification.");
+  sals = decode_line_1 (&string, funfirstline,
+                       current_source_symtab, current_source_line);
+  if (*string)
+    error ("Junk at end of line specification: %s", string);
+  return sals;
+}
+
+/* Given a list of NELTS symbols in sym_arr (with corresponding
+   mangled names in physnames), return a list of lines to operate on
+   (ask user if necessary).  */
+struct symtabs_and_lines
+decode_line_2 (argptr, sym_arr, physnames, nelts, funfirstline)
+     char **argptr;
+     struct symbol *sym_arr[];
+     char *physnames[];
+     int nelts;
+     int funfirstline;
+{
+  char *getenv();
+  struct symtabs_and_lines values, return_values;
+  register CORE_ADDR pc;
+  char *args, *arg1, *command_line_input ();
+  int i;
+  char *prompt;
+
+  values.sals = (struct symtab_and_line *) alloca (nelts * sizeof(struct symtab_and_line));
+  return_values.sals = (struct symtab_and_line *) malloc (nelts * sizeof(struct symtab_and_line));
+
+  i = 0;
+  printf("[0] cancel\n[1] all\n");
+  while (i < nelts)
+    {
+      if (sym_arr[i] && SYMBOL_CLASS (sym_arr[i]) == LOC_BLOCK)
+       {
+         /* Arg is the name of a function */
+         pc = BLOCK_START (SYMBOL_BLOCK_VALUE (sym_arr[i])) 
+              + FUNCTION_START_OFFSET;
+         if (funfirstline)
+           SKIP_PROLOGUE (pc);
+         values.sals[i] = find_pc_line (pc, 0);
+         values.sals[i].pc = (values.sals[i].end && values.sals[i].pc != pc) ? values.sals[i].end : pc;
+         printf("[%d] file:%s; line number:%d\n",
+                (i+2), values.sals[i].symtab->filename, values.sals[i].line);
+       }
+      else printf ("?HERE\n");
+      i++;
+    }
+  
+  if ((prompt = getenv ("PS2")) == NULL)
+    {
+      prompt = ">";
+    }
+  printf("%s ",prompt);
+  fflush(stdout);
+
+  args = command_line_input (0, 0);
+  
+  if (args == 0)
+    error_no_arg ("one or more choice numbers");
+
+  i = 0;
+  while (*args)
+    {
+      int num;
+
+      arg1 = args;
+      while (*arg1 >= '0' && *arg1 <= '9') arg1++;
+      if (*arg1 && *arg1 != ' ' && *arg1 != '\t')
+       error ("Arguments must be choice numbers.");
+
+      num = atoi (args);
+
+      if (num == 0)
+       error ("cancelled");
+      else if (num == 1)
+       {
+         bcopy (values.sals, return_values.sals, (nelts * sizeof(struct symtab_and_line)));
+         return_values.nelts = nelts;
+         return return_values;
+       }
+
+      if (num > nelts + 2)
+       {
+         printf ("No choice number %d.\n", num);
+       }
+      else
+       {
+         num -= 2;
+         if (values.sals[num].pc)
+           {
+             return_values.sals[i++] = values.sals[num];
+             values.sals[num].pc = 0;
+           }
+         else
+           {
+             printf ("duplicate request for %d ignored.\n", num);
+           }
+       }
+
+      args = arg1;
+      while (*args == ' ' || *args == '\t') args++;
+    }
+  return_values.nelts = i;
+  return return_values;
+}
+
+/* hash a symbol ("hashpjw" from Aho, Sethi & Ullman, p.436) */
+
+int
+hash_symbol(str)
+       register char *str;
+{
+       register unsigned int h = 0, g;
+       register unsigned char c;
+
+       while (c = *(unsigned char *)str++) {
+               h = (h << 4) + c;
+               if (g = h & 0xf0000000) {
+                       h = h ^ (g >> 24);
+                       h = h ^ g;
+               }
+       }
+       return ((int)h);
+}
+
+/* Return the index of misc function named NAME.  */
+
+int
+lookup_misc_func (name)
+     register char *name;
+{
+  register int i = hash_symbol(name) & (MISC_FUNC_HASH_SIZE - 1);
+
+  if (misc_function_vector == 0)
+         error("No symbol file");
+
+  i = misc_function_hash_tab[i];
+  while (i >= 0)
+    {
+      if (strcmp(misc_function_vector[i].name, name) == 0)
+       break;
+      i = misc_function_vector[i].next;
+    }
+  return (i);
+}
+\f
+/*
+ * Slave routine for sources_info.  Force line breaks at ,'s.
+ */
+static void
+output_source_filename (name, next)
+char *name;
+int next;
+{
+  static int column = 0;
+  
+  if (column != 0 && column + strlen (name) >= 70)
+    {
+      printf_filtered ("\n");
+      column = 0;
+    }
+  else if (column != 0)
+    {
+      printf_filtered (" ");
+      column++;
+    }
+  printf_filtered ("%s", name);
+  column += strlen (name);
+  if (next)
+    {
+      printf_filtered (",");
+      column++;
+    }
+  
+  if (!next) column = 0;
+}  
+
+static void
+sources_info ()
+{
+  register struct symtab *s;
+  register struct partial_symtab *ps;
+  register int column = 0;
+
+  if (symtab_list == 0 && partial_symtab_list == 0)
+    {
+      printf ("No symbol table is loaded.\n");
+      return;
+    }
+  
+  printf_filtered ("Source files for which symbols have been read in:\n\n");
+
+  for (s = symtab_list; s; s = s->next)
+    output_source_filename (s->filename, s->next);
+  printf_filtered ("\n\n");
+  
+  printf_filtered ("Source files for which symbols will be read in on demand:\n\n");
+
+  for (ps = partial_symtab_list; ps; ps = ps->next)
+    if (!ps->readin)
+      output_source_filename (ps->filename, ps->next);
+  printf_filtered ("\n");
+}
+
+/* List all symbols (if REGEXP is 0) or all symbols matching REGEXP.
+   If CLASS is zero, list all symbols except functions and type names.
+   If CLASS is 1, list only functions.
+   If CLASS is 2, list only type names.  */
+
+static void sort_block_syms ();
+
+static void
+list_symbols (regexp, class)
+     char *regexp;
+     int class;
+{
+  register struct symtab *s;
+  register struct partial_symtab *ps;
+  register struct blockvector *bv;
+  struct blockvector *prev_bv = 0;
+  register struct block *b;
+  register int i, j;
+  register struct symbol *sym;
+  struct partial_symbol *psym, *bound;
+  char *val;
+  static char *classnames[]
+    = {"variable", "function", "type", "method"};
+  int print_count = 0;
+  int found_in_file = 0;
+
+  if (regexp)
+    if (val = (char *) re_comp (regexp))
+      error ("Invalid regexp: %s", val);
+
+  /* Search through the partial_symtab_list *first* for all symbols
+     matching the regexp.  That way we don't have to reproduce all of
+     the machinery below. */
+  for (psym = global_psymbols.list, bound = global_psymbols.next; ;
+       psym = static_psymbols.list, bound = static_psymbols.next)
+    {
+      for (; psym < bound; ++psym)
+       {
+         if (psym->pst->readin)
+           continue;
+
+         QUIT;
+         /* If it would match (logic taken from loop below)
+            load the file and go on to the next one */
+         if ((regexp == 0 || re_exec (SYMBOL_NAME (psym)))
+             && ((class == 0 && SYMBOL_CLASS (psym) != LOC_TYPEDEF
+                  && SYMBOL_CLASS (psym) != LOC_BLOCK)
+                 || (class == 1 && SYMBOL_CLASS (psym) == LOC_BLOCK)
+                 || (class == 2 && SYMBOL_CLASS (psym) == LOC_TYPEDEF)
+                 || (class == 3 && SYMBOL_CLASS (psym) == LOC_BLOCK)))
+           psymtab_to_symtab(psym->pst);
+       }
+      if (psym == static_psymbols.next)
+       break;
+    }
+
+  /* Printout here so as to get after the "Reading in symbols"
+     messages which will be generated above.  */
+  printf_filtered (regexp
+         ? "All %ss matching regular expression \"%s\":\n"
+         : "All defined %ss:\n",
+         classnames[class],
+         regexp);
+
+  /* Here, *if* the class is correct (function only, right now), we
+     should search through the misc function vector for symbols that
+     match and call find_pc_psymtab on them.  If find_pc_psymtab returns
+     0, don't worry about it (already read in or no debugging info).  */
+
+  if (class == 1)
+    {
+      for (i = 0; i < misc_function_count; i++)
+       if (regexp == 0 || re_exec (misc_function_vector[i].name))
+         {
+           ps = find_pc_psymtab (misc_function_vector[i].address);
+           if (ps && !ps->readin)
+             psymtab_to_symtab (ps);
+         }
+    }
+
+  for (s = symtab_list; s; s = s->next)
+    {
+      found_in_file = 0;
+      bv = BLOCKVECTOR (s);
+      /* Often many files share a blockvector.
+        Scan each blockvector only once so that
+        we don't get every symbol many times.
+        It happens that the first symtab in the list
+        for any given blockvector is the main file.  */
+      if (bv != prev_bv)
+       for (i = 0; i < 2; i++)
+         {
+           b = BLOCKVECTOR_BLOCK (bv, i);
+           /* Skip the sort if this block is always sorted.  */
+           if (!BLOCK_SHOULD_SORT (b))
+             sort_block_syms (b);
+           for (j = 0; j < BLOCK_NSYMS (b); j++)
+             {
+               QUIT;
+               sym = BLOCK_SYM (b, j);
+               if ((regexp == 0 || re_exec (SYMBOL_NAME (sym)))
+                   && ((class == 0 && SYMBOL_CLASS (sym) != LOC_TYPEDEF
+                        && SYMBOL_CLASS (sym) != LOC_BLOCK)
+                       || (class == 1 && SYMBOL_CLASS (sym) == LOC_BLOCK)
+                       || (class == 2 && SYMBOL_CLASS (sym) == LOC_TYPEDEF)
+                       || (class == 3 && SYMBOL_CLASS (sym) == LOC_BLOCK)))
+                 {
+                   if (!found_in_file)
+                     {
+                       printf_filtered ("\nFile %s:\n", s->filename);
+                       print_count += 2;
+                     }
+                   found_in_file = 1;
+                   if (class != 2 && i == 1)
+                     printf_filtered ("static ");
+                   if (class == 2
+                       && SYMBOL_NAMESPACE (sym) != STRUCT_NAMESPACE)
+                     printf_filtered ("typedef ");
+
+                   if (class < 3)
+                     {
+                       type_print (SYMBOL_TYPE (sym),
+                                   (SYMBOL_CLASS (sym) == LOC_TYPEDEF
+                                    ? "" : SYMBOL_NAME (sym)),
+                                   stdout, 0);
+
+                       if (class == 2
+                           && SYMBOL_NAMESPACE (sym) != STRUCT_NAMESPACE
+                           && (TYPE_NAME ((SYMBOL_TYPE (sym))) == 0
+                               || 0 != strcmp (TYPE_NAME ((SYMBOL_TYPE (sym))),
+                                               SYMBOL_NAME (sym))))
+                         printf_filtered (" %s", SYMBOL_NAME (sym));
+
+                       printf_filtered (";\n");
+                     }
+                   else
+                     {
+# if 0
+                       char buf[1024];
+                       type_print_base (TYPE_FN_FIELD_TYPE(t, i), stdout, 0, 0); 
+                       type_print_varspec_prefix (TYPE_FN_FIELD_TYPE(t, i), stdout, 0); 
+                       sprintf (buf, " %s::", TYPE_NAME (t));
+                       type_print_method_args (TYPE_FN_FIELD_ARGS (t, i), buf, name, stdout);
+# endif
+                     }
+                 }
+             }
+         }
+      prev_bv = bv;
+    }
+}
+
+static void
+variables_info (regexp)
+     char *regexp;
+{
+  list_symbols (regexp, 0);
+}
+
+static void
+functions_info (regexp)
+     char *regexp;
+{
+  list_symbols (regexp, 1);
+}
+
+static void
+types_info (regexp)
+     char *regexp;
+{
+  list_symbols (regexp, 2);
+}
+
+#if 0
+/* Tiemann says: "info methods was never implemented."  */
+static void
+methods_info (regexp)
+     char *regexp;
+{
+  list_symbols (regexp, 3);
+}
+#endif /* 0 */
+\f
+/* Call sort_block_syms to sort alphabetically the symbols of one block.  */
+
+static int
+compare_symbols (s1, s2)
+     struct symbol **s1, **s2;
+{
+  /* Names that are less should come first.  */
+  register int namediff = strcmp (SYMBOL_NAME (*s1), SYMBOL_NAME (*s2));
+  if (namediff != 0) return namediff;
+  /* For symbols of the same name, registers should come first.  */
+  return ((SYMBOL_CLASS (*s2) == LOC_REGISTER)
+         - (SYMBOL_CLASS (*s1) == LOC_REGISTER));
+}
+
+static void
+sort_block_syms (b)
+     register struct block *b;
+{
+  qsort (&BLOCK_SYM (b, 0), BLOCK_NSYMS (b),
+        sizeof (struct symbol *), compare_symbols);
+}
+\f
+/* Initialize the standard C scalar types.  */
+
+static
+struct type *
+init_type (code, length, uns, name)
+     enum type_code code;
+     int length, uns;
+     char *name;
+{
+  register struct type *type;
+
+  type = (struct type *) xmalloc (sizeof (struct type));
+  bzero (type, sizeof *type);
+  TYPE_MAIN_VARIANT (type) = type;
+  TYPE_CODE (type) = code;
+  TYPE_LENGTH (type) = length;
+  TYPE_FLAGS (type) = uns ? TYPE_FLAG_UNSIGNED : 0;
+  TYPE_FLAGS (type) |= TYPE_FLAG_PERM;
+  TYPE_NFIELDS (type) = 0;
+  TYPE_NAME (type) = name;
+
+  /* C++ fancies.  */
+  TYPE_NFN_FIELDS (type) = 0;
+  TYPE_N_BASECLASSES (type) = 0;
+  TYPE_BASECLASSES (type) = 0;
+  return type;
+}
+
+/* Return Nonzero if block a is lexically nested within block b,
+   or if a and b have the same pc range.
+   Return zero otherwise. */
+int
+contained_in (a, b)
+     struct block *a, *b;
+{
+  if (!a || !b)
+    return 0;
+  return a->startaddr >= b->startaddr && a->endaddr <= b->endaddr;
+}
+
+\f
+/* Helper routine for make_symbol_completion_list.  */
+
+int return_val_size, return_val_index;
+char **return_val;
+
+void
+completion_list_add_symbol (symname)
+     char *symname;
+{
+  if (return_val_index + 3 > return_val_size)
+    return_val =
+      (char **)xrealloc (return_val,
+                        (return_val_size *= 2) * sizeof (char *));
+  
+  return_val[return_val_index] =
+    (char *)xmalloc (1 + strlen (symname));
+  
+  strcpy (return_val[return_val_index], symname);
+  
+  return_val[++return_val_index] = (char *)NULL;
+}
+
+/* Return a NULL terminated array of all symbols (regardless of class) which
+   begin by matching TEXT.  If the answer is no symbols, then the return value
+   is an array which contains only a NULL pointer.
+
+   Problem: All of the symbols have to be copied because readline
+   frees them.  I'm not going to worry about this; hopefully there
+   won't be that many.  */
+
+char **
+make_symbol_completion_list (text)
+  char *text;
+{
+  register struct symtab *s;
+  register struct partial_symtab *ps;
+  register struct blockvector *bv;
+  struct blockvector *prev_bv = 0;
+  register struct block *b, *surrounding_static_block;
+  extern struct block *get_selected_block ();
+  register int i, j;
+  register struct symbol *sym;
+  struct partial_symbol *psym;
+
+  int text_len = strlen (text);
+  return_val_size = 100;
+  return_val_index = 0;
+  return_val =
+    (char **)xmalloc ((1 + return_val_size) *sizeof (char *));
+  return_val[0] = (char *)NULL;
+
+  /* Look through the partial symtabs for all symbols which begin
+     by matching TEXT.  Add each one that you find to the list.  */
+
+  for (ps = partial_symtab_list; ps; ps = ps->next)
+    {
+      /* If the psymtab's been read in we'll get it when we search
+        through the blockvector.  */
+      if (ps->readin) continue;
+
+      for (psym = global_psymbols.list + ps->globals_offset;
+          psym < (global_psymbols.list + ps->globals_offset
+                  + ps->n_global_syms);
+          psym++)
+       {
+         QUIT;                 /* If interrupted, then quit. */
+         if ((strncmp (SYMBOL_NAME (psym), text, text_len) == 0))
+           completion_list_add_symbol (SYMBOL_NAME (psym));
+       }
+      
+      for (psym = static_psymbols.list + ps->statics_offset;
+          psym < (static_psymbols.list + ps->statics_offset
+                  + ps->n_static_syms);
+          psym++)
+       {
+         QUIT;
+         if ((strncmp (SYMBOL_NAME (psym), text, text_len) == 0))
+           completion_list_add_symbol (SYMBOL_NAME (psym));
+       }
+    }
+
+  /* At this point scan through the misc function vector and add each
+     symbol you find to the list.  Eventually we want to ignore
+      anything that isn't a text symbol (everything else will be
+      handled by the psymtab code above).  */
+
+  for (i = 0; i < misc_function_count; i++)
+    if (!strncmp (text, misc_function_vector[i].name, text_len))
+      completion_list_add_symbol (misc_function_vector[i].name);
+
+  /* Search upwards from currently selected frame (so that we can
+     complete on local vars.  */
+  for (b = get_selected_block (); b; b = BLOCK_SUPERBLOCK (b))
+    {
+      if (!BLOCK_SUPERBLOCK (b))
+       surrounding_static_block = b; /* For elmin of dups */
+      
+      /* Also catch fields of types defined in this places which
+        match our text string.  Only complete on types visible
+        from current context.  */
+      for (i = 0; i < BLOCK_NSYMS (b); i++)
+       {
+         register struct symbol *sym = BLOCK_SYM (b, i);
+         
+         if (!strncmp (SYMBOL_NAME (sym), text, text_len))
+           completion_list_add_symbol (SYMBOL_NAME (sym));
+
+         if (SYMBOL_CLASS (sym) == LOC_TYPEDEF)
+           {
+             struct type *t = SYMBOL_TYPE (sym);
+             enum type_code c = TYPE_CODE (t);
+
+             if (c == TYPE_CODE_UNION || c == TYPE_CODE_STRUCT)
+               for (j = 0; j < TYPE_NFIELDS (t); j++)
+                 if (TYPE_FIELD_NAME (t, j) &&
+                     !strncmp (TYPE_FIELD_NAME (t, j), text, text_len))
+                   completion_list_add_symbol (TYPE_FIELD_NAME (t, j));
+           }
+       }
+    }
+
+  /* Go through the symtabs and check the externs and statics for
+     symbols which match.  */
+
+  for (s = symtab_list; s; s = s->next)
+    {
+      struct block *b = BLOCKVECTOR_BLOCK (BLOCKVECTOR (s), 0);
+      
+      for (i = 0; i < BLOCK_NSYMS (b); i++)
+       if (!strncmp (SYMBOL_NAME (BLOCK_SYM (b, i)), text, text_len))
+         completion_list_add_symbol (SYMBOL_NAME (BLOCK_SYM (b, i)));
+    }
+
+  for (s = symtab_list; s; s = s->next)
+    {
+      struct block *b = BLOCKVECTOR_BLOCK (BLOCKVECTOR (s), 1);
+
+      /* Don't do this block twice.  */
+      if (b == surrounding_static_block) continue;
+      
+      for (i = 0; i < BLOCK_NSYMS (b); i++)
+       if (!strncmp (SYMBOL_NAME (BLOCK_SYM (b, i)), text, text_len))
+         completion_list_add_symbol (SYMBOL_NAME (BLOCK_SYM (b, i)));
+    }
+
+  return (return_val);
+}
+\f
+void
+_initialize_symtab ()
+{
+  add_info ("variables", variables_info,
+           "All global and static variable names, or those matching REGEXP.");
+  add_info ("functions", functions_info,
+           "All function names, or those matching REGEXP.");
+  add_info ("types", types_info,
+           "All types names, or those matching REGEXP.");
+#if 0
+  add_info ("methods", methods_info,
+           "All method names, or those matching REGEXP::REGEXP.\n\
+If the class qualifier is ommited, it is assumed to be the current scope.\n\
+If the first REGEXP is ommited, then all methods matching the second REGEXP\n\
+are listed.");
+#endif
+  add_info ("sources", sources_info,
+           "Source files in the program.");
+
+  obstack_init (symbol_obstack);
+  obstack_init (psymbol_obstack);
+
+  builtin_type_void = init_type (TYPE_CODE_VOID, 1, 0, "void");
+
+  builtin_type_float = init_type (TYPE_CODE_FLT, sizeof (float), 0, "float");
+  builtin_type_double = init_type (TYPE_CODE_FLT, sizeof (double), 0, "double");
+
+  builtin_type_char = init_type (TYPE_CODE_INT, sizeof (char), 0, "char");
+  builtin_type_short = init_type (TYPE_CODE_INT, sizeof (short), 0, "short");
+  builtin_type_long = init_type (TYPE_CODE_INT, sizeof (long), 0, "long");
+  builtin_type_int = init_type (TYPE_CODE_INT, sizeof (int), 0, "int");
+
+  builtin_type_unsigned_char = init_type (TYPE_CODE_INT, sizeof (char), 1, "unsigned char");
+  builtin_type_unsigned_short = init_type (TYPE_CODE_INT, sizeof (short), 1, "unsigned short");
+  builtin_type_unsigned_long = init_type (TYPE_CODE_INT, sizeof (long), 1, "unsigned long");
+  builtin_type_unsigned_int = init_type (TYPE_CODE_INT, sizeof (int), 1, "unsigned int");
+#ifdef LONG_LONG
+  builtin_type_long_long =
+    init_type (TYPE_CODE_INT, sizeof (long long), 0, "long long");
+  builtin_type_unsigned_long_long = 
+    init_type (TYPE_CODE_INT, sizeof (long long), 1, "unsigned long long");
+#endif
+}
+
diff --git a/usr/src/usr.bin/gdb/utils.c b/usr/src/usr.bin/gdb/utils.c
new file mode 100644 (file)
index 0000000..e0eca92
--- /dev/null
@@ -0,0 +1,1096 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory.
+ *
+ * $Header: utils.c,v 1.6 91/03/07 17:44:30 mccanne Exp $;
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)utils.c    6.4 (Berkeley) 5/8/91";
+#endif /* not lint */
+
+/* General utility routines for GDB, the GNU debugger.
+   Copyright (C) 1986, 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING.  If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#include "param.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <sys/param.h>
+#include <pwd.h>
+#include "defs.h"
+#ifdef HAVE_TERMIO
+#include <termio.h>
+#endif
+
+/* If this definition isn't overridden by the header files, assume
+   that isatty and fileno exist on this system.  */
+#ifndef ISATTY
+#define ISATTY(FP)     (isatty (fileno (FP)))
+#endif
+
+extern FILE *instream;
+
+void error ();
+void fatal ();
+
+/* Chain of cleanup actions established with make_cleanup,
+   to be executed if an error happens.  */
+
+static struct cleanup *cleanup_chain;
+
+/* Nonzero means a quit has been requested.  */
+
+int quit_flag;
+
+/* Nonzero means quit immediately if Control-C is typed now,
+   rather than waiting until QUIT is executed.  */
+
+int immediate_quit;
+\f
+/* Add a new cleanup to the cleanup_chain,
+   and return the previous chain pointer
+   to be passed later to do_cleanups or discard_cleanups.
+   Args are FUNCTION to clean up with, and ARG to pass to it.  */
+
+struct cleanup *
+make_cleanup (function, arg)
+     void (*function) ();
+     int arg;
+{
+  register struct cleanup *new
+    = (struct cleanup *) xmalloc (sizeof (struct cleanup));
+  register struct cleanup *old_chain = cleanup_chain;
+
+  new->next = cleanup_chain;
+  new->function = function;
+  new->arg = arg;
+  cleanup_chain = new;
+
+  return old_chain;
+}
+
+/* Discard cleanups and do the actions they describe
+   until we get back to the point OLD_CHAIN in the cleanup_chain.  */
+
+void
+do_cleanups (old_chain)
+     register struct cleanup *old_chain;
+{
+  register struct cleanup *ptr;
+  while ((ptr = cleanup_chain) != old_chain)
+    {
+      (*ptr->function) (ptr->arg);
+      cleanup_chain = ptr->next;
+      free (ptr);
+    }
+}
+
+/* Discard cleanups, not doing the actions they describe,
+   until we get back to the point OLD_CHAIN in the cleanup_chain.  */
+
+void
+discard_cleanups (old_chain)
+     register struct cleanup *old_chain;
+{
+  register struct cleanup *ptr;
+  while ((ptr = cleanup_chain) != old_chain)
+    {
+      cleanup_chain = ptr->next;
+      free (ptr);
+    }
+}
+
+/* Set the cleanup_chain to 0, and return the old cleanup chain.  */
+struct cleanup *
+save_cleanups ()
+{
+  struct cleanup *old_chain = cleanup_chain;
+
+  cleanup_chain = 0;
+  return old_chain;
+}
+
+/* Restore the cleanup chain from a previously saved chain.  */
+void
+restore_cleanups (chain)
+     struct cleanup *chain;
+{
+  cleanup_chain = chain;
+}
+
+/* This function is useful for cleanups.
+   Do
+
+     foo = xmalloc (...);
+     old_chain = make_cleanup (free_current_contents, &foo);
+
+   to arrange to free the object thus allocated.  */
+
+void
+free_current_contents (location)
+     char **location;
+{
+  free (*location);
+}
+\f
+/* Generally useful subroutines used throughout the program.  */
+
+/* Like malloc but get error if no storage available.  */
+
+char *
+xmalloc (size)
+     long size;
+{
+  register char *val = (char *) malloc (size);
+  if (!val)
+    fatal ("virtual memory exhausted.", 0);
+  return val;
+}
+
+/* Like realloc but get error if no storage available.  */
+
+char *
+xrealloc (ptr, size)
+     char *ptr;
+     long size;
+{
+  register char *val = (char *) realloc (ptr, size);
+  if (!val)
+    fatal ("virtual memory exhausted.", 0);
+  return val;
+}
+
+/* Print the system error message for errno, and also mention STRING
+   as the file name for which the error was encountered.
+   Then return to command level.  */
+
+void
+perror_with_name (string)
+     char *string;
+{
+  extern int sys_nerr;
+  extern char *sys_errlist[];
+  extern int errno;
+  char *err;
+  char *combined;
+
+  if (errno < sys_nerr)
+    err = sys_errlist[errno];
+  else
+    err = "unknown error";
+
+  combined = (char *) alloca (strlen (err) + strlen (string) + 3);
+  strcpy (combined, string);
+  strcat (combined, ": ");
+  strcat (combined, err);
+
+  error ("%s.", combined);
+}
+
+/* Print the system error message for ERRCODE, and also mention STRING
+   as the file name for which the error was encountered.  */
+
+void
+print_sys_errmsg (string, errcode)
+     char *string;
+     int errcode;
+{
+  extern int sys_nerr;
+  extern char *sys_errlist[];
+  char *err;
+  char *combined;
+
+  if (errcode < sys_nerr)
+    err = sys_errlist[errcode];
+  else
+    err = "unknown error";
+
+  combined = (char *) alloca (strlen (err) + strlen (string) + 3);
+  strcpy (combined, string);
+  strcat (combined, ": ");
+  strcat (combined, err);
+
+  printf ("%s.\n", combined);
+}
+
+void
+quit ()
+{
+#ifdef HAVE_TERMIO
+  ioctl (fileno (stdout), TCFLSH, 1);
+#else /* not HAVE_TERMIO */
+  ioctl (fileno (stdout), TIOCFLUSH, 0);
+#endif /* not HAVE_TERMIO */
+#ifdef TIOCGPGRP
+  error ("Quit");
+#else
+  error ("Quit (expect signal %d when inferior is resumed)", SIGINT);
+#endif /* TIOCGPGRP */
+}
+
+/* Control C comes here */
+
+void
+request_quit ()
+{
+  extern int remote_debugging;
+
+  quit_flag = 1;
+
+#ifdef USG
+  /* Restore the signal handler.  */
+  signal (SIGINT, request_quit);
+#endif
+
+  if (immediate_quit)
+         quit();
+}
+
+/* Print an error message and return to command level.
+   STRING is the error message, used as a fprintf string,
+   and ARG is passed as an argument to it.  */
+
+void
+error (string, arg1, arg2, arg3)
+     char *string;
+     int arg1, arg2, arg3;
+{
+  terminal_ours ();            /* Should be ok even if no inf.  */
+  fflush (stdout);
+  fprintf (stderr, string, arg1, arg2, arg3);
+  fprintf (stderr, "\n");
+  return_to_top_level ();
+}
+
+/* Print an error message and exit reporting failure.
+   This is for a error that we cannot continue from.
+   STRING and ARG are passed to fprintf.  */
+
+void
+fatal (string, arg)
+     char *string;
+     int arg;
+{
+  fprintf (stderr, "gdb: ");
+  fprintf (stderr, string, arg);
+  fprintf (stderr, "\n");
+  exit (1);
+}
+
+/* Print an error message and exit, dumping core.
+   STRING is a printf-style control string, and ARG is a corresponding
+   argument.  */
+void
+fatal_dump_core (string, arg)
+     char *string;
+     int arg;
+{
+  /* "internal error" is always correct, since GDB should never dump
+     core, no matter what the input.  */
+  fprintf (stderr, "gdb internal error: ");
+  fprintf (stderr, string, arg);
+  fprintf (stderr, "\n");
+  signal (SIGQUIT, SIG_DFL);
+  kill (getpid (), SIGQUIT);
+  /* We should never get here, but just in case...  */
+  exit (1);
+}
+
+/* Make a copy of the string at PTR with SIZE characters
+   (and add a null character at the end in the copy).
+   Uses malloc to get the space.  Returns the address of the copy.  */
+
+char *
+savestring (ptr, size)
+     char *ptr;
+     int size;
+{
+  register char *p = (char *) xmalloc (size + 1);
+  bcopy (ptr, p, size);
+  p[size] = 0;
+  return p;
+}
+
+char *
+concat (s1, s2, s3)
+     char *s1, *s2, *s3;
+{
+  register int len = strlen (s1) + strlen (s2) + strlen (s3) + 1;
+  register char *val = (char *) xmalloc (len);
+  strcpy (val, s1);
+  strcat (val, s2);
+  strcat (val, s3);
+  return val;
+}
+
+void
+print_spaces (n, file)
+     register int n;
+     register FILE *file;
+{
+  while (n-- > 0)
+    fputc (' ', file);
+}
+
+/* Ask user a y-or-n question and return 1 iff answer is yes.
+   Takes three args which are given to printf to print the question.
+   The first, a control string, should end in "? ".
+   It should not say how to answer, because we do that.  */
+
+int
+query (ctlstr, arg1, arg2)
+     char *ctlstr;
+{
+  register int answer;
+
+  /* Automatically answer "yes" if input is not from a terminal.  */
+  if (!input_from_terminal_p ())
+    return 1;
+
+  while (1)
+    {
+      printf (ctlstr, arg1, arg2);
+      printf ("(y or n) ");
+      fflush (stdout);
+      answer = fgetc (stdin);
+      clearerr (stdin);                /* in case of C-d */
+      if (answer != '\n')
+       while (fgetc (stdin) != '\n') clearerr (stdin);
+      if (answer >= 'a')
+       answer -= 040;
+      if (answer == 'Y')
+       return 1;
+      if (answer == 'N')
+       return 0;
+      printf ("Please answer y or n.\n");
+    }
+}
+\f
+/* Parse a C escape sequence.  STRING_PTR points to a variable
+   containing a pointer to the string to parse.  That pointer
+   is updated past the characters we use.  The value of the
+   escape sequence is returned.
+
+   A negative value means the sequence \ newline was seen,
+   which is supposed to be equivalent to nothing at all.
+
+   If \ is followed by a null character, we return a negative
+   value and leave the string pointer pointing at the null character.
+
+   If \ is followed by 000, we return 0 and leave the string pointer
+   after the zeros.  A value of 0 does not mean end of string.  */
+
+int
+parse_escape (string_ptr)
+     char **string_ptr;
+{
+  register int c = *(*string_ptr)++;
+  switch (c)
+    {
+    case 'a':
+      return '\a';
+    case 'b':
+      return '\b';
+    case 'e':
+      return 033;
+    case 'f':
+      return '\f';
+    case 'n':
+      return '\n';
+    case 'r':
+      return '\r';
+    case 't':
+      return '\t';
+    case 'v':
+      return '\v';
+    case '\n':
+      return -2;
+    case 0:
+      (*string_ptr)--;
+      return 0;
+    case '^':
+      c = *(*string_ptr)++;
+      if (c == '\\')
+       c = parse_escape (string_ptr);
+      if (c == '?')
+       return 0177;
+      return (c & 0200) | (c & 037);
+      
+    case '0':
+    case '1':
+    case '2':
+    case '3':
+    case '4':
+    case '5':
+    case '6':
+    case '7':
+      {
+       register int i = c - '0';
+       register int count = 0;
+       while (++count < 3)
+         {
+           if ((c = *(*string_ptr)++) >= '0' && c <= '7')
+             {
+               i *= 8;
+               i += c - '0';
+             }
+           else
+             {
+               (*string_ptr)--;
+               break;
+             }
+         }
+       return i;
+      }
+    default:
+      return c;
+    }
+}
+\f
+/* Print the character CH on STREAM as part of the contents
+   of a literal string whose delimiter is QUOTER.  */
+
+void
+printchar (ch, stream, quoter)
+     unsigned char ch;
+     FILE *stream;
+     int quoter;
+{
+  register int c = ch;
+  if (c < 040 || c >= 0177)
+    switch (c)
+      {
+      case '\n':
+       fputs_filtered ("\\n", stream);
+       break;
+      case '\b':
+       fputs_filtered ("\\b", stream);
+       break;
+      case '\t':
+       fputs_filtered ("\\t", stream);
+       break;
+      case '\f':
+       fputs_filtered ("\\f", stream);
+       break;
+      case '\r':
+       fputs_filtered ("\\r", stream);
+       break;
+      case '\033':
+       fputs_filtered ("\\e", stream);
+       break;
+      case '\007':
+       fputs_filtered ("\\a", stream);
+       break;
+      default:
+       fprintf_filtered (stream, "\\%.3o", (unsigned int) c);
+       break;
+      }
+  else
+    {
+      if (c == '\\' || c == quoter)
+       fputs_filtered ("\\", stream);
+      fprintf_filtered (stream, "%c", c);
+    }
+}
+\f
+static int lines_per_page, lines_printed, chars_per_line, chars_printed;
+
+/* Set values of page and line size.  */
+static void
+set_screensize_command (arg, from_tty)
+     char *arg;
+     int from_tty;
+{
+  char *p = arg;
+  char *p1;
+  int tolinesize = lines_per_page;
+  int tocharsize = chars_per_line;
+
+  if (p == 0)
+    error_no_arg ("set screensize");
+
+  while (*p >= '0' && *p <= '9')
+    p++;
+
+  if (*p && *p != ' ' && *p != '\t')
+    error ("Non-integral argument given to \"set screensize\".");
+
+  tolinesize = atoi (arg);
+
+  while (*p == ' ' || *p == '\t')
+    p++;
+
+  if (*p)
+    {
+      p1 = p;
+      while (*p1 >= '0' && *p1 <= '9')
+       p1++;
+
+      if (*p1)
+       error ("Non-integral second argument given to \"set screensize\".");
+
+      tocharsize = atoi (p);
+    }
+
+  lines_per_page = tolinesize;
+  chars_per_line = tocharsize;
+}
+
+static void
+instream_cleanup(stream)
+    FILE *stream;
+{
+  instream = stream;
+}
+
+static void
+prompt_for_continue ()
+{
+  if (ISATTY(stdin) && ISATTY(stdout))
+    {
+      struct cleanup *old_chain = make_cleanup(instream_cleanup, instream);
+      char *cp, *gdb_readline();
+
+      instream = stdin;
+      immediate_quit++;
+      if (cp = gdb_readline ("---Type <return> to continue---"))
+       free(cp);
+      chars_printed = lines_printed = 0;
+      immediate_quit--;
+      do_cleanups(old_chain);
+    }
+}
+
+/* Reinitialize filter; ie. tell it to reset to original values.  */
+
+void
+reinitialize_more_filter ()
+{
+  lines_printed = 0;
+  chars_printed = 0;
+}
+
+static void
+screensize_info (arg, from_tty)
+     char *arg;
+     int from_tty;
+{
+  if (arg)
+    error ("\"info screensize\" does not take any arguments.");
+  
+  if (!lines_per_page)
+    printf ("Output more filtering is disabled.\n");
+  else
+    {
+      printf ("Output more filtering is enabled with\n");
+      printf ("%d lines per page and %d characters per line.\n",
+             lines_per_page, chars_per_line);
+    }
+}
+
+/* Like fputs but pause after every screenful.
+   Unlike fputs, fputs_filtered does not return a value.
+   It is OK for LINEBUFFER to be NULL, in which case just don't print
+   anything.
+
+   Note that a longjmp to top level may occur in this routine
+   (since prompt_for_continue may do so) so this routine should not be
+   called when cleanups are not in place.  */
+
+void
+fputs_filtered (linebuffer, stream)
+     char *linebuffer;
+     FILE *stream;
+{
+  char *lineptr;
+
+  if (linebuffer == 0)
+    return;
+  
+  /* Don't do any filtering if it is disabled.  */
+  if (stream != stdout || !ISATTY(stdout) || lines_per_page == 0)
+    {
+      fputs (linebuffer, stream);
+      return;
+    }
+
+  /* Go through and output each character.  Show line extension
+     when this is necessary; prompt user for new page when this is
+     necessary.  */
+  
+  lineptr = linebuffer;
+  while (*lineptr)
+    {
+      /* Possible new page.  */
+      if (lines_printed >= lines_per_page - 1)
+       prompt_for_continue ();
+
+      while (*lineptr && *lineptr != '\n')
+       {
+         /* Print a single line.  */
+         if (*lineptr == '\t')
+           {
+             putc ('\t', stream);
+             /* Shifting right by 3 produces the number of tab stops
+                we have already passed, and then adding one and
+                shifting left 3 advances to the next tab stop.  */
+             chars_printed = ((chars_printed >> 3) + 1) << 3;
+             lineptr++;
+           }
+         else
+           {
+             putc (*lineptr, stream);
+             chars_printed++;
+             lineptr++;
+           }
+      
+         if (chars_printed >= chars_per_line)
+           {
+             chars_printed = 0;
+             lines_printed++;
+             /* Possible new page.  */
+             if (lines_printed >= lines_per_page - 1)
+               prompt_for_continue ();
+           }
+       }
+
+      if (*lineptr == '\n')
+       {
+         lines_printed++;
+         putc ('\n', stream);
+         lineptr++;
+         chars_printed = 0;
+       }
+    }
+}
+
+/* fputs_demangled is a variant of fputs_filtered that
+   demangles g++ names.*/
+
+void
+fputs_demangled (linebuffer, stream, arg_mode)
+     char *linebuffer;
+     FILE *stream;
+{
+#ifdef __STDC__
+  extern char *cplus_demangle (const char *, int);
+#else
+  extern char *cplus_demangle ();
+#endif
+#define SYMBOL_MAX 1024
+
+#define SYMBOL_CHAR(c) (isascii(c) && (isalnum(c) || (c) == '_' || (c) == '$'))
+
+  char buf[SYMBOL_MAX+1];
+  char *p;
+
+  if (linebuffer == NULL)
+    return;
+
+  p = linebuffer;
+
+  while ( *p != (char) 0 ) {
+    int i = 0;
+
+    /* collect non-interesting characters into buf */
+    while ( *p != (char) 0 && !SYMBOL_CHAR(*p) ) {
+      buf[i++] = *p;
+      p++;
+    }
+    if (i > 0) {
+      /* output the non-interesting characters without demangling */
+      buf[i] = (char) 0;
+      fputs_filtered(buf, stream);
+      i = 0;  /* reset buf */
+    }
+
+    /* and now the interesting characters */
+    while (i < SYMBOL_MAX && *p != (char) 0 && SYMBOL_CHAR(*p) ) {
+      buf[i++] = *p;
+      p++;
+    }
+    buf[i] = (char) 0;
+    if (i > 0) {
+      char * result;
+      
+      if ( (result = cplus_demangle(buf, arg_mode)) != NULL ) {
+       fputs_filtered(result, stream);
+       free(result);
+      }
+      else {
+       fputs_filtered(buf, stream);
+      }
+    }
+  }
+}
+
+/* Print ARG1, ARG2, and ARG3 on stdout using format FORMAT.  If this
+   information is going to put the amount written since the last call
+   to INIIALIZE_MORE_FILTER or the last page break over the page size,
+   print out a pause message and do a gdb_readline to get the users
+   permision to continue.
+
+   Unlike fprintf, this function does not return a value.
+
+   Note that this routine has a restriction that the length of the
+   final output line must be less than 255 characters *or* it must be
+   less than twice the size of the format string.  This is a very
+   arbitrary restriction, but it is an internal restriction, so I'll
+   put it in.  This means that the %s format specifier is almost
+   useless; unless the caller can GUARANTEE that the string is short
+   enough, fputs_filtered should be used instead.
+
+   Note also that a longjmp to top level may occur in this routine
+   (since prompt_for_continue may do so) so this routine should not be
+   called when cleanups are not in place.  */
+
+void
+fprintf_filtered (stream, format, arg1, arg2, arg3, arg4, arg5, arg6)
+     FILE *stream;
+     char *format;
+     int arg1, arg2, arg3, arg4, arg5, arg6;
+{
+  static char *linebuffer = (char *) 0;
+  static int line_size;
+  int format_length = strlen (format);
+  int numchars;
+
+  /* Allocated linebuffer for the first time.  */
+  if (!linebuffer)
+    {
+      linebuffer = (char *) xmalloc (255);
+      line_size = 255;
+    }
+
+  /* Reallocate buffer to a larger size if this is necessary.  */
+  if (format_length * 2 > line_size)
+    {
+      line_size = format_length * 2;
+
+      /* You don't have to copy.  */
+      free (linebuffer);
+      linebuffer = (char *) xmalloc (line_size);
+    }
+
+  /* This won't blow up if the restrictions described above are
+     followed.   */
+  (void) sprintf (linebuffer, format, arg1, arg2, arg3, arg4, arg5, arg6);
+
+  fputs_filtered (linebuffer, stream);
+}
+
+void
+printf_filtered (format, arg1, arg2, arg3, arg4, arg5, arg6)
+     char *format;
+     int arg1, arg2, arg3, arg4, arg5, arg6;
+{
+  fprintf_filtered (stdout, format, arg1, arg2, arg3, arg4, arg5, arg6);
+}
+
+/* Print N spaces.  */
+void
+print_spaces_filtered (n, stream)
+     int n;
+     FILE *stream;
+{
+  register char *s = (char *) alloca (n + 1);
+  register char *t = s;
+
+  while (n--)
+    *t++ = ' ';
+  *t = '\0';
+
+  fputs_filtered (s, stream);
+}
+
+\f
+#ifdef USG
+bcopy (from, to, count)
+char *from, *to;
+{
+       memcpy (to, from, count);
+}
+
+bcmp (from, to, count)
+{
+       return (memcmp (to, from, count));
+}
+
+bzero (to, count)
+char *to;
+{
+       while (count--)
+               *to++ = 0;
+}
+
+getwd (buf)
+char *buf;
+{
+  getcwd (buf, MAXPATHLEN);
+}
+
+char *
+index (s, c)
+     char *s;
+{
+  char *strchr ();
+  return strchr (s, c);
+}
+
+char *
+rindex (s, c)
+     char *s;
+{
+  char *strrchr ();
+  return strrchr (s, c);
+}
+
+#ifndef USG
+char *sys_siglist[32] = {
+       "SIG0",
+       "SIGHUP",
+       "SIGINT",
+       "SIGQUIT",
+       "SIGILL",
+       "SIGTRAP",
+       "SIGIOT",
+       "SIGEMT",
+       "SIGFPE",
+       "SIGKILL",
+       "SIGBUS",
+       "SIGSEGV",
+       "SIGSYS",
+       "SIGPIPE",
+       "SIGALRM",
+       "SIGTERM",
+       "SIGUSR1",
+       "SIGUSR2",
+       "SIGCLD",
+       "SIGPWR",
+       "SIGWIND",
+       "SIGPHONE",
+       "SIGPOLL",
+};
+#endif
+
+/* Queue routines */
+
+struct queue {
+       struct queue *forw;
+       struct queue *back;
+};
+
+insque (item, after)
+struct queue *item;
+struct queue *after;
+{
+       item->forw = after->forw;
+       after->forw->back = item;
+
+       item->back = after;
+       after->forw = item;
+}
+
+remque (item)
+struct queue *item;
+{
+       item->forw->back = item->back;
+       item->back->forw = item->forw;
+}
+#endif /* USG */
+\f
+#ifdef USG
+/* There is too much variation in Sys V signal numbers and names, so
+   we must initialize them at runtime.  */
+static char undoc[] = "(undocumented)";
+
+char *sys_siglist[NSIG];
+#endif /* USG */
+
+extern struct cmd_list_element *setlist;
+
+void
+_initialize_utils ()
+{
+  int i;
+  add_cmd ("screensize", class_support, set_screensize_command,
+          "Change gdb's notion of the size of the output screen.\n\
+The first argument is the number of lines on a page.\n\
+The second argument (optional) is the number of characters on a line.",
+          &setlist);
+  add_info ("screensize", screensize_info,
+           "Show gdb's current notion of the size of the output screen.");
+
+  /* These defaults will be used if we are unable to get the correct
+     values from termcap.  */
+  lines_per_page = 24;
+  chars_per_line = 80;
+  /* Initialize the screen height and width from termcap.  */
+  {
+    int termtype = getenv ("TERM");
+
+    /* Positive means success, nonpositive means failure.  */
+    int status;
+
+    /* 2048 is large enough for all known terminals, according to the
+       GNU termcap manual.  */
+    char term_buffer[2048];
+
+    if (termtype)
+      {
+       status = tgetent (term_buffer, termtype);
+       if (status > 0)
+         {
+           int val;
+           
+           val = tgetnum ("li");
+           if (val >= 0)
+             lines_per_page = val;
+           else
+             /* The number of lines per page is not mentioned
+                in the terminal description.  This probably means
+                that paging is not useful (e.g. emacs shell window),
+                so disable paging.  */
+             lines_per_page = 0;
+           
+           val = tgetnum ("co");
+           if (val >= 0)
+             chars_per_line = val;
+         }
+      }
+  }
+
+#ifdef USG
+  /* Initialize signal names.  */
+       for (i = 0; i < NSIG; i++)
+               sys_siglist[i] = undoc;
+
+#ifdef SIGHUP
+       sys_siglist[SIGHUP      ] = "SIGHUP";
+#endif
+#ifdef SIGINT
+       sys_siglist[SIGINT      ] = "SIGINT";
+#endif
+#ifdef SIGQUIT
+       sys_siglist[SIGQUIT     ] = "SIGQUIT";
+#endif
+#ifdef SIGILL
+       sys_siglist[SIGILL      ] = "SIGILL";
+#endif
+#ifdef SIGTRAP
+       sys_siglist[SIGTRAP     ] = "SIGTRAP";
+#endif
+#ifdef SIGIOT
+       sys_siglist[SIGIOT      ] = "SIGIOT";
+#endif
+#ifdef SIGEMT
+       sys_siglist[SIGEMT      ] = "SIGEMT";
+#endif
+#ifdef SIGFPE
+       sys_siglist[SIGFPE      ] = "SIGFPE";
+#endif
+#ifdef SIGKILL
+       sys_siglist[SIGKILL     ] = "SIGKILL";
+#endif
+#ifdef SIGBUS
+       sys_siglist[SIGBUS      ] = "SIGBUS";
+#endif
+#ifdef SIGSEGV
+       sys_siglist[SIGSEGV     ] = "SIGSEGV";
+#endif
+#ifdef SIGSYS
+       sys_siglist[SIGSYS      ] = "SIGSYS";
+#endif
+#ifdef SIGPIPE
+       sys_siglist[SIGPIPE     ] = "SIGPIPE";
+#endif
+#ifdef SIGALRM
+       sys_siglist[SIGALRM     ] = "SIGALRM";
+#endif
+#ifdef SIGTERM
+       sys_siglist[SIGTERM     ] = "SIGTERM";
+#endif
+#ifdef SIGUSR1
+       sys_siglist[SIGUSR1     ] = "SIGUSR1";
+#endif
+#ifdef SIGUSR2
+       sys_siglist[SIGUSR2     ] = "SIGUSR2";
+#endif
+#ifdef SIGCLD
+       sys_siglist[SIGCLD      ] = "SIGCLD";
+#endif
+#ifdef SIGCHLD
+       sys_siglist[SIGCHLD     ] = "SIGCHLD";
+#endif
+#ifdef SIGPWR
+       sys_siglist[SIGPWR      ] = "SIGPWR";
+#endif
+#ifdef SIGTSTP
+       sys_siglist[SIGTSTP     ] = "SIGTSTP";
+#endif
+#ifdef SIGTTIN
+       sys_siglist[SIGTTIN     ] = "SIGTTIN";
+#endif
+#ifdef SIGTTOU
+       sys_siglist[SIGTTOU     ] = "SIGTTOU";
+#endif
+#ifdef SIGSTOP
+       sys_siglist[SIGSTOP     ] = "SIGSTOP";
+#endif
+#ifdef SIGXCPU
+       sys_siglist[SIGXCPU     ] = "SIGXCPU";
+#endif
+#ifdef SIGXFSZ
+       sys_siglist[SIGXFSZ     ] = "SIGXFSZ";
+#endif
+#ifdef SIGVTALRM
+       sys_siglist[SIGVTALRM   ] = "SIGVTALRM";
+#endif
+#ifdef SIGPROF
+       sys_siglist[SIGPROF     ] = "SIGPROF";
+#endif
+#ifdef SIGWINCH
+       sys_siglist[SIGWINCH    ] = "SIGWINCH";
+#endif
+#ifdef SIGCONT
+       sys_siglist[SIGCONT     ] = "SIGCONT";
+#endif
+#ifdef SIGURG
+       sys_siglist[SIGURG      ] = "SIGURG";
+#endif
+#ifdef SIGIO
+       sys_siglist[SIGIO       ] = "SIGIO";
+#endif
+#ifdef SIGWIND
+       sys_siglist[SIGWIND     ] = "SIGWIND";
+#endif
+#ifdef SIGPHONE
+       sys_siglist[SIGPHONE    ] = "SIGPHONE";
+#endif
+#ifdef SIGPOLL
+       sys_siglist[SIGPOLL     ] = "SIGPOLL";
+#endif
+#endif /* USG */
+}
diff --git a/usr/src/usr.bin/gdb/valarith.c b/usr/src/usr.bin/gdb/valarith.c
new file mode 100644 (file)
index 0000000..8e76899
--- /dev/null
@@ -0,0 +1,690 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)valarith.c 6.3 (Berkeley) 5/8/91";
+#endif /* not lint */
+
+/* Perform arithmetic and other operations on values, for GDB.
+   Copyright (C) 1986, 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING.  If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#include "defs.h"
+#include "param.h"
+#include "symtab.h"
+#include "value.h"
+#include "expression.h"
+
+\f
+value value_x_binop ();
+value value_subscripted_rvalue ();
+
+value
+value_add (arg1, arg2)
+       value arg1, arg2;
+{
+  register value val, valint, valptr;
+  register int len;
+
+  COERCE_ARRAY (arg1);
+  COERCE_ARRAY (arg2);
+
+  if ((TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_PTR
+       || TYPE_CODE (VALUE_TYPE (arg2)) == TYPE_CODE_PTR)
+      &&
+      (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_INT
+       || TYPE_CODE (VALUE_TYPE (arg2)) == TYPE_CODE_INT))
+    /* Exactly one argument is a pointer, and one is an integer.  */
+    {
+      if (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_PTR)
+       {
+         valptr = arg1;
+         valint = arg2;
+       }
+      else
+       {
+         valptr = arg2;
+         valint = arg1;
+       }
+      len = TYPE_LENGTH (TYPE_TARGET_TYPE (VALUE_TYPE (valptr)));
+      if (len == 0) len = 1;   /* For (void *) */
+      val = value_from_long (builtin_type_long,
+                            value_as_long (valptr)
+                            + (len * value_as_long (valint)));
+      VALUE_TYPE (val) = VALUE_TYPE (valptr);
+      return val;
+    }
+
+  return value_binop (arg1, arg2, BINOP_ADD);
+}
+
+value
+value_sub (arg1, arg2)
+       value arg1, arg2;
+{
+  register value val;
+
+  COERCE_ARRAY (arg1);
+  COERCE_ARRAY (arg2);
+
+  if (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_PTR
+      && 
+      TYPE_CODE (VALUE_TYPE (arg2)) == TYPE_CODE_INT)
+    {
+      val = value_from_long (builtin_type_long,
+                            value_as_long (arg1)
+                            - TYPE_LENGTH (TYPE_TARGET_TYPE (VALUE_TYPE (arg1))) * value_as_long (arg2));
+      VALUE_TYPE (val) = VALUE_TYPE (arg1);
+      return val;
+    }
+
+  if (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_PTR
+      && 
+      VALUE_TYPE (arg1) == VALUE_TYPE (arg2))
+    {
+      val = value_from_long (builtin_type_long,
+                            (value_as_long (arg1) - value_as_long (arg2))
+                            / TYPE_LENGTH (TYPE_TARGET_TYPE (VALUE_TYPE (arg1))));
+      return val;
+    }
+
+  return value_binop (arg1, arg2, BINOP_SUB);
+}
+
+/* Return the value of ARRAY[IDX].  */
+
+value
+value_subscript (array, idx)
+     value array, idx;
+{
+  if (TYPE_CODE (VALUE_TYPE (array)) == TYPE_CODE_ARRAY
+      && VALUE_LVAL (array) != lval_memory)
+    return value_subscripted_rvalue (array, idx);
+  else
+    return value_ind (value_add (array, idx));
+}
+
+/* Return the value of EXPR[IDX], expr an aggregate rvalue
+   (eg, a vector register) */
+
+value
+value_subscripted_rvalue (array, idx)
+     value array, idx;
+{
+  struct type *elt_type = TYPE_TARGET_TYPE (VALUE_TYPE (array));
+  int elt_size = TYPE_LENGTH (elt_type);
+  int elt_offs = elt_size * value_as_long (idx);
+  value v;
+
+  if (elt_offs >= TYPE_LENGTH (VALUE_TYPE (array)))
+    error ("no such vector element");
+
+  if (TYPE_CODE (elt_type) == TYPE_CODE_FLT) 
+    {
+      if (elt_size == sizeof (float))
+       v = value_from_double (elt_type, (double) *(float *)
+                              (VALUE_CONTENTS (array) + elt_offs));
+      else
+       v = value_from_double (elt_type, *(double *)
+                              (VALUE_CONTENTS (array) + elt_offs));
+    }
+  else
+    {
+      int offs;
+      union {int i; char c;} test;
+      test.i = 1;
+      if (test.c == 1)
+       offs = 0;
+      else
+       offs = sizeof (LONGEST) - elt_size;
+      v = value_from_long (elt_type, *(LONGEST *)
+                          (VALUE_CONTENTS (array) + elt_offs - offs));
+    }
+
+  if (VALUE_LVAL (array) == lval_internalvar)
+    VALUE_LVAL (v) = lval_internalvar_component;
+  else
+    VALUE_LVAL (v) = not_lval;
+  VALUE_ADDRESS (v) = VALUE_ADDRESS (array);
+  VALUE_OFFSET (v) = VALUE_OFFSET (array) + elt_offs;
+  VALUE_BITSIZE (v) = elt_size * 8;
+  return v;
+}
+\f
+/* Check to see if either argument is a structure.  This is called so
+   we know whether to go ahead with the normal binop or look for a 
+   user defined function instead.
+
+   For now, we do not overload the `=' operator.  */
+
+int
+binop_user_defined_p (op, arg1, arg2)
+     enum exp_opcode op;
+     value arg1, arg2;
+{
+  if (op == BINOP_ASSIGN)
+    return 0;
+  return (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_STRUCT
+         || TYPE_CODE (VALUE_TYPE (arg2)) == TYPE_CODE_STRUCT
+         || (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_REF
+             && TYPE_CODE (TYPE_TARGET_TYPE (VALUE_TYPE (arg1))) == TYPE_CODE_STRUCT)
+         || (TYPE_CODE (VALUE_TYPE (arg2)) == TYPE_CODE_REF
+             && TYPE_CODE (TYPE_TARGET_TYPE (VALUE_TYPE (arg2))) == TYPE_CODE_STRUCT));
+}
+
+/* Check to see if argument is a structure.  This is called so
+   we know whether to go ahead with the normal unop or look for a 
+   user defined function instead.
+
+   For now, we do not overload the `&' operator.  */
+
+int unop_user_defined_p (op, arg1)
+     enum exp_opcode op;
+     value arg1;
+{
+  if (op == UNOP_ADDR)
+    return 0;
+  return (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_STRUCT
+         || (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_REF
+             && TYPE_CODE (TYPE_TARGET_TYPE (VALUE_TYPE (arg1))) == TYPE_CODE_STRUCT));
+}
+
+/* We know either arg1 or arg2 is a structure, so try to find the right
+   user defined function.  Create an argument vector that calls 
+   arg1.operator @ (arg1,arg2) and return that value (where '@' is any
+   binary operator which is legal for GNU C++).  */
+
+value
+value_x_binop (arg1, arg2, op, otherop)
+     value arg1, arg2;
+     int op, otherop;
+{
+  value * argvec;
+  char *ptr;
+  char tstr[13];
+  int static_memfuncp;
+
+  COERCE_ENUM (arg1);
+  COERCE_ENUM (arg2);
+
+  /* now we know that what we have to do is construct our
+     arg vector and find the right function to call it with.  */
+
+  if (TYPE_CODE (VALUE_TYPE (arg1)) != TYPE_CODE_STRUCT)
+    error ("friend functions not implemented yet");
+
+  argvec = (value *) alloca (sizeof (value) * 4);
+  argvec[1] = value_addr (arg1);
+  argvec[2] = arg2;
+  argvec[3] = 0;
+
+  /* make the right function name up */  
+  strcpy(tstr, "operator __");
+  ptr = tstr+9;
+  switch (op)
+    {
+    case BINOP_ADD:    strcpy(ptr,"+"); break;
+    case BINOP_SUB:    strcpy(ptr,"-"); break;
+    case BINOP_MUL:    strcpy(ptr,"*"); break;
+    case BINOP_DIV:    strcpy(ptr,"/"); break;
+    case BINOP_REM:    strcpy(ptr,"%"); break;
+    case BINOP_LSH:    strcpy(ptr,"<<"); break;
+    case BINOP_RSH:    strcpy(ptr,">>"); break;
+    case BINOP_LOGAND: strcpy(ptr,"&"); break;
+    case BINOP_LOGIOR: strcpy(ptr,"|"); break;
+    case BINOP_LOGXOR: strcpy(ptr,"^"); break;
+    case BINOP_AND:    strcpy(ptr,"&&"); break;
+    case BINOP_OR:     strcpy(ptr,"||"); break;
+    case BINOP_MIN:    strcpy(ptr,"<?"); break;
+    case BINOP_MAX:    strcpy(ptr,">?"); break;
+    case BINOP_ASSIGN: strcpy(ptr,"="); break;
+    case BINOP_ASSIGN_MODIFY:  
+      switch (otherop)
+       {
+       case BINOP_ADD:      strcpy(ptr,"+="); break;
+       case BINOP_SUB:      strcpy(ptr,"-="); break;
+       case BINOP_MUL:      strcpy(ptr,"*="); break;
+       case BINOP_DIV:      strcpy(ptr,"/="); break;
+       case BINOP_REM:      strcpy(ptr,"%="); break;
+       case BINOP_LOGAND:   strcpy(ptr,"&="); break;
+       case BINOP_LOGIOR:   strcpy(ptr,"|="); break;
+       case BINOP_LOGXOR:   strcpy(ptr,"^="); break;
+       default:
+         error ("Invalid binary operation specified.");
+       }
+      break;
+    case BINOP_SUBSCRIPT: strcpy(ptr,"[]"); break;
+    case BINOP_EQUAL:    strcpy(ptr,"=="); break;
+    case BINOP_NOTEQUAL:  strcpy(ptr,"!="); break;
+    case BINOP_LESS:      strcpy(ptr,"<"); break;
+    case BINOP_GTR:       strcpy(ptr,">"); break;
+    case BINOP_GEQ:       strcpy(ptr,">="); break;
+    case BINOP_LEQ:       strcpy(ptr,"<="); break;
+    default:
+      error ("Invalid binary operation specified.");
+    }
+  argvec[0] = value_struct_elt (arg1, argvec+1, tstr, &static_memfuncp, "structure");
+  if (argvec[0])
+    {
+      if (static_memfuncp)
+       {
+         argvec[1] = argvec[0];
+         argvec++;
+       }
+      return call_function (argvec[0], 2 - static_memfuncp, argvec + 1);
+    }
+  error ("member function %s not found", tstr);
+}
+
+/* We know that arg1 is a structure, so try to find a unary user
+   defined operator that matches the operator in question.  
+   Create an argument vector that calls arg1.operator @ (arg1)
+   and return that value (where '@' is (almost) any unary operator which
+   is legal for GNU C++).  */
+
+value
+value_x_unop (arg1, op)
+     value arg1;
+     int op;
+{
+  value * argvec;
+  char *ptr;
+  char tstr[13];
+  int static_memfuncp;
+
+  COERCE_ENUM (arg1);
+
+  /* now we know that what we have to do is construct our
+     arg vector and find the right function to call it with.  */
+
+  if (TYPE_CODE (VALUE_TYPE (arg1)) != TYPE_CODE_STRUCT)
+    error ("friend functions not implemented yet");
+
+  argvec = (value *) alloca (sizeof (value) * 3);
+  argvec[1] = value_addr (arg1);
+  argvec[2] = 0;
+
+  /* make the right function name up */  
+  strcpy(tstr,"operator __");
+  ptr = tstr+9;
+  switch (op)
+    {
+    case UNOP_PREINCREMENT:    strcpy(ptr,"++"); break;
+    case UNOP_PREDECREMENT:    strcpy(ptr,"++"); break;
+    case UNOP_POSTINCREMENT:   strcpy(ptr,"++"); break;
+    case UNOP_POSTDECREMENT:   strcpy(ptr,"++"); break;
+    case UNOP_ZEROP:   strcpy(ptr,"!"); break;
+    case UNOP_LOGNOT:  strcpy(ptr,"~"); break;
+    case UNOP_NEG:     strcpy(ptr,"-"); break;
+    default:
+      error ("Invalid binary operation specified.");
+    }
+  argvec[0] = value_struct_elt (arg1, argvec+1, tstr, &static_memfuncp, "structure");
+  if (argvec[0])
+    {
+      if (static_memfuncp)
+       {
+         argvec[1] = argvec[0];
+         argvec++;
+       }
+      return call_function (argvec[0], 1 - static_memfuncp, argvec + 1);
+    }
+  error ("member function %s not found", tstr);
+}
+\f
+/* Perform a binary operation on two integers or two floats.
+   Does not support addition and subtraction on pointers;
+   use value_add or value_sub if you want to handle those possibilities.  */
+
+value
+value_binop (arg1, arg2, op)
+     value arg1, arg2;
+     int op;
+{
+  register value val;
+
+  COERCE_ENUM (arg1);
+  COERCE_ENUM (arg2);
+
+  if ((TYPE_CODE (VALUE_TYPE (arg1)) != TYPE_CODE_FLT
+       &&
+       TYPE_CODE (VALUE_TYPE (arg1)) != TYPE_CODE_INT)
+      ||
+      (TYPE_CODE (VALUE_TYPE (arg2)) != TYPE_CODE_FLT
+       &&
+       TYPE_CODE (VALUE_TYPE (arg2)) != TYPE_CODE_INT))
+    error ("Argument to arithmetic operation not a number.");
+
+  if (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_FLT
+      ||
+      TYPE_CODE (VALUE_TYPE (arg2)) == TYPE_CODE_FLT)
+    {
+      double v1, v2, v;
+      v1 = value_as_double (arg1);
+      v2 = value_as_double (arg2);
+      switch (op)
+       {
+       case BINOP_ADD:
+         v = v1 + v2;
+         break;
+
+       case BINOP_SUB:
+         v = v1 - v2;
+         break;
+
+       case BINOP_MUL:
+         v = v1 * v2;
+         break;
+
+       case BINOP_DIV:
+         v = v1 / v2;
+         break;
+
+       default:
+         error ("Integer-only operation on floating point number.");
+       }
+
+      val = allocate_value (builtin_type_double);
+      *(double *) VALUE_CONTENTS (val) = v;
+    }
+  else
+    /* Integral operations here.  */
+    {
+      /* Should we promote to unsigned longest?  */
+      if ((TYPE_UNSIGNED (VALUE_TYPE (arg1))
+          || TYPE_UNSIGNED (VALUE_TYPE (arg2)))
+         && (TYPE_LENGTH (VALUE_TYPE (arg1)) >= sizeof (unsigned LONGEST)
+             || TYPE_LENGTH (VALUE_TYPE (arg2)) >= sizeof (unsigned LONGEST)))
+       {
+         unsigned LONGEST v1, v2, v;
+         v1 = (unsigned LONGEST) value_as_long (arg1);
+         v2 = (unsigned LONGEST) value_as_long (arg2);
+         
+         switch (op)
+           {
+           case BINOP_ADD:
+             v = v1 + v2;
+             break;
+             
+           case BINOP_SUB:
+             v = v1 - v2;
+             break;
+             
+           case BINOP_MUL:
+             v = v1 * v2;
+             break;
+             
+           case BINOP_DIV:
+             v = v1 / v2;
+             break;
+             
+           case BINOP_REM:
+             v = v1 % v2;
+             break;
+             
+           case BINOP_LSH:
+             v = v1 << v2;
+             break;
+             
+           case BINOP_RSH:
+             v = v1 >> v2;
+             break;
+             
+           case BINOP_LOGAND:
+             v = v1 & v2;
+             break;
+             
+           case BINOP_LOGIOR:
+             v = v1 | v2;
+             break;
+             
+           case BINOP_LOGXOR:
+             v = v1 ^ v2;
+             break;
+             
+           case BINOP_AND:
+             v = v1 && v2;
+             break;
+             
+           case BINOP_OR:
+             v = v1 || v2;
+             break;
+             
+           case BINOP_MIN:
+             v = v1 < v2 ? v1 : v2;
+             break;
+             
+           case BINOP_MAX:
+             v = v1 > v2 ? v1 : v2;
+             break;
+             
+           default:
+             error ("Invalid binary operation on numbers.");
+           }
+
+         val = allocate_value (BUILTIN_TYPE_UNSIGNED_LONGEST);
+         *(unsigned LONGEST *) VALUE_CONTENTS (val) = v;
+       }
+      else
+       {
+         LONGEST v1, v2, v;
+         v1 = value_as_long (arg1);
+         v2 = value_as_long (arg2);
+         
+         switch (op)
+           {
+           case BINOP_ADD:
+             v = v1 + v2;
+             break;
+             
+           case BINOP_SUB:
+             v = v1 - v2;
+             break;
+             
+           case BINOP_MUL:
+             v = v1 * v2;
+             break;
+             
+           case BINOP_DIV:
+             v = v1 / v2;
+             break;
+             
+           case BINOP_REM:
+             v = v1 % v2;
+             break;
+             
+           case BINOP_LSH:
+             v = v1 << v2;
+             break;
+             
+           case BINOP_RSH:
+             v = v1 >> v2;
+             break;
+             
+           case BINOP_LOGAND:
+             v = v1 & v2;
+             break;
+             
+           case BINOP_LOGIOR:
+             v = v1 | v2;
+             break;
+             
+           case BINOP_LOGXOR:
+             v = v1 ^ v2;
+             break;
+             
+           case BINOP_AND:
+             v = v1 && v2;
+             break;
+             
+           case BINOP_OR:
+             v = v1 || v2;
+             break;
+             
+           case BINOP_MIN:
+             v = v1 < v2 ? v1 : v2;
+             break;
+             
+           case BINOP_MAX:
+             v = v1 > v2 ? v1 : v2;
+             break;
+             
+           default:
+             error ("Invalid binary operation on numbers.");
+           }
+         
+         val = allocate_value (BUILTIN_TYPE_LONGEST);
+         *(LONGEST *) VALUE_CONTENTS (val) = v;
+       }
+    }
+
+  return val;
+}
+\f
+/* Simulate the C operator ! -- return 1 if ARG1 contains zeros.  */
+
+int
+value_zerop (arg1)
+     value arg1;
+{
+  register int len;
+  register char *p;
+
+  COERCE_ARRAY (arg1);
+
+  len = TYPE_LENGTH (VALUE_TYPE (arg1));
+  p = VALUE_CONTENTS (arg1);
+
+  while (--len >= 0)
+    {
+      if (*p++)
+       break;
+    }
+
+  return len < 0;
+}
+
+/* Simulate the C operator == by returning a 1
+   iff ARG1 and ARG2 have equal contents.  */
+
+int
+value_equal (arg1, arg2)
+     register value arg1, arg2;
+
+{
+  register int len;
+  register char *p1, *p2;
+  enum type_code code1;
+  enum type_code code2;
+
+  COERCE_ARRAY (arg1);
+  COERCE_ARRAY (arg2);
+
+  code1 = TYPE_CODE (VALUE_TYPE (arg1));
+  code2 = TYPE_CODE (VALUE_TYPE (arg2));
+
+  if (code1 == TYPE_CODE_INT && code2 == TYPE_CODE_INT)
+    return value_as_long (arg1) == value_as_long (arg2);
+  else if ((code1 == TYPE_CODE_FLT || code1 == TYPE_CODE_INT)
+          && (code2 == TYPE_CODE_FLT || code2 == TYPE_CODE_INT))
+    return value_as_double (arg1) == value_as_double (arg2);
+  else if ((code1 == TYPE_CODE_PTR && code2 == TYPE_CODE_INT)
+          || (code2 == TYPE_CODE_PTR && code1 == TYPE_CODE_INT))
+    return (char *) value_as_long (arg1) == (char *) value_as_long (arg2);
+  else if (code1 == code2
+          && ((len = TYPE_LENGTH (VALUE_TYPE (arg1)))
+              == TYPE_LENGTH (VALUE_TYPE (arg2))))
+    {
+      p1 = VALUE_CONTENTS (arg1);
+      p2 = VALUE_CONTENTS (arg2);
+      while (--len >= 0)
+       {
+         if (*p1++ != *p2++)
+           break;
+       }
+      return len < 0;
+    }
+  else
+    error ("Invalid type combination in equality test.");
+}
+
+/* Simulate the C operator < by returning 1
+   iff ARG1's contents are less than ARG2's.  */
+
+int
+value_less (arg1, arg2)
+     register value arg1, arg2;
+{
+  register enum type_code code1;
+  register enum type_code code2;
+
+  COERCE_ARRAY (arg1);
+  COERCE_ARRAY (arg2);
+
+  code1 = TYPE_CODE (VALUE_TYPE (arg1));
+  code2 = TYPE_CODE (VALUE_TYPE (arg2));
+
+  if (code1 == TYPE_CODE_INT && code2 == TYPE_CODE_INT)
+    return value_as_long (arg1) < value_as_long (arg2);
+  else if ((code1 == TYPE_CODE_FLT || code1 == TYPE_CODE_INT)
+          && (code2 == TYPE_CODE_FLT || code2 == TYPE_CODE_INT))
+    return value_as_double (arg1) < value_as_double (arg2);
+  else if ((code1 == TYPE_CODE_PTR || code1 == TYPE_CODE_INT)
+          && (code2 == TYPE_CODE_PTR || code2 == TYPE_CODE_INT))
+    return (char *) value_as_long (arg1) < (char *) value_as_long (arg2);
+  else
+    error ("Invalid type combination in ordering comparison.");
+}
+\f
+/* The unary operators - and ~.  Both free the argument ARG1.  */
+
+value
+value_neg (arg1)
+     register value arg1;
+{
+  register struct type *type;
+
+  COERCE_ENUM (arg1);
+
+  type = VALUE_TYPE (arg1);
+
+  if (TYPE_CODE (type) == TYPE_CODE_FLT)
+    return value_from_double (type, - value_as_double (arg1));
+  else if (TYPE_CODE (type) == TYPE_CODE_INT)
+    return value_from_long (type, - value_as_long (arg1));
+  else
+    error ("Argument to negate operation not a number.");
+}
+
+value
+value_lognot (arg1)
+     register value arg1;
+{
+  COERCE_ENUM (arg1);
+
+  if (TYPE_CODE (VALUE_TYPE (arg1)) != TYPE_CODE_INT)
+    error ("Argument to complement operation not an integer.");
+
+  return value_from_long (VALUE_TYPE (arg1), ~ value_as_long (arg1));
+}
+\f
diff --git a/usr/src/usr.bin/gdb/valops.c b/usr/src/usr.bin/gdb/valops.c
new file mode 100644 (file)
index 0000000..ab5652c
--- /dev/null
@@ -0,0 +1,1418 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)valops.c   6.4 (Berkeley) 5/8/91";
+#endif /* not lint */
+
+/* Perform non-arithmetic operations on values, for GDB.
+   Copyright (C) 1986, 1987, 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING.  If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#include "stdio.h"
+#include "defs.h"
+#include "param.h"
+#include "symtab.h"
+#include "value.h"
+#include "frame.h"
+#include "inferior.h"
+\f
+/* Cast value ARG2 to type TYPE and return as a value.
+   More general than a C cast: accepts any two types of the same length,
+   and if ARG2 is an lvalue it can be cast into anything at all.  */
+
+value
+value_cast (type, arg2)
+     struct type *type;
+     register value arg2;
+{
+  register enum type_code code1;
+  register enum type_code code2;
+  register int scalar;
+
+  /* Coerce arrays but not enums.  Enums will work as-is
+     and coercing them would cause an infinite recursion.  */
+  if (TYPE_CODE (VALUE_TYPE (arg2)) != TYPE_CODE_ENUM)
+    COERCE_ARRAY (arg2);
+
+  code1 = TYPE_CODE (type);
+  code2 = TYPE_CODE (VALUE_TYPE (arg2));
+  scalar = (code2 == TYPE_CODE_INT || code2 == TYPE_CODE_FLT
+           || code2 == TYPE_CODE_ENUM);
+
+  if (code1 == TYPE_CODE_FLT && scalar)
+    return value_from_double (type, value_as_double (arg2));
+  else if ((code1 == TYPE_CODE_INT || code1 == TYPE_CODE_ENUM)
+          && (scalar || code2 == TYPE_CODE_PTR))
+    return value_from_long (type, value_as_long (arg2));
+  else if (TYPE_LENGTH (type) == TYPE_LENGTH (VALUE_TYPE (arg2)))
+    {
+      VALUE_TYPE (arg2) = type;
+      return arg2;
+    }
+  else if (VALUE_LVAL (arg2) == lval_memory)
+    {
+      return value_at (type, VALUE_ADDRESS (arg2) + VALUE_OFFSET (arg2));
+    }
+  else
+    error ("Invalid cast.");
+}
+
+/* Create a value of type TYPE that is zero, and return it.  */
+
+value
+value_zero (type, lv)
+     struct type *type;
+     enum lval_type lv;
+{
+  register value val = allocate_value (type);
+
+  bzero (VALUE_CONTENTS (val), TYPE_LENGTH (type));
+  VALUE_LVAL (val) = lv;
+
+  return val;
+}
+
+/* Return the value with a specified type located at specified address.  */
+
+value
+value_at (type, addr)
+     struct type *type;
+     CORE_ADDR addr;
+{
+  register value val = allocate_value (type);
+  int temp;
+
+  temp = read_memory (addr, VALUE_CONTENTS (val), TYPE_LENGTH (type));
+  if (temp)
+    {
+      if (have_inferior_p () && !remote_debugging)
+       print_sys_errmsg ("ptrace", temp);
+      /* Actually, address between addr and addr + len was out of bounds. */
+      error ("Cannot read memory: address 0x%x out of bounds.", addr);
+    }
+
+  VALUE_LVAL (val) = lval_memory;
+  VALUE_ADDRESS (val) = addr;
+
+  return val;
+}
+
+/* Store the contents of FROMVAL into the location of TOVAL.
+   Return a new value with the location of TOVAL and contents of FROMVAL.  */
+
+value
+value_assign (toval, fromval)
+     register value toval, fromval;
+{
+  register struct type *type = VALUE_TYPE (toval);
+  register value val;
+  char raw_buffer[MAX_REGISTER_RAW_SIZE];
+  char virtual_buffer[MAX_REGISTER_VIRTUAL_SIZE];
+  int use_buffer = 0;
+
+  extern CORE_ADDR find_saved_register ();
+
+  COERCE_ARRAY (fromval);
+
+  if (VALUE_LVAL (toval) != lval_internalvar)
+    fromval = value_cast (type, fromval);
+
+  /* If TOVAL is a special machine register requiring conversion
+     of program values to a special raw format,
+     convert FROMVAL's contents now, with result in `raw_buffer',
+     and set USE_BUFFER to the number of bytes to write.  */
+
+  if (VALUE_REGNO (toval) >= 0
+      && REGISTER_CONVERTIBLE (VALUE_REGNO (toval)))
+    {
+      int regno = VALUE_REGNO (toval);
+      if (VALUE_TYPE (fromval) != REGISTER_VIRTUAL_TYPE (regno))
+       fromval = value_cast (REGISTER_VIRTUAL_TYPE (regno), fromval);
+      bcopy (VALUE_CONTENTS (fromval), virtual_buffer,
+            REGISTER_VIRTUAL_SIZE (regno));
+      REGISTER_CONVERT_TO_RAW (regno, virtual_buffer, raw_buffer);
+      use_buffer = REGISTER_RAW_SIZE (regno);
+    }
+
+  switch (VALUE_LVAL (toval))
+    {
+    case lval_internalvar:
+      set_internalvar (VALUE_INTERNALVAR (toval), fromval);
+      break;
+
+    case lval_internalvar_component:
+      set_internalvar_component (VALUE_INTERNALVAR (toval),
+                                VALUE_OFFSET (toval),
+                                VALUE_BITPOS (toval),
+                                VALUE_BITSIZE (toval),
+                                fromval);
+      break;
+
+    case lval_memory:
+      if (VALUE_BITSIZE (toval))
+       {
+         int val;
+         read_memory (VALUE_ADDRESS (toval) + VALUE_OFFSET (toval),
+                      &val, sizeof val);
+         modify_field (&val, (int) value_as_long (fromval),
+                       VALUE_BITPOS (toval), VALUE_BITSIZE (toval));
+         write_memory (VALUE_ADDRESS (toval) + VALUE_OFFSET (toval),
+                       &val, sizeof val);
+       }
+      else if (use_buffer)
+       write_memory (VALUE_ADDRESS (toval) + VALUE_OFFSET (toval),
+                     raw_buffer, use_buffer);
+      else
+       write_memory (VALUE_ADDRESS (toval) + VALUE_OFFSET (toval),
+                     VALUE_CONTENTS (fromval), TYPE_LENGTH (type));
+      break;
+
+    case lval_register:
+      if (VALUE_BITSIZE (toval))
+       {
+         int val;
+
+         read_register_bytes (VALUE_ADDRESS (toval) + VALUE_OFFSET (toval),
+                              &val, sizeof val);
+         modify_field (&val, (int) value_as_long (fromval),
+                       VALUE_BITPOS (toval), VALUE_BITSIZE (toval));
+         write_register_bytes (VALUE_ADDRESS (toval) + VALUE_OFFSET (toval),
+                               &val, sizeof val);
+       }
+      else if (use_buffer)
+       write_register_bytes (VALUE_ADDRESS (toval) + VALUE_OFFSET (toval),
+                             raw_buffer, use_buffer);
+      else
+       write_register_bytes (VALUE_ADDRESS (toval) + VALUE_OFFSET (toval),
+                             VALUE_CONTENTS (fromval), TYPE_LENGTH (type));
+      break;
+
+    case lval_reg_frame_relative:
+      {
+       /* value is stored in a series of registers in the frame
+          specified by the structure.  Copy that value out, modify
+          it, and copy it back in.  */
+       int amount_to_copy = (VALUE_BITSIZE (toval) ? 1 : TYPE_LENGTH (type));
+       int reg_size = REGISTER_RAW_SIZE (VALUE_FRAME_REGNUM (toval));
+       int byte_offset = VALUE_OFFSET (toval) % reg_size;
+       int reg_offset = VALUE_OFFSET (toval) / reg_size;
+       int amount_copied;
+       char *buffer = (char *) alloca (amount_to_copy);
+       int regno;
+       FRAME frame;
+       CORE_ADDR addr;
+
+       /* Figure out which frame this is in currently.  */
+       for (frame = get_current_frame ();
+            frame && FRAME_FP (frame) != VALUE_FRAME (toval);
+            frame = get_prev_frame (frame))
+         ;
+
+       if (!frame)
+         error ("Value being assigned to is no longer active.");
+
+       amount_to_copy += (reg_size - amount_to_copy % reg_size);
+
+       /* Copy it out.  */
+       for ((regno = VALUE_FRAME_REGNUM (toval) + reg_offset,
+             amount_copied = 0);
+            amount_copied < amount_to_copy;
+            amount_copied += reg_size, regno++)
+         {
+           addr = find_saved_register (frame, regno);
+           if (addr == 0)
+             read_register_bytes (REGISTER_BYTE (regno),
+                                  buffer + amount_copied,
+                                  reg_size);
+           else
+             read_memory (addr, buffer + amount_copied, reg_size);
+         }
+
+       /* Modify what needs to be modified.  */
+       if (VALUE_BITSIZE (toval))
+         modify_field (buffer + byte_offset,
+                       (int) value_as_long (fromval),
+                       VALUE_BITPOS (toval), VALUE_BITSIZE (toval));
+       else if (use_buffer)
+         bcopy (raw_buffer, buffer + byte_offset, use_buffer);
+       else
+         bcopy (VALUE_CONTENTS (fromval), buffer + byte_offset,
+                TYPE_LENGTH (type));
+
+       /* Copy it back.  */
+       for ((regno = VALUE_FRAME_REGNUM (toval) + reg_offset,
+             amount_copied = 0);
+            amount_copied < amount_to_copy;
+            amount_copied += reg_size, regno++)
+         {
+           addr = find_saved_register (frame, regno);
+           if (addr == 0)
+             write_register_bytes (REGISTER_BYTE (regno),
+                                   buffer + amount_copied,
+                                   reg_size);
+           else
+             write_memory (addr, buffer + amount_copied, reg_size);
+         }
+      }
+      break;
+       
+
+    default:
+      error ("Left side of = operation is not an lvalue.");
+    }
+
+  /* Return a value just like TOVAL except with the contents of FROMVAL
+     (except in the case of the type if TOVAL is an internalvar).  */
+
+  if (VALUE_LVAL (toval) == lval_internalvar
+      || VALUE_LVAL (toval) == lval_internalvar_component)
+    {
+      type = VALUE_TYPE (fromval);
+    }
+
+  val = allocate_value (type);
+  bcopy (toval, val, VALUE_CONTENTS (val) - (char *) val);
+  bcopy (VALUE_CONTENTS (fromval), VALUE_CONTENTS (val), TYPE_LENGTH (type));
+  VALUE_TYPE (val) = type;
+  
+  return val;
+}
+
+/* Extend a value VAL to COUNT repetitions of its type.  */
+
+value
+value_repeat (arg1, count)
+     value arg1;
+     int count;
+{
+  register value val;
+
+  if (VALUE_LVAL (arg1) != lval_memory)
+    error ("Only values in memory can be extended with '@'.");
+  if (count < 1)
+    error ("Invalid number %d of repetitions.", count);
+
+  val = allocate_repeat_value (VALUE_TYPE (arg1), count);
+
+  read_memory (VALUE_ADDRESS (arg1) + VALUE_OFFSET (arg1),
+              VALUE_CONTENTS (val),
+              TYPE_LENGTH (VALUE_TYPE (val)) * count);
+  VALUE_LVAL (val) = lval_memory;
+  VALUE_ADDRESS (val) = VALUE_ADDRESS (arg1) + VALUE_OFFSET (arg1);
+
+  return val;
+}
+
+value
+value_of_variable (var)
+     struct symbol *var;
+{
+  return read_var_value (var, (FRAME) 0);
+}
+
+/* Given a value which is an array, return a value which is
+   a pointer to its first element.  */
+
+value
+value_coerce_array (arg1)
+     value arg1;
+{
+  register struct type *type;
+  register value val;
+
+  if (VALUE_LVAL (arg1) != lval_memory)
+    error ("Attempt to take address of value not located in memory.");
+
+  /* Get type of elements.  */
+  if (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_ARRAY)
+    type = TYPE_TARGET_TYPE (VALUE_TYPE (arg1));
+  else
+    /* A phony array made by value_repeat.
+       Its type is the type of the elements, not an array type.  */
+    type = VALUE_TYPE (arg1);
+
+  /* Get the type of the result.  */
+  type = lookup_pointer_type (type);
+  val = value_from_long (builtin_type_long,
+                      (LONGEST) (VALUE_ADDRESS (arg1) + VALUE_OFFSET (arg1)));
+  VALUE_TYPE (val) = type;
+  return val;
+}
+
+/* Return a pointer value for the object for which ARG1 is the contents.  */
+
+value
+value_addr (arg1)
+     value arg1;
+{
+  register struct type *type;
+  register value val, arg1_coerced;
+
+  /* Taking the address of an array is really a no-op
+     once the array is coerced to a pointer to its first element.  */
+  arg1_coerced = arg1;
+  COERCE_ARRAY (arg1_coerced);
+  if (arg1 != arg1_coerced)
+    return arg1_coerced;
+
+  if (VALUE_LVAL (arg1) != lval_memory)
+    error ("Attempt to take address of value not located in memory.");
+
+  /* Get the type of the result.  */
+  type = lookup_pointer_type (VALUE_TYPE (arg1));
+  val = value_from_long (builtin_type_long,
+               (LONGEST) (VALUE_ADDRESS (arg1) + VALUE_OFFSET (arg1)));
+  VALUE_TYPE (val) = type;
+  return val;
+}
+
+/* Given a value of a pointer type, apply the C unary * operator to it.  */
+
+value
+value_ind (arg1)
+     value arg1;
+{
+  /* Must do this before COERCE_ARRAY, otherwise an infinite loop
+     will result */
+  if (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_REF)
+    return value_at (TYPE_TARGET_TYPE (VALUE_TYPE (arg1)),
+                    (CORE_ADDR) value_as_long (arg1));
+
+  COERCE_ARRAY (arg1);
+
+  if (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_MEMBER)
+    error ("not implemented: member types in value_ind");
+
+  /* Allow * on an integer so we can cast it to whatever we want.
+     This returns an int, which seems like the most C-like thing
+     to do.  "long long" variables are rare enough that
+     BUILTIN_TYPE_LONGEST would seem to be a mistake.  */
+  if (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_INT)
+    return value_at (builtin_type_int,
+                    (CORE_ADDR) value_as_long (arg1));
+  else if (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_PTR)
+    return value_at (TYPE_TARGET_TYPE (VALUE_TYPE (arg1)),
+                    (CORE_ADDR) value_as_long (arg1));
+  else if (TYPE_CODE (VALUE_TYPE (arg1)) == TYPE_CODE_REF)
+    return value_at (TYPE_TARGET_TYPE (VALUE_TYPE (arg1)),
+                    (CORE_ADDR) value_as_long (arg1));
+  error ("Attempt to take contents of a non-pointer value.");
+}
+\f
+/* Pushing small parts of stack frames.  */
+
+/* Push one word (the size of object that a register holds).  */
+
+CORE_ADDR
+push_word (sp, buffer)
+     CORE_ADDR sp;
+     REGISTER_TYPE buffer;
+{
+  register int len = sizeof (REGISTER_TYPE);
+
+#if 1 INNER_THAN 2
+  sp -= len;
+  write_memory (sp, &buffer, len);
+#else /* stack grows upward */
+  write_memory (sp, &buffer, len);
+  sp += len;
+#endif /* stack grows upward */
+
+  return sp;
+}
+
+/* Push LEN bytes with data at BUFFER.  */
+
+CORE_ADDR
+push_bytes (sp, buffer, len)
+     CORE_ADDR sp;
+     char *buffer;
+     int len;
+{
+#if 1 INNER_THAN 2
+  sp -= len;
+  write_memory (sp, buffer, len);
+#else /* stack grows upward */
+  write_memory (sp, buffer, len);
+  sp += len;
+#endif /* stack grows upward */
+
+  return sp;
+}
+
+/* Push onto the stack the specified value VALUE.  */
+
+CORE_ADDR
+value_push (sp, arg)
+     register CORE_ADDR sp;
+     value arg;
+{
+  register int len = TYPE_LENGTH (VALUE_TYPE (arg));
+
+#if 1 INNER_THAN 2
+  sp -= len;
+  write_memory (sp, VALUE_CONTENTS (arg), len);
+#else /* stack grows upward */
+  write_memory (sp, VALUE_CONTENTS (arg), len);
+  sp += len;
+#endif /* stack grows upward */
+
+  return sp;
+}
+
+/* Perform the standard coercions that are specified
+   for arguments to be passed to C functions.  */
+
+value
+value_arg_coerce (arg)
+     value arg;
+{
+  register struct type *type;
+
+  COERCE_ENUM (arg);
+
+  type = VALUE_TYPE (arg);
+
+  if (TYPE_CODE (type) == TYPE_CODE_INT
+      && TYPE_LENGTH (type) < sizeof (int))
+    return value_cast (builtin_type_int, arg);
+
+  if (type == builtin_type_float)
+    return value_cast (builtin_type_double, arg);
+
+  return arg;
+}
+
+/* Push the value ARG, first coercing it as an argument
+   to a C function.  */
+
+CORE_ADDR
+value_arg_push (sp, arg)
+     register CORE_ADDR sp;
+     value arg;
+{
+  return value_push (sp, value_arg_coerce (arg));
+}
+
+#ifdef NEW_CALL_FUNCTION
+
+int
+arg_stacklen(nargs, args)
+       int nargs;
+       value *args;
+{
+       int len = 0;
+
+       while (--nargs >= 0)
+               len += TYPE_LENGTH(VALUE_TYPE(value_arg_coerce(args[nargs])));
+
+       return len;
+}
+
+CORE_ADDR
+function_address(function, type)
+       value function;
+       struct type **type;
+{
+       register CORE_ADDR funaddr;
+       register struct type *ftype = VALUE_TYPE(function);
+       register enum type_code code = TYPE_CODE(ftype);
+
+       /*
+        * If it's a member function, just look at the function part
+        * of it.
+        */
+
+       /* Determine address to call.  */
+       if (code == TYPE_CODE_FUNC || code == TYPE_CODE_METHOD) {
+               funaddr = VALUE_ADDRESS(function);
+               *type = TYPE_TARGET_TYPE(ftype);
+       } else if (code == TYPE_CODE_PTR) {
+               funaddr = value_as_long(function);
+               if (TYPE_CODE(TYPE_TARGET_TYPE(ftype)) == TYPE_CODE_FUNC
+                   || TYPE_CODE(TYPE_TARGET_TYPE(ftype)) == TYPE_CODE_METHOD)
+                       *type = TYPE_TARGET_TYPE(TYPE_TARGET_TYPE(ftype));
+               else
+                       *type = builtin_type_int;
+       } else if (code == TYPE_CODE_INT) {
+               /*
+                * Handle the case of functions lacking debugging
+                * info. Their values are characters since their
+                * addresses are char
+                */
+               if (TYPE_LENGTH(ftype) == 1)
+
+                       funaddr = value_as_long(value_addr(function));
+               else
+                       /*
+                        * Handle integer used as address of a
+                        * function.
+                        */
+                       funaddr = value_as_long(function);
+               
+               *type = builtin_type_int;
+       } else
+               error("Invalid data type for function to be called.");
+
+       return funaddr;
+}
+       
+/* Perform a function call in the inferior.
+   ARGS is a vector of values of arguments (NARGS of them).
+   FUNCTION is a value, the function to be called.
+   Returns a value representing what the function returned.
+   May fail to return, if a breakpoint or signal is hit
+   during the execution of the function.  */
+
+value
+call_function(function, nargs, args)
+       value function;
+       int nargs;
+       value *args;
+{
+       register CORE_ADDR sp, pc;
+       struct type *value_type;
+       struct inferior_status inf_status;
+       struct cleanup *old_chain;
+       register CORE_ADDR funaddr;
+       int struct_return_bytes;
+       char retbuf[REGISTER_BYTES];
+
+       if (!have_inferior_p())
+           error("Cannot invoke functions if the inferior is not running.");
+
+       save_inferior_status(&inf_status, 1);
+       old_chain = make_cleanup(restore_inferior_status, &inf_status);
+
+       sp = read_register(SP_REGNUM);
+       funaddr = function_address(function, &value_type);
+       /*
+        * Are we returning a value using a structure return or a
+        * normal value return?
+        */
+       if (using_struct_return(function, funaddr, value_type))
+               struct_return_bytes = TYPE_LENGTH(value_type);
+       else 
+               struct_return_bytes = 0;
+       /*
+        * Create a call sequence customized for this function and
+        * the number of arguments for it.
+        */
+       pc = setup_dummy(sp, funaddr, nargs, args, 
+                        struct_return_bytes, value_arg_push);
+
+       /*
+        * Execute the stack dummy stub.  The register state will be
+        * returned in retbuf.  It is restored below.
+        */
+       run_stack_dummy(pc, retbuf);
+
+       /*
+        * This will restore the register context that existed before
+        * we called the dummy function.
+        */
+       do_cleanups(old_chain);
+
+       return value_being_returned(value_type, retbuf, struct_return_bytes);
+}
+#else
+
+/* Perform a function call in the inferior.
+   ARGS is a vector of values of arguments (NARGS of them).
+   FUNCTION is a value, the function to be called.
+   Returns a value representing what the function returned.
+   May fail to return, if a breakpoint or signal is hit
+   during the execution of the function.  */
+
+value
+call_function (function, nargs, args)
+     value function;
+     int nargs;
+     value *args;
+{
+  register CORE_ADDR sp;
+  register int i;
+  CORE_ADDR start_sp;
+  static REGISTER_TYPE dummy[] = CALL_DUMMY;
+  REGISTER_TYPE dummy1[sizeof dummy / sizeof (REGISTER_TYPE)];
+  CORE_ADDR old_sp;
+  struct type *value_type;
+  unsigned char struct_return;
+  CORE_ADDR struct_addr;
+  struct inferior_status inf_status;
+  struct cleanup *old_chain;
+
+  if (!have_inferior_p ())
+    error ("Cannot invoke functions if the inferior is not running.");
+
+  save_inferior_status (&inf_status, 1);
+  old_chain = make_cleanup (restore_inferior_status, &inf_status);
+
+  /* PUSH_DUMMY_FRAME is responsible for saving the inferior registers
+     (and POP_FRAME for restoring them).  (At least on most machines)
+     they are saved on the stack in the inferior.  */
+  PUSH_DUMMY_FRAME;
+
+  old_sp = sp = read_register (SP_REGNUM);
+
+#if 1 INNER_THAN 2             /* Stack grows down */
+  sp -= sizeof dummy;
+  start_sp = sp;
+#else                          /* Stack grows up */
+  start_sp = sp;
+  sp += sizeof dummy;
+#endif
+
+  {
+    register CORE_ADDR funaddr;
+    register struct type *ftype = VALUE_TYPE (function);
+    register enum type_code code = TYPE_CODE (ftype);
+
+    /* If it's a member function, just look at the function
+       part of it.  */
+
+    /* Determine address to call.  */
+    if (code == TYPE_CODE_FUNC || code == TYPE_CODE_METHOD)
+      {
+       funaddr = VALUE_ADDRESS (function);
+       value_type = TYPE_TARGET_TYPE (ftype);
+      }
+    else if (code == TYPE_CODE_PTR)
+      {
+       funaddr = value_as_long (function);
+       if (TYPE_CODE (TYPE_TARGET_TYPE (ftype)) == TYPE_CODE_FUNC
+           || TYPE_CODE (TYPE_TARGET_TYPE (ftype)) == TYPE_CODE_METHOD)
+         value_type = TYPE_TARGET_TYPE (TYPE_TARGET_TYPE (ftype));
+       else
+         value_type = builtin_type_int;
+      }
+    else if (code == TYPE_CODE_INT)
+      {
+       /* Handle the case of functions lacking debugging info.
+          Their values are characters since their addresses are char */
+       if (TYPE_LENGTH (ftype) == 1)
+         funaddr = value_as_long (value_addr (function));
+       else
+         /* Handle integer used as address of a function.  */
+         funaddr = value_as_long (function);
+
+       value_type = builtin_type_int;
+      }
+    else
+      error ("Invalid data type for function to be called.");
+
+    /* Are we returning a value using a structure return or a normal
+       value return? */
+
+    struct_return = using_struct_return (function, funaddr, value_type);
+
+    /* Create a call sequence customized for this function
+       and the number of arguments for it.  */
+    bcopy (dummy, dummy1, sizeof dummy);
+    FIX_CALL_DUMMY (dummy1, start_sp, funaddr, nargs, value_type);
+  }
+
+#ifndef CANNOT_EXECUTE_STACK
+  write_memory (start_sp, dummy1, sizeof dummy);
+
+#else
+  /* Convex Unix prohibits executing in the stack segment. */
+  /* Hope there is empty room at the top of the text segment. */
+  {
+    extern CORE_ADDR text_end;
+    static checked = 0;
+    if (!checked)
+      for (start_sp = text_end - sizeof dummy; start_sp < text_end; ++start_sp)
+       if (read_memory_integer (start_sp, 1) != 0)
+         error ("text segment full -- no place to put call");
+    checked = 1;
+    sp = old_sp;
+    start_sp = text_end - sizeof dummy;
+    write_memory (start_sp, dummy1, sizeof dummy);
+  }
+#endif /* CANNOT_EXECUTE_STACK */
+#ifdef STACK_ALIGN
+  /* If stack grows down, we must leave a hole at the top. */
+  {
+    int len = 0;
+
+    /* Reserve space for the return structure to be written on the
+       stack, if necessary */
+
+    if (struct_return)
+      len += TYPE_LENGTH (value_type);
+    
+    for (i = nargs - 1; i >= 0; i--)
+      len += TYPE_LENGTH (VALUE_TYPE (value_arg_coerce (args[i])));
+#ifdef CALL_DUMMY_STACK_ADJUST
+    len += CALL_DUMMY_STACK_ADJUST;
+#endif
+#if 1 INNER_THAN 2
+    sp -= STACK_ALIGN (len) - len;
+#else
+    sp += STACK_ALIGN (len) - len;
+#endif
+  }
+#endif /* STACK_ALIGN */
+
+    /* Reserve space for the return structure to be written on the
+       stack, if necessary */
+
+    if (struct_return)
+      {
+#if 1 INNER_THAN 2
+       sp -= TYPE_LENGTH (value_type);
+       struct_addr = sp;
+#else
+       struct_addr = sp;
+       sp += TYPE_LENGTH (value_type);
+#endif
+      }
+    
+  for (i = nargs - 1; i >= 0; i--)
+    sp = value_arg_push (sp, args[i]);
+
+#ifdef CALL_DUMMY_STACK_ADJUST
+#if 1 INNER_THAN 2
+  sp -= CALL_DUMMY_STACK_ADJUST;
+#else
+  sp += CALL_DUMMY_STACK_ADJUST;
+#endif
+#endif /* CALL_DUMMY_STACK_ADJUST */
+
+  /* Store the address at which the structure is supposed to be
+     written.  Note that this (and the code which reserved the space
+     above) assumes that gcc was used to compile this function.  Since
+     it doesn't cost us anything but space and if the function is pcc
+     it will ignore this value, we will make that assumption.
+
+     Also note that on some machines (like the sparc) pcc uses this
+     convention in a slightly twisted way also.  */
+
+  if (struct_return)
+    STORE_STRUCT_RETURN (struct_addr, sp);
+
+  /* Write the stack pointer.  This is here because the statement above
+     might fool with it */
+  write_register (SP_REGNUM, sp);
+
+  /* Figure out the value returned by the function.  */
+  {
+    char retbuf[REGISTER_BYTES];
+
+    /* Execute the stack dummy routine, calling FUNCTION.
+       When it is done, discard the empty frame
+       after storing the contents of all regs into retbuf.  */
+    run_stack_dummy (start_sp + CALL_DUMMY_START_OFFSET, retbuf);
+
+    do_cleanups (old_chain);
+
+    return value_being_returned (value_type, retbuf, struct_return);
+  }
+}
+#endif
+\f
+/* Create a value for a string constant:
+   Call the function malloc in the inferior to get space for it,
+   then copy the data into that space
+   and then return the address with type char *.
+   PTR points to the string constant data; LEN is number of characters.  */
+
+value
+value_string (ptr, len)
+     char *ptr;
+     int len;
+{
+  register value val;
+  register struct symbol *sym;
+  value blocklen;
+  register char *copy = (char *) alloca (len + 1);
+  char *i = ptr;
+  register char *o = copy, *ibeg = ptr;
+  register int c;
+#ifdef KERNELDEBUG
+  extern int kernel_debugging;
+
+  if (kernel_debugging)
+    error("Can't stuff string constants into kernel (yet).");
+#endif
+
+  /* Copy the string into COPY, processing escapes.
+     We could not conveniently process them in expread
+     because the string there wants to be a substring of the input.  */
+
+  while (i - ibeg < len)
+    {
+      c = *i++;
+      if (c == '\\')
+       {
+         c = parse_escape (&i);
+         if (c == -1)
+           continue;
+       }
+      *o++ = c;
+    }
+  *o = 0;
+
+  /* Get the length of the string after escapes are processed.  */
+
+  len = o - copy;
+
+  /* Find the address of malloc in the inferior.  */
+
+  sym = lookup_symbol ("malloc", 0, VAR_NAMESPACE, 0);
+  if (sym != 0)
+    {
+      if (SYMBOL_CLASS (sym) != LOC_BLOCK)
+       error ("\"malloc\" exists in this program but is not a function.");
+      val = value_of_variable (sym);
+    }
+  else
+    {
+      register int i;
+      for (i = 0; i < misc_function_count; i++)
+       if (!strcmp (misc_function_vector[i].name, "malloc"))
+         break;
+      if (i < misc_function_count)
+       val = value_from_long (builtin_type_long,
+                            (LONGEST) misc_function_vector[i].address);
+      else
+       error ("String constants require the program to have a function \"malloc\".");
+    }
+
+  blocklen = value_from_long (builtin_type_int, (LONGEST) (len + 1));
+  val = call_function (val, 1, &blocklen);
+  if (value_zerop (val))
+    error ("No memory available for string constant.");
+  write_memory ((CORE_ADDR) value_as_long (val), copy, len + 1);
+  VALUE_TYPE (val) = lookup_pointer_type (builtin_type_char);
+  return val;
+}
+\f
+static int
+type_field_index(t, name)
+  register struct type *t;
+  register char *name;
+{
+  register int i;
+
+  for (i = TYPE_NFIELDS(t); --i >= 0;)
+    {
+      register char *t_field_name = TYPE_FIELD_NAME (t, i);
+
+      if (t_field_name && !strcmp (t_field_name, name))
+       break;
+    }
+  return (i);
+}
+
+/* Given ARG1, a value of type (pointer to a)* structure/union,
+   extract the component named NAME from the ultimate target structure/union
+   and return it as a value with its appropriate type.
+   ERR is used in the error message if ARG1's type is wrong.
+
+   C++: ARGS is a list of argument types to aid in the selection of
+   an appropriate method. Also, handle derived types.
+
+   STATIC_MEMFUNCP, if non-NULL, points to a caller-supplied location
+   where the truthvalue of whether the function that was resolved was
+   a static member function or not.
+
+   ERR is an error message to be printed in case the field is not found.  */
+
+value
+value_struct_elt (arg1, args, name, static_memfuncp, err)
+     register value arg1, *args;
+     char *name;
+     int *static_memfuncp;
+     char *err;
+{
+  register struct type *t;
+  register int i;
+  int found = 0;
+
+  struct type *baseclass;
+
+  COERCE_ARRAY (arg1);
+
+  t = VALUE_TYPE (arg1);
+
+  /* Check for the usual case: we have pointer, target type is a struct
+   * and `name' is a legal field of the struct.  In this case, we can
+   * just snarf the value of the field & not waste time while value_ind
+   * sucks over the entire struct. */
+  if (! args)
+    {
+      if (TYPE_CODE(t) == TYPE_CODE_PTR
+          && (TYPE_CODE((baseclass = TYPE_TARGET_TYPE(t))) == TYPE_CODE_STRUCT
+             || TYPE_CODE(baseclass) == TYPE_CODE_UNION)
+          && (i = type_field_index(baseclass, name)) >= 0)
+       {
+         register int offset;
+         register struct type *f = TYPE_FIELD_TYPE(baseclass, i);
+
+         offset = TYPE_FIELD_BITPOS(baseclass, i) >> 3;
+         if (TYPE_FIELD_BITSIZE(baseclass, i) == 0)
+           return value_at(f, (CORE_ADDR)(value_as_long(arg1) + offset));
+       }
+    }
+
+  /* Follow pointers until we get to a non-pointer.  */
+
+  while (TYPE_CODE (t) == TYPE_CODE_PTR || TYPE_CODE (t) == TYPE_CODE_REF)
+    {
+      arg1 = value_ind (arg1);
+      COERCE_ARRAY (arg1);
+      t = VALUE_TYPE (arg1);
+    }
+
+  if (TYPE_CODE (t) == TYPE_CODE_MEMBER)
+    error ("not implemented: member type in value_struct_elt");
+
+  if (TYPE_CODE (t) != TYPE_CODE_STRUCT
+      && TYPE_CODE (t) != TYPE_CODE_UNION)
+    error ("Attempt to extract a component of a value that is not a %s.", err);
+
+  baseclass = t;
+
+  /* Assume it's not, unless we see that it is.  */
+  if (static_memfuncp)
+    *static_memfuncp =0;
+
+  if (!args)
+    {
+      /* if there are no arguments ...do this...  */
+
+      /* Try as a variable first, because if we succeed, there
+        is less work to be done.  */
+      while (t)
+       {
+         i = type_field_index(t, name);
+         if (i >= 0)
+           return TYPE_FIELD_STATIC (t, i)
+             ? value_static_field (t, name, i) : value_field (arg1, i);
+
+         if (TYPE_N_BASECLASSES (t) == 0)
+           break;
+
+         t = TYPE_BASECLASS (t, 1);
+         VALUE_TYPE (arg1) = t; /* side effect! */
+       }
+
+      /* C++: If it was not found as a data field, then try to
+         return it as a pointer to a method.  */
+      t = baseclass;
+      VALUE_TYPE (arg1) = t;   /* side effect! */
+
+      if (destructor_name_p (name, t))
+       error ("use `info method' command to print out value of destructor");
+
+      while (t)
+       {
+         for (i = TYPE_NFN_FIELDS (t) - 1; i >= 0; --i)
+           {
+             if (! strcmp (TYPE_FN_FIELDLIST_NAME (t, i), name))
+               {
+                 error ("use `info method' command to print value of method \"%s\"", name);
+               }
+           }
+
+         if (TYPE_N_BASECLASSES (t) == 0)
+           break;
+
+         t = TYPE_BASECLASS (t, 1);
+       }
+
+      error ("There is no field named %s.", name);
+      return 0;
+    }
+
+  if (destructor_name_p (name, t))
+    {
+      if (!args[1])
+       {
+         /* destructors are a special case.  */
+         return (value)value_fn_field (arg1, 0,
+                                       TYPE_FN_FIELDLIST_LENGTH (t, 0));
+       }
+      else
+       {
+         error ("destructor should not have any argument");
+       }
+    }
+
+  /*   This following loop is for methods with arguments.  */
+  while (t)
+    {
+      /* Look up as method first, because that is where we
+        expect to find it first.  */
+      for (i = TYPE_NFN_FIELDS (t) - 1; i >= 0; i--)
+       {
+         struct fn_field *f = TYPE_FN_FIELDLIST1 (t, i);
+
+         if (!strcmp (TYPE_FN_FIELDLIST_NAME (t, i), name))
+           {
+             int j;
+             struct fn_field *f = TYPE_FN_FIELDLIST1 (t, i);
+
+             found = 1;
+             for (j = TYPE_FN_FIELDLIST_LENGTH (t, i) - 1; j >= 0; --j)
+               if (!typecmp (TYPE_FN_FIELD_STATIC_P (f, j),
+                             TYPE_FN_FIELD_ARGS (f, j), args))
+                 {
+                   if (TYPE_FN_FIELD_VIRTUAL_P (f, j))
+                     return (value)value_virtual_fn_field (arg1, f, j, t);
+                   if (TYPE_FN_FIELD_STATIC_P (f, j) && static_memfuncp)
+                     *static_memfuncp = 1;
+                   return (value)value_fn_field (arg1, i, j);
+                 }
+           }
+       }
+
+      if (TYPE_N_BASECLASSES (t) == 0)
+       break;
+      
+      t = TYPE_BASECLASS (t, 1);
+      VALUE_TYPE (arg1) = t;   /* side effect! */
+    }
+
+  if (found)
+    {
+      error ("Structure method %s not defined for arglist.", name);
+      return 0;
+    }
+  else
+    {
+      /* See if user tried to invoke data as function */
+      t = baseclass;
+      while (t)
+       {
+         i = type_field_index(t, name);
+         if (i >= 0)
+           return TYPE_FIELD_STATIC (t, i)
+             ? value_static_field (t, name, i) : value_field (arg1, i);
+
+         if (TYPE_N_BASECLASSES (t) == 0)
+           break;
+
+         t = TYPE_BASECLASS (t, 1);
+         VALUE_TYPE (arg1) = t; /* side effect! */
+       }
+      error ("Structure has no component named %s.", name);
+    }
+}
+
+/* C++: return 1 is NAME is a legitimate name for the destructor
+   of type TYPE.  If TYPE does not have a destructor, or
+   if NAME is inappropriate for TYPE, an error is signaled.  */
+int
+destructor_name_p (name, type)
+     char *name;
+     struct type *type;
+{
+  /* destructors are a special case.  */
+  char *dname = TYPE_NAME (type);
+
+  if (name[0] == '~')
+    {
+      if (! TYPE_HAS_DESTRUCTOR (type))
+       error ("type `%s' does not have destructor defined",
+              TYPE_NAME (type));
+      /* Skip past the "struct " at the front.  */
+      while (*dname++ != ' ') ;
+      if (strcmp (dname, name+1))
+       error ("destructor specification error");
+      else
+       return 1;
+    }
+  return 0;
+}
+
+/* C++: Given ARG1, a value of type (pointer to a)* structure/union,
+   return 1 if the component named NAME from the ultimate
+   target structure/union is defined, otherwise, return 0.  */
+
+int
+check_field (arg1, name)
+     register value arg1;
+     char *name;
+{
+  register struct type *t;
+  register int i;
+  int found = 0;
+
+  struct type *baseclass;
+
+  COERCE_ARRAY (arg1);
+
+  t = VALUE_TYPE (arg1);
+
+  /* Follow pointers until we get to a non-pointer.  */
+
+  while (TYPE_CODE (t) == TYPE_CODE_PTR || TYPE_CODE (t) == TYPE_CODE_REF)
+    t = TYPE_TARGET_TYPE (t);
+
+  if (TYPE_CODE (t) == TYPE_CODE_MEMBER)
+    error ("not implemented: member type in check_field");
+
+  if (TYPE_CODE (t) != TYPE_CODE_STRUCT
+      && TYPE_CODE (t) != TYPE_CODE_UNION)
+    error ("Internal error: `this' is not an aggregate");
+
+  baseclass = t;
+
+  while (t)
+    {
+      for (i = TYPE_NFIELDS (t) - 1; i >= 0; i--)
+       {
+         char *t_field_name = TYPE_FIELD_NAME (t, i);
+         if (t_field_name && !strcmp (t_field_name, name))
+                 goto success;
+       }
+      if (TYPE_N_BASECLASSES (t) == 0)
+       break;
+
+      t = TYPE_BASECLASS (t, 1);
+      VALUE_TYPE (arg1) = t;   /* side effect! */
+    }
+
+  /* C++: If it was not found as a data field, then try to
+     return it as a pointer to a method.  */
+  t = baseclass;
+
+  /* Destructors are a special case.  */
+  if (destructor_name_p (name, t))
+    goto success;
+
+  while (t)
+    {
+      for (i = TYPE_NFN_FIELDS (t) - 1; i >= 0; --i)
+       {
+         if (!strcmp (TYPE_FN_FIELDLIST_NAME (t, i), name))
+           return 1;
+       }
+
+      if (TYPE_N_BASECLASSES (t) == 0)
+       break;
+
+      t = TYPE_BASECLASS (t, 1);
+    }
+  return 0;
+
+ success:
+  t = VALUE_TYPE (arg1);
+  while (TYPE_CODE (t) == TYPE_CODE_PTR || TYPE_CODE (t) == TYPE_CODE_REF)
+    {
+      arg1 = value_ind (arg1);
+      COERCE_ARRAY (arg1);
+      t = VALUE_TYPE (arg1);
+    }
+}
+
+/* C++: Given an aggregate type DOMAIN, and a member name NAME,
+   return the address of this member as a pointer to member
+   type.  If INTYPE is non-null, then it will be the type
+   of the member we are looking for.  This will help us resolve
+   pointers to member functions.  */
+
+value
+value_struct_elt_for_address (domain, intype, name)
+     struct type *domain, *intype;
+     char *name;
+{
+  register struct type *t = domain;
+  register int i;
+  int found = 0;
+  value v;
+
+  struct type *baseclass;
+
+  if (TYPE_CODE (t) != TYPE_CODE_STRUCT
+      && TYPE_CODE (t) != TYPE_CODE_UNION)
+    error ("Internal error: non-aggregate type to value_struct_elt_for_address");
+
+  baseclass = t;
+
+  while (t)
+    {
+      for (i = TYPE_NFIELDS (t) - 1; i >= 0; i--)
+       {
+         char *t_field_name = TYPE_FIELD_NAME (t, i);
+         if (t_field_name && !strcmp (t_field_name, name))
+           {
+             if (TYPE_FIELD_PACKED (t, i))
+               error ("pointers to bitfield members not allowed");
+
+             v = value_from_long (builtin_type_int,
+                                  (LONGEST) (TYPE_FIELD_BITPOS (t, i) >> 3));
+             VALUE_TYPE (v) = lookup_pointer_type (
+                     lookup_member_type (TYPE_FIELD_TYPE (t, i), baseclass));
+             return v;
+           }
+       }
+
+      if (TYPE_N_BASECLASSES (t) == 0)
+       break;
+
+      t = TYPE_BASECLASS (t, 1);
+    }
+
+  /* C++: If it was not found as a data field, then try to
+     return it as a pointer to a method.  */
+  t = baseclass;
+
+  /* Destructors are a special case.  */
+  if (destructor_name_p (name, t))
+    {
+      error ("pointers to destructors not implemented yet");
+    }
+
+  /* Perform all necessary dereferencing.  */
+  while (intype && TYPE_CODE (intype) == TYPE_CODE_PTR)
+    intype = TYPE_TARGET_TYPE (intype);
+
+  while (t)
+    {
+      for (i = TYPE_NFN_FIELDS (t) - 1; i >= 0; --i)
+       {
+         if (!strcmp (TYPE_FN_FIELDLIST_NAME (t, i), name))
+           {
+             int j = TYPE_FN_FIELDLIST_LENGTH (t, i);
+             struct fn_field *f = TYPE_FN_FIELDLIST1 (t, i);
+
+             if (intype == 0 && j > 1)
+               error ("non-unique member `%s' requires type instantiation", name);
+             if (intype)
+               {
+                 while (j--)
+                   if (TYPE_FN_FIELD_TYPE (f, j) == intype)
+                     break;
+                 if (j < 0)
+                   error ("no member function matches that type instantiation");
+               }
+             else
+               j = 0;
+
+             if (TYPE_FN_FIELD_VIRTUAL_P (f, j))
+               {
+                 v = value_from_long (builtin_type_long,
+                                      (LONGEST) TYPE_FN_FIELD_VOFFSET (f, j));
+               }
+             else
+               {
+                 struct symbol *s = lookup_symbol (TYPE_FN_FIELD_PHYSNAME (f, j),
+                                                   0, VAR_NAMESPACE, 0);
+                 v = locate_var_value (s, 0);
+               }
+             VALUE_TYPE (v) = lookup_pointer_type (lookup_member_type (TYPE_FN_FIELD_TYPE (f, j), baseclass));
+             return v;
+           }
+       }
+
+      if (TYPE_N_BASECLASSES (t) == 0)
+       break;
+
+      t = TYPE_BASECLASS (t, 1);
+    }
+  return 0;
+}
+
+/* Compare two argument lists and return the position in which they differ,
+   or zero if equal.
+
+   STATICP is nonzero if the T1 argument list came from a
+   static member function.
+
+   For non-static member functions, we ignore the first argument,
+   which is the type of the instance variable.  This is because we want
+   to handle calls with objects from derived classes.  This is not
+   entirely correct: we should actually check to make sure that a
+   requested operation is type secure, shouldn't we?  */
+
+int
+typecmp (staticp, t1, t2)
+     int staticp;
+     struct type *t1[];
+     value t2[];
+{
+  int i;
+
+  if (staticp && t1 == 0)
+    return t2[1] != 0;
+  if (t1 == 0)
+    return 1;
+  if (t1[0]->code == TYPE_CODE_VOID) return 0;
+  if (t1[!staticp] == 0) return 0;
+  for (i = !staticp; t1[i] && t1[i]->code != TYPE_CODE_VOID; i++)
+    {
+      if (! t2[i]
+         || t1[i]->code != t2[i]->type->code
+         || t1[i]->target_type != t2[i]->type->target_type)
+       return i+1;
+    }
+  if (!t1[i]) return 0;
+  return t2[i] ? i+1 : 0;
+}
+
+/* C++: return the value of the class instance variable, if one exists.
+   Flag COMPLAIN signals an error if the request is made in an
+   inappropriate context.  */
+value
+value_of_this (complain)
+     int complain;
+{
+  extern FRAME selected_frame;
+  struct symbol *func, *sym;
+  char *funname = 0;
+  struct block *b;
+  int i;
+
+  if (selected_frame == 0)
+    if (complain)
+      error ("no frame selected");
+    else return 0;
+
+  func = get_frame_function (selected_frame);
+  if (func)
+    funname = SYMBOL_NAME (func);
+  else
+    if (complain)
+      error ("no `this' in nameless context");
+    else return 0;
+
+  b = SYMBOL_BLOCK_VALUE (func);
+  i = BLOCK_NSYMS (b);
+  if (i <= 0)
+    if (complain)
+      error ("no args, no `this'");
+    else return 0;
+
+  sym = BLOCK_SYM (b, 0);
+  if (strncmp ("$this", SYMBOL_NAME (sym), 5))
+    if (complain)
+      error ("current stack frame not in method");
+    else return 0;
+
+  return read_var_value (sym, selected_frame);
+}
diff --git a/usr/src/usr.bin/gdb/valprint.c b/usr/src/usr.bin/gdb/valprint.c
new file mode 100644 (file)
index 0000000..781eb29
--- /dev/null
@@ -0,0 +1,1430 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)valprint.c 6.5 (Berkeley) 5/8/91";
+#endif /* not lint */
+
+/* Print values for GNU debugger gdb.
+   Copyright (C) 1986, 1988, 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING.  If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#include <stdio.h>
+#include "defs.h"
+#include "param.h"
+#include "symtab.h"
+#include "value.h"
+
+/* GNU software is only expected to run on systems with 32-bit integers.  */
+#define UINT_MAX 0xffffffff
+
+/* Maximum number of chars to print for a string pointer value
+   or vector contents, or UINT_MAX for no limit.  */
+
+static unsigned int print_max;
+
+static void type_print_varspec_suffix ();
+static void type_print_varspec_prefix ();
+static void type_print_base ();
+static void type_print_method_args ();
+
+
+char **unsigned_type_table;
+char **signed_type_table;
+char **float_type_table;
+
+
+/* Print repeat counts if there are more than this
+   many repetitions of an element in an array.  */
+#define        REPEAT_COUNT_THRESHOLD  10
+\f
+/* Print the character string STRING, printing at most LENGTH characters.
+   Printing stops early if the number hits print_max; repeat counts
+   are printed as appropriate.  Print ellipses at the end if we
+   had to stop before printing LENGTH characters, or if FORCE_ELLIPSES.  */
+
+void
+print_string (stream, string, length, force_ellipses)
+     FILE *stream;
+     char *string;
+     unsigned int length;
+     int force_ellipses;
+{
+  register unsigned int i;
+  unsigned int things_printed = 0;
+  int in_quotes = 0;
+  int need_comma = 0;
+
+  if (length == 0)
+    {
+      fputs_filtered ("\"\"", stdout);
+      return;
+    }
+
+  for (i = 0; i < length && things_printed < print_max; ++i)
+    {
+      /* Position of the character we are examining
+        to see whether it is repeated.  */
+      unsigned int rep1;
+      /* Number of repititions we have detected so far.  */
+      unsigned int reps;
+
+      QUIT;
+
+      if (need_comma)
+       {
+         fputs_filtered (", ", stream);
+         need_comma = 0;
+       }
+
+      rep1 = i + 1;
+      reps = 1;
+      while (rep1 < length && string[rep1] == string[i])
+       {
+         ++rep1;
+         ++reps;
+       }
+
+      if (reps > REPEAT_COUNT_THRESHOLD)
+       {
+         if (in_quotes)
+           {
+             fputs_filtered ("\", ", stream);
+             in_quotes = 0;
+           }
+         fputs_filtered ("'", stream);
+         printchar (string[i], stream, '\'');
+         fprintf_filtered (stream, "' <repeats %u times>", reps);
+         i = rep1 - 1;
+         things_printed += REPEAT_COUNT_THRESHOLD;
+         need_comma = 1;
+       }
+      else
+       {
+         if (!in_quotes)
+           {
+             fputs_filtered ("\"", stream);
+             in_quotes = 1;
+           }
+         printchar (string[i], stream, '"');
+         ++things_printed;
+       }
+    }
+
+  /* Terminate the quotes if necessary.  */
+  if (in_quotes)
+    fputs_filtered ("\"", stream);
+
+  if (force_ellipses || i < length)
+    fputs_filtered ("...", stream);
+}
+\f
+/* Print the value VAL in C-ish syntax on stream STREAM.
+   FORMAT is a format-letter, or 0 for print in natural format of data type.
+   If the object printed is a string pointer, returns
+   the number of string bytes printed.  */
+
+int
+value_print (val, stream, format, pretty)
+     value val;
+     FILE *stream;
+     char format;
+     enum val_prettyprint pretty;
+{
+  register unsigned int i, n, typelen;
+
+  /* A "repeated" value really contains several values in a row.
+     They are made by the @ operator.
+     Print such values as if they were arrays.  */
+
+  if (VALUE_REPEATED (val))
+    {
+      n = VALUE_REPETITIONS (val);
+      typelen = TYPE_LENGTH (VALUE_TYPE (val));
+      fprintf_filtered (stream, "{");
+      /* Print arrays of characters using string syntax.  */
+      if (typelen == 1 && TYPE_CODE (VALUE_TYPE (val)) == TYPE_CODE_INT
+         && format == 0)
+       print_string (stream, VALUE_CONTENTS (val), n, 0);
+      else
+       {
+         unsigned int things_printed = 0;
+         
+         for (i = 0; i < n && things_printed < print_max; i++)
+           {
+             /* Position of the array element we are examining to see
+                whether it is repeated.  */
+             unsigned int rep1;
+             /* Number of repititions we have detected so far.  */
+             unsigned int reps;
+
+             if (i != 0)
+               fprintf_filtered (stream, ", ");
+
+             rep1 = i + 1;
+             reps = 1;
+             while (rep1 < n
+                    && !bcmp (VALUE_CONTENTS (val) + typelen * i,
+                              VALUE_CONTENTS (val) + typelen * rep1, typelen))
+               {
+                 ++reps;
+                 ++rep1;
+               }
+
+             if (reps > REPEAT_COUNT_THRESHOLD)
+               {
+                 val_print (VALUE_TYPE (val),
+                            VALUE_CONTENTS (val) + typelen * i,
+                            VALUE_ADDRESS (val) + typelen * i,
+                            stream, format, 1, 0, pretty);
+                 fprintf (stream, " <repeats %u times>", reps);
+                 i = rep1 - 1;
+                 things_printed += REPEAT_COUNT_THRESHOLD;
+               }
+             else
+               {
+                 val_print (VALUE_TYPE (val),
+                            VALUE_CONTENTS (val) + typelen * i,
+                            VALUE_ADDRESS (val) + typelen * i,
+                            stream, format, 1, 0, pretty);
+                 things_printed++;
+               }
+           }
+         if (i < n)
+           fprintf_filtered (stream, "...");
+       }
+      fprintf_filtered (stream, "}");
+      return n * typelen;
+    }
+  else
+    {
+      /* If it is a pointer, indicate what it points to.
+
+        Print type also if it is a reference.
+
+         C++: if it is a member pointer, we will take care
+        of that when we print it.  */
+      if (TYPE_CODE (VALUE_TYPE (val)) == TYPE_CODE_PTR
+         || TYPE_CODE (VALUE_TYPE (val)) == TYPE_CODE_REF)
+       {
+         fprintf_filtered (stream, "(");
+         type_print (VALUE_TYPE (val), "", stream, -1);
+         fprintf_filtered (stream, ") ");
+
+         /* If this is a function pointer, try to print what
+            function it is pointing to by name.  */
+         if (TYPE_CODE (TYPE_TARGET_TYPE (VALUE_TYPE (val)))
+             == TYPE_CODE_FUNC)
+           {
+             print_address (((int *) VALUE_CONTENTS (val))[0], stream);
+             /* Return value is irrelevant except for string pointers.  */
+             return 0;
+           }
+       }
+      return val_print (VALUE_TYPE (val), VALUE_CONTENTS (val),
+                       VALUE_ADDRESS (val), stream, format, 1, 0, pretty);
+    }
+}
+\f
+static int prettyprint;        /* Controls prettyprinting of structures.  */
+int unionprint;                /* Controls printing of nested unions.  */
+static void scalar_print_hack();
+void (*default_scalar_print)() = scalar_print_hack;
+
+/* Print data of type TYPE located at VALADDR (within GDB),
+   which came from the inferior at address ADDRESS,
+   onto stdio stream STREAM according to FORMAT
+   (a letter or 0 for natural format).
+
+   If the data are a string pointer, returns the number of
+   sting characters printed.
+
+   if DEREF_REF is nonzero, then dereference references,
+   otherwise just print them like pointers.
+
+   The PRETTY parameter controls prettyprinting.  */
+
+int
+val_print (type, valaddr, address, stream, format,
+          deref_ref, recurse, pretty)
+     struct type *type;
+     char *valaddr;
+     CORE_ADDR address;
+     FILE *stream;
+     char format;
+     int deref_ref;
+     int recurse;
+     enum val_prettyprint pretty;
+{
+  register unsigned int i;
+  int len, n_baseclasses;
+  struct type *elttype;
+  int eltlen;
+  LONGEST val;
+  unsigned char c;
+
+  if (pretty == Val_pretty_default)
+    {
+      pretty = prettyprint ? Val_prettyprint : Val_no_prettyprint;
+    }
+  
+  QUIT;
+
+  if (TYPE_FLAGS (type) & TYPE_FLAG_STUB)
+    {
+      fprintf_filtered (stream, "<Type not defined in this context>");
+      fflush (stream);
+      return 0;
+    }
+  
+  switch (TYPE_CODE (type))
+    {
+    case TYPE_CODE_ARRAY:
+      if (TYPE_LENGTH (type) >= 0
+         && TYPE_LENGTH (TYPE_TARGET_TYPE (type)) > 0)
+       {
+         elttype = TYPE_TARGET_TYPE (type);
+         eltlen = TYPE_LENGTH (elttype);
+         len = TYPE_LENGTH (type) / eltlen;
+         fprintf_filtered (stream, "{");
+         /* For an array of chars, print with string syntax.  */
+         if (eltlen == 1 && TYPE_CODE (elttype) == TYPE_CODE_INT
+             && format == 0)
+           print_string (stream, valaddr, len, 0);
+         else
+           {
+             unsigned int things_printed = 0;
+             
+             for (i = 0; i < len && things_printed < print_max; i++)
+               {
+                 /* Position of the array element we are examining to see
+                    whether it is repeated.  */
+                 unsigned int rep1;
+                 /* Number of repititions we have detected so far.  */
+                 unsigned int reps;
+                 
+                 if (i > 0)
+                   fprintf_filtered (stream, ", ");
+                 
+                 rep1 = i + 1;
+                 reps = 1;
+                 while (rep1 < len
+                        && !bcmp (valaddr + i * eltlen,
+                                  valaddr + rep1 * eltlen, eltlen))
+                   {
+                     ++reps;
+                     ++rep1;
+                   }
+
+                 if (reps > REPEAT_COUNT_THRESHOLD)
+                   {
+                     val_print (elttype, valaddr + i * eltlen,
+                                0, stream, format, deref_ref,
+                                recurse + 1, pretty);
+                     fprintf_filtered (stream, " <repeats %u times>", reps);
+                     i = rep1 - 1;
+                     things_printed += REPEAT_COUNT_THRESHOLD;
+                   }
+                 else
+                   {
+                     val_print (elttype, valaddr + i * eltlen,
+                                0, stream, format, deref_ref,
+                                recurse + 1, pretty);
+                     things_printed++;
+                   }
+               }
+             if (i < len)
+               fprintf_filtered (stream, "...");
+           }
+         fprintf_filtered (stream, "}");
+         break;
+       }
+      /* Array of unspecified length: treat like pointer to first elt.  */
+      valaddr = (char *) &address;
+
+    case TYPE_CODE_PTR:
+      if (format)
+       {
+         print_scalar_formatted (valaddr, type, format, 0, stream);
+         break;
+       }
+      if (TYPE_CODE (TYPE_TARGET_TYPE (type)) == TYPE_CODE_METHOD)
+       {
+         struct type *domain = TYPE_DOMAIN_TYPE (TYPE_TARGET_TYPE (type));
+         struct type *target = TYPE_TARGET_TYPE (TYPE_TARGET_TYPE (type));
+         struct fn_field *f;
+         int j, len2;
+         char *kind = "";
+
+         val = unpack_long (builtin_type_int, valaddr);
+         if (val < 128)
+           {
+             len = TYPE_NFN_FIELDS (domain);
+             for (i = 0; i < len; i++)
+               {
+                 f = TYPE_FN_FIELDLIST1 (domain, i);
+                 len2 = TYPE_FN_FIELDLIST_LENGTH (domain, i);
+
+                 for (j = 0; j < len2; j++)
+                   {
+                     QUIT;
+                     if (TYPE_FN_FIELD_VOFFSET (f, j) == val)
+                       {
+                         kind = "virtual";
+                         goto common;
+                       }
+                   }
+               }
+           }
+         else
+           {
+             struct symbol *sym = find_pc_function ((CORE_ADDR) val);
+             if (sym == 0)
+               error ("invalid pointer to member function");
+             len = TYPE_NFN_FIELDS (domain);
+             for (i = 0; i < len; i++)
+               {
+                 f = TYPE_FN_FIELDLIST1 (domain, i);
+                 len2 = TYPE_FN_FIELDLIST_LENGTH (domain, i);
+
+                 for (j = 0; j < len2; j++)
+                   {
+                     QUIT;
+                     if (!strcmp (SYMBOL_NAME (sym), TYPE_FN_FIELD_PHYSNAME (f, j)))
+                       goto common;
+                   }
+               }
+           }
+       common:
+         if (i < len)
+           {
+             fprintf_filtered (stream, "&");
+             type_print_varspec_prefix (TYPE_FN_FIELD_TYPE (f, j), stream, 0, 0);
+             fprintf (stream, kind);
+             if (TYPE_FN_FIELD_PHYSNAME (f, j)[0] == '_'
+                 && TYPE_FN_FIELD_PHYSNAME (f, j)[1] == '$')
+               type_print_method_args
+                 (TYPE_FN_FIELD_ARGS (f, j) + 1, "~",
+                  TYPE_FN_FIELDLIST_NAME (domain, i), 0, stream);
+             else
+               type_print_method_args
+                 (TYPE_FN_FIELD_ARGS (f, j), "",
+                  TYPE_FN_FIELDLIST_NAME (domain, i), 0, stream);
+             break;
+           }
+         fprintf_filtered (stream, "(");
+         type_print (type, "", stream, -1);
+         fprintf_filtered (stream, ") %d", (int) val >> 3);
+       }
+      else if (TYPE_CODE (TYPE_TARGET_TYPE (type)) == TYPE_CODE_MEMBER)
+       {
+         struct type *domain = TYPE_DOMAIN_TYPE (TYPE_TARGET_TYPE (type));
+         struct type *target = TYPE_TARGET_TYPE (TYPE_TARGET_TYPE (type));
+         char *kind = "";
+
+         /* VAL is a byte offset into the structure type DOMAIN.
+            Find the name of the field for that offset and
+            print it.  */
+         int extra = 0;
+         int bits = 0;
+         len = TYPE_NFIELDS (domain);
+         /* @@ Make VAL into bit offset */
+         val = unpack_long (builtin_type_int, valaddr) << 3;
+         for (i = 0; i < len; i++)
+           {
+             int bitpos = TYPE_FIELD_BITPOS (domain, i);
+             QUIT;
+             if (val == bitpos)
+               break;
+             if (val < bitpos && i > 0)
+               {
+                 int ptrsize = (TYPE_LENGTH (builtin_type_char) * TYPE_LENGTH (target));
+                 /* Somehow pointing into a field.  */
+                 i -= 1;
+                 extra = (val - TYPE_FIELD_BITPOS (domain, i));
+                 if (extra & 0x3)
+                   bits = 1;
+                 else
+                   extra >>= 3;
+                 break;
+               }
+           }
+         if (i < len)
+           {
+             fprintf_filtered (stream, "&");
+             type_print_base (domain, stream, 0, 0);
+             fprintf_filtered (stream, "::");
+             fputs_filtered (TYPE_FIELD_NAME (domain, i), stream);
+             if (extra)
+               fprintf_filtered (stream, " + %d bytes", extra);
+             if (bits)
+               fprintf_filtered (stream, " (offset in bits)");
+             break;
+           }
+         fprintf_filtered (stream, "%d", val >> 3);
+       }
+      else
+       {
+         fprintf_filtered (stream, "0x%x", * (int *) valaddr);
+         /* For a pointer to char or unsigned char,
+            also print the string pointed to, unless pointer is null.  */
+         
+         /* For an array of chars, print with string syntax.  */
+         elttype = TYPE_TARGET_TYPE (type);
+         i = 0;                /* Number of characters printed.  */
+         if (TYPE_LENGTH (elttype) == 1 
+             && TYPE_CODE (elttype) == TYPE_CODE_INT
+             && format == 0
+             && unpack_long (type, valaddr) != 0
+             /* If print_max is UINT_MAX, the alloca below will fail.
+                In that case don't try to print the string.  */
+             && print_max < UINT_MAX)
+           {
+             fprintf_filtered (stream, " ");
+
+             /* Get first character.  */
+             if (read_memory ( (CORE_ADDR) unpack_long (type, valaddr),
+                              &c, 1))
+               {
+                 /* First address out of bounds.  */
+                 fprintf_filtered (stream, "<Address 0x%x out of bounds>",
+                          (* (int *) valaddr));
+                 break;
+               }
+             else
+               {
+                 /* A real string.  */
+                 int out_of_bounds = 0;
+                 char *string = (char *) alloca (print_max);
+
+                 /* If the loop ends by us hitting print_max characters,
+                    we need to have elipses at the end.  */
+                 int force_ellipses = 1;
+
+                 /* This loop only fetches print_max characters, even
+                    though print_string might want to print more
+                    (with repeated characters).  This is so that
+                    we don't spend forever fetching if we print
+                    a long string consisting of the same character
+                    repeated.  */
+                 while (i < print_max)
+                   {
+                     QUIT;
+                     if (read_memory ((CORE_ADDR) unpack_long (type, valaddr)
+                                      + i, &c, 1))
+                       {
+                         out_of_bounds = 1;
+                         force_ellipses = 0;
+                         break;
+                       }
+                     else if (c == '\0')
+                       {
+                         force_ellipses = 0;
+                         break;
+                       }
+                     else
+                       string[i++] = c;
+                   }
+
+                 if (i != 0)
+                   print_string (stream, string, i, force_ellipses);
+                 if (out_of_bounds)
+                   fprintf_filtered (stream,
+                                     " <Address 0x%x out of bounds>",
+                                     (*(int *) valaddr) + i);
+               }
+
+             fflush (stream);
+           }
+         /* Return number of characters printed, plus one for the
+            terminating null if we have "reached the end".  */
+         return i + (print_max && i != print_max);
+       }
+      break;
+
+    case TYPE_CODE_MEMBER:
+      error ("not implemented: member type in val_print");
+      break;
+
+    case TYPE_CODE_REF:
+      fprintf_filtered (stream, "(0x%x &) = ", * (int *) valaddr);
+      /* De-reference the reference.  */
+      if (deref_ref)
+       {
+         if (TYPE_CODE (TYPE_TARGET_TYPE (type)) != TYPE_CODE_UNDEF)
+           {
+             value val = value_at (TYPE_TARGET_TYPE (type), * (int *) valaddr);
+             val_print (VALUE_TYPE (val), VALUE_CONTENTS (val),
+                        VALUE_ADDRESS (val), stream, format,
+                        deref_ref, recurse + 1, pretty);
+           }
+         else
+           fprintf_filtered (stream, "???");
+       }
+      break;
+
+    case TYPE_CODE_UNION:
+      if (recurse && !unionprint)
+       {
+         fprintf_filtered (stream, "{...}");
+         break;
+       }
+      /* Fall through.  */
+    case TYPE_CODE_STRUCT:
+      fprintf_filtered (stream, "{");
+      len = TYPE_NFIELDS (type);
+      n_baseclasses = TYPE_N_BASECLASSES (type);
+      for (i = 1; i <= n_baseclasses; i++)
+       {
+         fprintf_filtered (stream, "\n");
+         if (pretty)
+           print_spaces_filtered (2 + 2 * recurse, stream);
+         fputs_filtered ("<", stream);
+         fputs_filtered (TYPE_NAME (TYPE_BASECLASS (type, i)), stream);
+         fputs_filtered ("> = ", stream);
+         val_print (TYPE_FIELD_TYPE (type, 0),
+                    valaddr + TYPE_FIELD_BITPOS (type, i-1) / 8,
+                    0, stream, 0, 0, recurse + 1, pretty);
+       }
+      if (i > 1) {
+       fprintf_filtered (stream, "\n");
+       print_spaces_filtered (2 + 2 * recurse, stream);
+       fputs_filtered ("members of ", stream);
+        fputs_filtered (TYPE_NAME (type), stream);
+        fputs_filtered (": ", stream);
+      }
+      if (!len && i == 1)
+       fprintf_filtered (stream, "<No data fields>");
+      else
+       {
+         for (i -= 1; i < len; i++)
+           {
+             if (i > n_baseclasses) fprintf_filtered (stream, ", ");
+             if (pretty)
+               {
+                 fprintf_filtered (stream, "\n");
+                 print_spaces_filtered (2 + 2 * recurse, stream);
+               }
+             fputs_filtered (TYPE_FIELD_NAME (type, i), stream);
+             fputs_filtered (" = ", stream);
+             /* check if static field */
+             if (TYPE_FIELD_STATIC (type, i))
+               {
+                 value v;
+                 
+                 v = value_static_field (type, TYPE_FIELD_NAME (type, i), i);
+                 val_print (TYPE_FIELD_TYPE (type, i),
+                            VALUE_CONTENTS (v), 0, stream, format,
+                            deref_ref, recurse + 1, pretty);
+               }
+             else if (TYPE_FIELD_PACKED (type, i))
+               {
+                 char *valp = (char *) & val;
+                 union {int i; char c;} test;
+                 test.i = 1;
+                 if (test.c != 1)
+                   valp += sizeof val - TYPE_LENGTH (TYPE_FIELD_TYPE (type, i));
+                 val = unpack_field_as_long (type, valaddr, i);
+                 val_print (TYPE_FIELD_TYPE (type, i), valp, 0,
+                            stream, format, deref_ref, recurse + 1, pretty);
+               }
+             else
+               {
+                 val_print (TYPE_FIELD_TYPE (type, i), 
+                            valaddr + TYPE_FIELD_BITPOS (type, i) / 8,
+                            0, stream, format, deref_ref,
+                            recurse + 1, pretty);
+               }
+           }
+         if (pretty)
+           {
+             fprintf_filtered (stream, "\n");
+             print_spaces_filtered (2 * recurse, stream);
+           }
+       }
+      fprintf_filtered (stream, "}");
+      break;
+
+    case TYPE_CODE_ENUM:
+      if (format)
+       {
+         print_scalar_formatted (valaddr, type, format, 0, stream);
+         break;
+       }
+      len = TYPE_NFIELDS (type);
+      val = unpack_long (builtin_type_int, valaddr);
+      for (i = 0; i < len; i++)
+       {
+         QUIT;
+         if (val == TYPE_FIELD_BITPOS (type, i))
+           break;
+       }
+      if (i < len)
+       fputs_filtered (TYPE_FIELD_NAME (type, i), stream);
+      else
+       fprintf_filtered (stream, "%d", (int) val);
+      break;
+
+    case TYPE_CODE_FUNC:
+      if (format)
+       {
+         print_scalar_formatted (valaddr, type, format, 0, stream);
+         break;
+       }
+      fprintf_filtered (stream, "{");
+      type_print (type, "", stream, -1);
+      fprintf_filtered (stream, "} ");
+      fprintf_filtered (stream, "0x%x", address);
+      break;
+
+    case TYPE_CODE_INT:
+      if (format)
+       print_scalar_formatted (valaddr, type, format, 0, stream);
+      else
+       {
+         (*default_scalar_print)(stream, type, unpack_long(type, valaddr));
+#ifdef notdef
+         if (TYPE_LENGTH (type) == 1)
+           {
+             fprintf_filtered (stream, " '");
+             printchar ((unsigned char) unpack_long (type, valaddr), 
+                        stream, '\'');
+             fprintf_filtered (stream, "'");
+           }
+#endif
+       }
+      break;
+
+    case TYPE_CODE_FLT:
+      if (format)
+       print_scalar_formatted (valaddr, type, format, 0, stream);
+      else
+       print_floating (valaddr, type, stream);
+      break;
+
+    case TYPE_CODE_VOID:
+      fprintf_filtered (stream, "void");
+      break;
+
+    default:
+      error ("Invalid type code in symbol table.");
+    }
+  fflush (stream);
+  return 0;
+}
+\f
+/* Print a description of a type TYPE
+   in the form of a declaration of a variable named VARSTRING.
+   Output goes to STREAM (via stdio).
+   If SHOW is positive, we show the contents of the outermost level
+   of structure even if there is a type name that could be used instead.
+   If SHOW is negative, we never show the details of elements' types.  */
+
+void
+type_print (type, varstring, stream, show)
+     struct type *type;
+     char *varstring;
+     FILE *stream;
+     int show;
+{
+  type_print_1 (type, varstring, stream, show, 0);
+}
+
+/* LEVEL is the depth to indent lines by.  */
+
+void
+type_print_1 (type, varstring, stream, show, level)
+     struct type *type;
+     char *varstring;
+     FILE *stream;
+     int show;
+     int level;
+{
+  register enum type_code code;
+  type_print_base (type, stream, show, level);
+  code = TYPE_CODE (type);
+  if ((varstring && *varstring)
+      ||
+      /* Need a space if going to print stars or brackets;
+        but not if we will print just a type name.  */
+      ((show > 0 || TYPE_NAME (type) == 0)
+       &&
+       (code == TYPE_CODE_PTR || code == TYPE_CODE_FUNC
+       || code == TYPE_CODE_METHOD
+       || code == TYPE_CODE_ARRAY
+       || code == TYPE_CODE_MEMBER
+       || code == TYPE_CODE_REF)))
+    fprintf_filtered (stream, " ");
+  type_print_varspec_prefix (type, stream, show, 0);
+  fputs_filtered (varstring, stream);
+  type_print_varspec_suffix (type, stream, show, 0);
+}
+
+/* Print the method arguments ARGS to the file STREAM.  */
+static void
+type_print_method_args (args, prefix, varstring, staticp, stream)
+     struct type **args;
+     char *prefix, *varstring;
+     int staticp;
+     FILE *stream;
+{
+  int i;
+
+  fputs_filtered (" ", stream);
+  fputs_filtered (prefix, stream);
+  fputs_filtered (varstring, stream);
+  fputs_filtered (" (", stream);
+  if (args && args[!staticp] && args[!staticp]->code != TYPE_CODE_VOID)
+    {
+      i = !staticp;            /* skip the class variable */
+      while (1)
+       {
+         type_print (args[i++], "", stream, 0);
+         if (!args[i]) 
+           {
+             fprintf_filtered (stream, " ...");
+             break;
+           }
+         else if (args[i]->code != TYPE_CODE_VOID)
+           {
+             fprintf_filtered (stream, ", ");
+           }
+         else break;
+       }
+    }
+  fprintf_filtered (stream, ")");
+}
+  
+/* If TYPE is a derived type, then print out derivation
+   information.  Print out all layers of the type heirarchy
+   until we encounter one with multiple inheritance.
+   At that point, print out that ply, and return.  */
+static void
+type_print_derivation_info (stream, type)
+     FILE *stream;
+     struct type *type;
+{
+  char *name;
+  int i, n_baseclasses = TYPE_N_BASECLASSES (type);
+  struct type *basetype = 0;
+
+  while (type && n_baseclasses == 1)
+    {
+      basetype = TYPE_BASECLASS (type, 1);
+      if (TYPE_NAME (basetype) && (name = TYPE_NAME (basetype)))
+       {
+         while (*name != ' ') name++;
+         fprintf_filtered (stream, ": %s%s ",
+                  TYPE_VIA_PUBLIC (basetype) ? "public" : "private",
+                  TYPE_VIA_VIRTUAL (basetype) ? " virtual" : "");
+         fputs_filtered (name + 1, stream);
+         fputs_filtered (" ", stream);
+       }
+      n_baseclasses = TYPE_N_BASECLASSES (basetype);
+      type = basetype;
+    }
+
+  if (type)
+    {
+      if (n_baseclasses != 0)
+       fprintf_filtered (stream, ": ");
+      for (i = 1; i <= n_baseclasses; i++)
+       {
+         basetype = TYPE_BASECLASS (type, i);
+         if (TYPE_NAME (basetype) && (name = TYPE_NAME (basetype)))
+           {
+             while (*name != ' ') name++;
+             fprintf_filtered (stream, "%s%s ",
+                      TYPE_VIA_PUBLIC (basetype) ? "public" : "private",
+                      TYPE_VIA_VIRTUAL (basetype) ? " virtual" : "");
+             fputs_filtered (name + 1, stream);
+           }
+         if (i < n_baseclasses)
+           fprintf_filtered (stream, ", ");
+       }
+      fprintf_filtered (stream, " ");
+    }
+}
+
+/* Print any asterisks or open-parentheses needed before the
+   variable name (to describe its type).
+
+   On outermost call, pass 0 for PASSED_A_PTR.
+   On outermost call, SHOW > 0 means should ignore
+   any typename for TYPE and show its details.
+   SHOW is always zero on recursive calls.  */
+
+static void
+type_print_varspec_prefix (type, stream, show, passed_a_ptr)
+     struct type *type;
+     FILE *stream;
+     int show;
+     int passed_a_ptr;
+{
+  if (type == 0)
+    return;
+
+  if (TYPE_NAME (type) && show <= 0)
+    return;
+
+  QUIT;
+
+  switch (TYPE_CODE (type))
+    {
+    case TYPE_CODE_PTR:
+      type_print_varspec_prefix (TYPE_TARGET_TYPE (type), stream, 0, 1);
+      fprintf_filtered (stream, "*");
+      break;
+
+    case TYPE_CODE_MEMBER:
+      if (passed_a_ptr)
+       fprintf_filtered (stream, "(");
+      type_print_varspec_prefix (TYPE_TARGET_TYPE (type), stream, 0,
+                                0);
+      fprintf_filtered (stream, " ");
+      type_print_base (TYPE_DOMAIN_TYPE (type), stream, 0,
+                      passed_a_ptr);
+      fprintf_filtered (stream, "::");
+      break;
+
+    case TYPE_CODE_METHOD:
+      if (passed_a_ptr)
+       fprintf (stream, "(");
+      type_print_varspec_prefix (TYPE_TARGET_TYPE (type), stream, 0,
+                                0);
+      fprintf_filtered (stream, " ");
+      type_print_base (TYPE_DOMAIN_TYPE (type), stream, 0,
+                      passed_a_ptr);
+      fprintf_filtered (stream, "::");
+      break;
+
+    case TYPE_CODE_REF:
+      type_print_varspec_prefix (TYPE_TARGET_TYPE (type), stream, 0, 1);
+      fprintf_filtered (stream, "&");
+      break;
+
+    case TYPE_CODE_FUNC:
+      type_print_varspec_prefix (TYPE_TARGET_TYPE (type), stream, 0,
+                                0);
+      if (passed_a_ptr)
+       fprintf_filtered (stream, "(");
+      break;
+
+    case TYPE_CODE_ARRAY:
+      type_print_varspec_prefix (TYPE_TARGET_TYPE (type), stream, 0,
+                                0);
+      if (passed_a_ptr)
+       fprintf_filtered (stream, "(");
+    }
+}
+
+/* Print any array sizes, function arguments or close parentheses
+   needed after the variable name (to describe its type).
+   Args work like type_print_varspec_prefix.  */
+
+static void
+type_print_varspec_suffix (type, stream, show, passed_a_ptr)
+     struct type *type;
+     FILE *stream;
+     int show;
+     int passed_a_ptr;
+{
+  if (type == 0)
+    return;
+
+  if (TYPE_NAME (type) && show <= 0)
+    return;
+
+  QUIT;
+
+  switch (TYPE_CODE (type))
+    {
+    case TYPE_CODE_ARRAY:
+      if (passed_a_ptr)
+       fprintf_filtered (stream, ")");
+      
+      fprintf_filtered (stream, "[");
+      if (TYPE_LENGTH (type) >= 0
+         && TYPE_LENGTH (TYPE_TARGET_TYPE (type)) > 0)
+       fprintf_filtered (stream, "%d",
+                         (TYPE_LENGTH (type)
+                          / TYPE_LENGTH (TYPE_TARGET_TYPE (type))));
+      fprintf_filtered (stream, "]");
+      
+      type_print_varspec_suffix (TYPE_TARGET_TYPE (type), stream, 0,
+                                0);
+      break;
+
+    case TYPE_CODE_MEMBER:
+      if (passed_a_ptr)
+       fprintf_filtered (stream, ")");
+      type_print_varspec_suffix (TYPE_TARGET_TYPE (type), stream, 0, 0);
+      break;
+
+    case TYPE_CODE_METHOD:
+      if (passed_a_ptr)
+       fprintf_filtered (stream, ")");
+      type_print_varspec_suffix (TYPE_TARGET_TYPE (type), stream, 0, 0);
+      if (passed_a_ptr)
+       {
+         int i;
+         struct type **args = TYPE_ARG_TYPES (type);
+
+         fprintf_filtered (stream, "(");
+         if (args[1] == 0)
+           fprintf_filtered (stream, "...");
+         else for (i = 1; args[i] != 0 && args[i]->code != TYPE_CODE_VOID; i++)
+           {
+             type_print_1 (args[i], "", stream, -1, 0);
+             if (args[i+1] == 0)
+               fprintf_filtered (stream, "...");
+             else if (args[i+1]->code != TYPE_CODE_VOID)
+               fprintf_filtered (stream, ",");
+           }
+         fprintf_filtered (stream, ")");
+       }
+      break;
+
+    case TYPE_CODE_PTR:
+    case TYPE_CODE_REF:
+      type_print_varspec_suffix (TYPE_TARGET_TYPE (type), stream, 0, 1);
+      break;
+
+    case TYPE_CODE_FUNC:
+      type_print_varspec_suffix (TYPE_TARGET_TYPE (type), stream, 0,
+                                passed_a_ptr);
+      if (passed_a_ptr)
+       fprintf_filtered (stream, ")");
+      fprintf_filtered (stream, "()");
+      break;
+    }
+}
+
+/* Print the name of the type (or the ultimate pointer target,
+   function value or array element), or the description of a
+   structure or union.
+
+   SHOW nonzero means don't print this type as just its name;
+   show its real definition even if it has a name.
+   SHOW zero means print just typename or struct tag if there is one
+   SHOW negative means abbreviate structure elements.
+   SHOW is decremented for printing of structure elements.
+
+   LEVEL is the depth to indent by.
+   We increase it for some recursive calls.  */
+
+static void
+type_print_base (type, stream, show, level)
+     struct type *type;
+     FILE *stream;
+     int show;
+     int level;
+{
+  char *name;
+  register int i;
+  register int len;
+  register int lastval;
+
+  QUIT;
+
+  if (type == 0)
+    {
+      fprintf_filtered (stream, "type unknown");
+      return;
+    }
+
+  if (TYPE_NAME (type) && show <= 0)
+    {
+      fputs_filtered (TYPE_NAME (type), stream);
+      return;
+    }
+
+  switch (TYPE_CODE (type))
+    {
+    case TYPE_CODE_ARRAY:
+    case TYPE_CODE_PTR:
+    case TYPE_CODE_MEMBER:
+    case TYPE_CODE_REF:
+    case TYPE_CODE_FUNC:
+    case TYPE_CODE_METHOD:
+      type_print_base (TYPE_TARGET_TYPE (type), stream, show, level);
+      break;
+
+    case TYPE_CODE_STRUCT:
+      fprintf_filtered (stream, "struct ");
+      goto struct_union;
+
+    case TYPE_CODE_UNION:
+      fprintf_filtered (stream, "union ");
+    struct_union:
+      if (TYPE_NAME (type) && (name = TYPE_NAME (type)))
+       {
+         while (*name != ' ') name++;
+         fputs_filtered (name + 1, stream);
+         fputs_filtered (" ", stream);
+       }
+      if (show < 0)
+       fprintf_filtered (stream, "{...}");
+      else
+       {
+         int i;
+
+         type_print_derivation_info (stream, type);
+         
+         fprintf_filtered (stream, "{");
+         len = TYPE_NFIELDS (type);
+         if (len)
+           fprintf_filtered (stream, "\n");
+         else
+           {
+             if (TYPE_FLAGS (type) & TYPE_FLAG_STUB)
+               fprintf_filtered (stream, "<incomplete type>\n");
+             else
+               fprintf_filtered (stream, "<no data fields>\n");
+           }
+
+         /* If there is a base class for this type,
+            do not print the field that it occupies.  */
+         for (i = TYPE_N_BASECLASSES (type); i < len; i++)
+           {
+             QUIT;
+             /* Don't print out virtual function table.  */
+             if (! strncmp (TYPE_FIELD_NAME (type, i),
+                          "_vptr$", 6))
+               continue;
+
+             print_spaces_filtered (level + 4, stream);
+             if (TYPE_FIELD_STATIC (type, i))
+               {
+                 fprintf_filtered (stream, "static ");
+               }
+             type_print_1 (TYPE_FIELD_TYPE (type, i),
+                           TYPE_FIELD_NAME (type, i),
+                           stream, show - 1, level + 4);
+             if (!TYPE_FIELD_STATIC (type, i)
+                 && TYPE_FIELD_PACKED (type, i))
+               {
+                 /* It is a bitfield.  This code does not attempt
+                    to look at the bitpos and reconstruct filler,
+                    unnamed fields.  This would lead to misleading
+                    results if the compiler does not put out fields
+                    for such things (I don't know what it does).  */
+                 fprintf_filtered (stream, " : %d",
+                                   TYPE_FIELD_BITSIZE (type, i));
+               }
+             fprintf_filtered (stream, ";\n");
+           }
+
+         /* C++: print out the methods */
+         len = TYPE_NFN_FIELDS (type);
+         if (len) fprintf_filtered (stream, "\n");
+         for (i = 0; i < len; i++)
+           {
+             struct fn_field *f = TYPE_FN_FIELDLIST1 (type, i);
+             int j, len2 = TYPE_FN_FIELDLIST_LENGTH (type, i);
+
+             for (j = 0; j < len2; j++)
+               {
+                 QUIT;
+                 print_spaces_filtered (level + 4, stream);
+                 if (TYPE_FN_FIELD_VIRTUAL_P (f, j))
+                   fprintf_filtered (stream, "virtual ");
+                 else if (TYPE_FN_FIELD_STATIC_P (f, j))
+                   fprintf_filtered (stream, "static ");
+                 type_print (TYPE_TARGET_TYPE (TYPE_FN_FIELD_TYPE (f, j)), "", stream, 0);
+                 if (TYPE_FN_FIELD_PHYSNAME (f, j)[0] == '_'
+                     && TYPE_FN_FIELD_PHYSNAME (f, j)[1] == '$')
+                   type_print_method_args
+                     (TYPE_FN_FIELD_ARGS (f, j) + 1, "~",
+                      TYPE_FN_FIELDLIST_NAME (type, i), 0, stream);
+                 else
+                   type_print_method_args
+                     (TYPE_FN_FIELD_ARGS (f, j), "",
+                      TYPE_FN_FIELDLIST_NAME (type, i),
+                      TYPE_FN_FIELD_STATIC_P (f, j), stream);
+
+                 fprintf_filtered (stream, ";\n");
+               }
+             if (len2) fprintf_filtered (stream, "\n");
+           }
+
+         print_spaces_filtered (level, stream);
+         fprintf_filtered (stream, "}");
+       }
+      break;
+
+    case TYPE_CODE_ENUM:
+      fprintf_filtered (stream, "enum ");
+      if (TYPE_NAME (type))
+       {
+         name = TYPE_NAME (type);
+         while (*name != ' ') name++;
+         fputs_filtered (name + 1, stream);
+         fputs_filtered (" ", stream);
+       }
+      if (show < 0)
+       fprintf_filtered (stream, "{...}");
+      else
+       {
+         fprintf_filtered (stream, "{");
+         len = TYPE_NFIELDS (type);
+         lastval = 0;
+         for (i = 0; i < len; i++)
+           {
+             QUIT;
+             if (i) fprintf_filtered (stream, ", ");
+             fputs_filtered (TYPE_FIELD_NAME (type, i), stream);
+             if (lastval != TYPE_FIELD_BITPOS (type, i))
+               {
+                 fprintf_filtered (stream, " : %d", TYPE_FIELD_BITPOS (type, i));
+                 lastval = TYPE_FIELD_BITPOS (type, i);
+               }
+             lastval++;
+           }
+         fprintf_filtered (stream, "}");
+       }
+      break;
+
+    case TYPE_CODE_INT:
+      if (TYPE_UNSIGNED (type))
+       name = unsigned_type_table[TYPE_LENGTH (type)];
+      else
+       name = signed_type_table[TYPE_LENGTH (type)];
+      fputs_filtered (name, stream);
+      break;
+
+    case TYPE_CODE_FLT:
+      name = float_type_table[TYPE_LENGTH (type)];
+      fputs_filtered (name, stream);
+      break;
+
+    case TYPE_CODE_VOID:
+      fprintf_filtered (stream, "void");
+      break;
+
+    case 0:
+      fprintf_filtered (stream, "struct unknown");
+      break;
+
+    default:
+      error ("Invalid type code in symbol table.");
+    }
+}
+
+static void
+scalar_print_decimal(stream, type, val)
+       FILE *stream;
+       struct type *type;
+       LONGEST val;
+{
+       fprintf_filtered(stream, TYPE_UNSIGNED(type)? "%lu":"%ld", val);
+}
+
+static void
+scalar_print_hex(stream, type, val)
+       FILE *stream;
+       struct type *type;
+       LONGEST val;
+{
+       switch (TYPE_LENGTH(type)) {
+       case 1:
+               fprintf_filtered (stream, "0x%02lx", val);
+               break;
+       case 2:
+               fprintf_filtered (stream, "0x%04lx", val);
+               break;
+       case 4:
+               fprintf_filtered (stream, "0x%08lx", val);
+               break;
+       default:
+               fprintf_filtered (stream, "0x%lx", val);
+               break;
+       }
+}
+
+static void
+scalar_print_octal(stream, type, val)
+       FILE *stream;
+       struct type *type;
+       LONGEST val;
+{
+       switch (TYPE_LENGTH(type)) {
+       case 1:
+               fprintf_filtered (stream, "0%03lo", val);
+               break;
+       case 2:
+               fprintf_filtered (stream, "0%06lo", val);
+               break;
+       case 4:
+               fprintf_filtered (stream, "0%012lo", val);
+               break;
+       default:
+               fprintf_filtered (stream, "0%lo", val);
+               break;
+       }
+}
+
+static void
+scalar_print_hack(stream, type, val)
+       FILE *stream;
+       struct type *type;
+       LONGEST val;
+{
+       if (TYPE_UNSIGNED(type))
+               scalar_print_hex(stream, type, val);
+       else
+               scalar_print_decimal(stream, type, val);
+}
+\f
+static void
+set_maximum_command (arg)
+     char *arg;
+{
+  if (!arg) error_no_arg ("value for maximum elements to print");
+  print_max = parse_and_eval_address (arg);
+  if (print_max == 0)
+    print_max = UINT_MAX;
+}
+
+static void
+set_base_command(arg)
+     char *arg;
+{
+       int base;
+
+       if (!arg)
+               base = 0;
+       else
+               base = parse_and_eval_address (arg);
+       switch (base) {
+       default:
+               default_scalar_print = scalar_print_hack;
+               break;
+       case 8:
+               default_scalar_print = scalar_print_octal;
+               break;
+       case 10:
+               default_scalar_print = scalar_print_decimal;
+               break;
+       case 16:
+               default_scalar_print = scalar_print_hex;
+               break;
+       }
+}
+
+static void
+set_prettyprint_command (arg, from_tty)
+     char *arg;
+     int from_tty;
+{
+  prettyprint = parse_binary_operation ("set prettyprint", arg);
+}
+
+static void
+set_unionprint_command (arg, from_tty)
+     char *arg;
+     int from_tty;
+{
+  unionprint = parse_binary_operation ("set unionprint", arg);
+}
+
+format_info (arg, from_tty)
+     char *arg;
+     int from_tty;
+{
+  if (arg)
+    error ("\"info format\" does not take any arguments.");
+  printf ("Prettyprinting of structures is %s.\n",
+         prettyprint ? "on" : "off");
+  printf ("Printing of unions interior to structures is %s.\n",
+         unionprint ? "on" : "off");
+  if (print_max == UINT_MAX)
+    printf_filtered
+      ("There is no maximum number of array elements printed.\n");
+  else
+    printf_filtered
+      ("The maximum number of array elements printed is %d.\n", print_max);
+}
+
+extern struct cmd_list_element *setlist;
+
+void
+_initialize_valprint ()
+{
+  add_cmd ("base", class_support, set_base_command,
+          "Change default integer print radix to 8, 10 or 16\n\
+No args returns to the ad-hoc default of `16' for unsigned values\n\
+and `10' otherwise.",
+          &setlist);
+  add_cmd ("array-max", class_vars, set_maximum_command,
+          "Set NUMBER as limit on string chars or array elements to print.\n\
+\"set array-max 0\" causes there to be no limit.",
+          &setlist);
+
+  add_cmd ("prettyprint", class_support, set_prettyprint_command,
+          "Turn prettyprinting of structures on and off.",
+          &setlist);
+  add_alias_cmd ("pp", "prettyprint", class_support, 1, &setlist);
+
+  add_cmd ("unionprint", class_support, set_unionprint_command,
+          "Turn printing of unions interior to structures on and off.",
+          &setlist);
+
+  add_info ("format", format_info,
+           "Show current settings of data formatting options.");
+
+  /* Give people the defaults which they are used to.  */
+  prettyprint = 0;
+  unionprint = 1;
+
+  print_max = 200;
+
+  unsigned_type_table
+    = (char **) xmalloc ((1 + sizeof (unsigned LONGEST)) * sizeof (char *));
+  bzero (unsigned_type_table, (1 + sizeof (unsigned LONGEST)));
+  unsigned_type_table[sizeof (unsigned char)] = "unsigned char";
+  unsigned_type_table[sizeof (unsigned short)] = "unsigned short";
+  unsigned_type_table[sizeof (unsigned long)] = "unsigned long";
+  unsigned_type_table[sizeof (unsigned int)] = "unsigned int";
+#ifdef LONG_LONG
+  unsigned_type_table[sizeof (unsigned long long)] = "unsigned long long";
+#endif
+
+  signed_type_table
+    = (char **) xmalloc ((1 + sizeof (LONGEST)) * sizeof (char *));
+  bzero (signed_type_table, (1 + sizeof (LONGEST)));
+  signed_type_table[sizeof (char)] = "char";
+  signed_type_table[sizeof (short)] = "short";
+  signed_type_table[sizeof (long)] = "long";
+  signed_type_table[sizeof (int)] = "int";
+#ifdef LONG_LONG
+  signed_type_table[sizeof (long long)] = "long long";
+#endif
+
+  float_type_table
+    = (char **) xmalloc ((1 + sizeof (double)) * sizeof (char *));
+  bzero (float_type_table, (1 + sizeof (double)));
+  float_type_table[sizeof (float)] = "float";
+  float_type_table[sizeof (double)] = "double";
+}
+
diff --git a/usr/src/usr.bin/gdb/values.c b/usr/src/usr.bin/gdb/values.c
new file mode 100644 (file)
index 0000000..93a2911
--- /dev/null
@@ -0,0 +1,1059 @@
+/*-
+ * This code is derived from software copyrighted by the Free Software
+ * Foundation.
+ *
+ * Modified 1991 by Donn Seeley at UUNET Technologies, Inc.
+ * Modified 1990 by Van Jacobson at Lawrence Berkeley Laboratory.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)values.c   6.3 (Berkeley) 5/8/91";
+#endif /* not lint */
+
+/* Low level packing and unpacking of values for GDB.
+   Copyright (C) 1986, 1987, 1989 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+GDB is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GDB is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GDB; see the file COPYING.  If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#include <stdio.h>
+#include "defs.h"
+#include "param.h"
+#include "symtab.h"
+#include "value.h"
+
+/* The value-history records all the values printed
+   by print commands during this session.  Each chunk
+   records 60 consecutive values.  The first chunk on
+   the chain records the most recent values.
+   The total number of values is in value_history_count.  */
+
+#define VALUE_HISTORY_CHUNK 60
+
+struct value_history_chunk
+{
+  struct value_history_chunk *next;
+  value values[VALUE_HISTORY_CHUNK];
+};
+
+/* Chain of chunks now in use.  */
+
+static struct value_history_chunk *value_history_chain;
+
+static int value_history_count;        /* Abs number of last entry stored */
+
+\f
+/* List of all value objects currently allocated
+   (except for those released by calls to release_value)
+   This is so they can be freed after each command.  */
+
+static value all_values;
+
+/* Allocate a  value  that has the correct length for type TYPE.  */
+
+value
+allocate_value (type)
+     struct type *type;
+{
+  register value val;
+
+  /* If the type we want had no definition in the file it first
+   * appeared in, it will be marked a `stub'.  The real definition
+   * probably appeared later so try to find it. */
+  if (TYPE_FLAGS(type) & TYPE_FLAG_STUB)
+    {
+      register char *cp;
+      register struct symbol *sym;
+      extern char *index();
+
+      if (cp = index(TYPE_NAME(type), ' '))
+       ++cp;
+      else
+       cp = TYPE_NAME(type);
+
+      sym = lookup_symbol(cp, 0, STRUCT_NAMESPACE, 0);
+
+      if (sym && TYPE_CODE(SYMBOL_TYPE(sym)) == TYPE_CODE(type))
+       bcopy (SYMBOL_TYPE (sym), type, sizeof (*type));
+    }
+  val = (value) xmalloc (sizeof (struct value) + TYPE_LENGTH (type));
+  VALUE_NEXT (val) = all_values;
+  all_values = val;
+  VALUE_TYPE (val) = type;
+  VALUE_LVAL (val) = not_lval;
+  VALUE_ADDRESS (val) = 0;
+  VALUE_FRAME (val) = 0;
+  VALUE_OFFSET (val) = 0;
+  VALUE_BITPOS (val) = 0;
+  VALUE_BITSIZE (val) = 0;
+  VALUE_REPEATED (val) = 0;
+  VALUE_REPETITIONS (val) = 0;
+  VALUE_REGNO (val) = -1;
+  return val;
+}
+
+/* Allocate a  value  that has the correct length
+   for COUNT repetitions type TYPE.  */
+
+value
+allocate_repeat_value (type, count)
+     struct type *type;
+     int count;
+{
+  register value val;
+
+  val = (value) xmalloc (sizeof (struct value) + TYPE_LENGTH (type) * count);
+  VALUE_NEXT (val) = all_values;
+  all_values = val;
+  VALUE_TYPE (val) = type;
+  VALUE_LVAL (val) = not_lval;
+  VALUE_ADDRESS (val) = 0;
+  VALUE_FRAME (val) = 0;
+  VALUE_OFFSET (val) = 0;
+  VALUE_BITPOS (val) = 0;
+  VALUE_BITSIZE (val) = 0;
+  VALUE_REPEATED (val) = 1;
+  VALUE_REPETITIONS (val) = count;
+  VALUE_REGNO (val) = -1;
+  return val;
+}
+
+/* Free all the values that have been allocated (except for those released).
+   Called after each command, successful or not.  */
+
+void
+free_all_values ()
+{
+  register value val, next;
+
+  for (val = all_values; val; val = next)
+    {
+      next = VALUE_NEXT (val);
+      free (val);
+    }
+
+  all_values = 0;
+}
+
+/* Remove VAL from the chain all_values
+   so it will not be freed automatically.  */
+
+void
+release_value (val)
+     register value val;
+{
+  register value v;
+
+  if (all_values == val)
+    {
+      all_values = val->next;
+      return;
+    }
+
+  for (v = all_values; v; v = v->next)
+    {
+      if (v->next == val)
+       {
+         v->next = val->next;
+         break;
+       }
+    }
+}
+
+/* Return a copy of the value ARG.
+   It contains the same contents, for same memory address,
+   but it's a different block of storage.  */
+
+static value
+value_copy (arg)
+     value arg;
+{
+  register value val;
+  register struct type *type = VALUE_TYPE (arg);
+  if (VALUE_REPEATED (arg))
+    val = allocate_repeat_value (type, VALUE_REPETITIONS (arg));
+  else
+    val = allocate_value (type);
+  VALUE_LVAL (val) = VALUE_LVAL (arg);
+  VALUE_ADDRESS (val) = VALUE_ADDRESS (arg);
+  VALUE_OFFSET (val) = VALUE_OFFSET (arg);
+  VALUE_BITPOS (val) = VALUE_BITPOS (arg);
+  VALUE_BITSIZE (val) = VALUE_BITSIZE (arg);
+  VALUE_REGNO (val) = VALUE_REGNO (arg);
+  bcopy (VALUE_CONTENTS (arg), VALUE_CONTENTS (val),
+        TYPE_LENGTH (VALUE_TYPE (arg))
+        * (VALUE_REPEATED (arg) ? VALUE_REPETITIONS (arg) : 1));
+  return val;
+}
+\f
+/* Access to the value history.  */
+
+/* Record a new value in the value history.
+   Returns the absolute history index of the entry.  */
+
+int
+record_latest_value (val)
+     value val;
+{
+  int i;
+  double foo;
+
+  /* Check error now if about to store an invalid float.  We return -1
+     to the caller, but allow them to continue, e.g. to print it as "Nan". */
+  if (TYPE_CODE (VALUE_TYPE (val)) == TYPE_CODE_FLT) {
+    foo = unpack_double (VALUE_TYPE (val), VALUE_CONTENTS (val), &i);
+    if (i) return -1;          /* Indicate value not saved in history */
+  }
+
+  /* Here we treat value_history_count as origin-zero
+     and applying to the value being stored now.  */
+
+  i = value_history_count % VALUE_HISTORY_CHUNK;
+  if (i == 0)
+    {
+      register struct value_history_chunk *new
+       = (struct value_history_chunk *)
+         xmalloc (sizeof (struct value_history_chunk));
+      bzero (new->values, sizeof new->values);
+      new->next = value_history_chain;
+      value_history_chain = new;
+    }
+
+  value_history_chain->values[i] = val;
+  release_value (val);
+
+  /* Now we regard value_history_count as origin-one
+     and applying to the value just stored.  */
+
+  return ++value_history_count;
+}
+
+/* Return a copy of the value in the history with sequence number NUM.  */
+
+value
+access_value_history (num)
+     int num;
+{
+  register struct value_history_chunk *chunk;
+  register int i;
+  register int absnum = num;
+
+  if (absnum <= 0)
+    absnum += value_history_count;
+
+  if (absnum <= 0)
+    {
+      if (num == 0)
+       error ("The history is empty.");
+      else if (num == 1)
+       error ("There is only one value in the history.");
+      else
+       error ("History does not go back to $$%d.", -num);
+    }
+  if (absnum > value_history_count)
+    error ("History has not yet reached $%d.", absnum);
+
+  absnum--;
+
+  /* Now absnum is always absolute and origin zero.  */
+
+  chunk = value_history_chain;
+  for (i = (value_history_count - 1) / VALUE_HISTORY_CHUNK - absnum / VALUE_HISTORY_CHUNK;
+       i > 0; i--)
+    chunk = chunk->next;
+
+  return value_copy (chunk->values[absnum % VALUE_HISTORY_CHUNK]);
+}
+
+/* Clear the value history entirely.
+   Must be done when new symbol tables are loaded,
+   because the type pointers become invalid.  */
+
+void
+clear_value_history ()
+{
+  register struct value_history_chunk *next;
+  register int i;
+  register value val;
+
+  while (value_history_chain)
+    {
+      for (i = 0; i < VALUE_HISTORY_CHUNK; i++)
+       if (val = value_history_chain->values[i])
+         free (val);
+      next = value_history_chain->next;
+      free (value_history_chain);
+      value_history_chain = next;
+    }
+  value_history_count = 0;
+}
+
+static void
+value_history_info (num_exp, from_tty)
+     char *num_exp;
+     int from_tty;
+{
+  register int i;
+  register value val;
+  static int num = 1;
+
+  if (num_exp)
+    {
+      if (num_exp[0] == '+' && num_exp[1] == '\0')
+       /* "info history +" should print from the stored position.  */
+       ;
+      else
+       /* "info history <exp>" should print around value number <exp>.  */
+       num = parse_and_eval_address (num_exp) - 5;
+    }
+  else
+    {
+      /* "info history" means print the last 10 values.  */
+      num = value_history_count - 9;
+    }
+
+  if (num <= 0)
+    num = 1;
+
+  for (i = num; i < num + 10 && i <= value_history_count; i++)
+    {
+      val = access_value_history (i);
+      printf_filtered ("$%d = ", i);
+      value_print (val, stdout, 0, Val_pretty_default);
+      printf_filtered ("\n");
+    }
+
+  /* The next "info history +" should start after what we just printed.  */
+  num += 10;
+
+  /* Hitting just return after this command should do the same thing as
+     "info history +".  If num_exp is null, this is unnecessary, since
+     "info history +" is not useful after "info history".  */
+  if (from_tty && num_exp)
+    {
+      num_exp[0] = '+';
+      num_exp[1] = '\0';
+    }
+}
+\f
+/* Internal variables.  These are variables within the debugger
+   that hold values assigned by debugger commands.
+   The user refers to them with a '$' prefix
+   that does not appear in the variable names stored internally.  */
+
+static struct internalvar *internalvars;
+
+/* Look up an internal variable with name NAME.  NAME should not
+   normally include a dollar sign.
+
+   If the specified internal variable does not exist,
+   one is created, with a void value.  */
+
+struct internalvar *
+lookup_internalvar (name)
+     char *name;
+{
+  register struct internalvar *var;
+
+  for (var = internalvars; var; var = var->next)
+    if (!strcmp (var->name, name))
+      return var;
+
+  var = (struct internalvar *) xmalloc (sizeof (struct internalvar));
+  var->name = concat (name, "", "");
+  var->value = allocate_value (builtin_type_void);
+  release_value (var->value);
+  var->next = internalvars;
+  internalvars = var;
+  return var;
+}
+
+value
+value_of_internalvar (var)
+     struct internalvar *var;
+{
+  register value val;
+
+#ifdef IS_TRAPPED_INTERNALVAR
+  if (IS_TRAPPED_INTERNALVAR (var->name))
+    return VALUE_OF_TRAPPED_INTERNALVAR (var);
+#endif 
+
+  val = value_copy (var->value);
+  VALUE_LVAL (val) = lval_internalvar;
+  VALUE_INTERNALVAR (val) = var;
+  return val;
+}
+
+void
+set_internalvar_component (var, offset, bitpos, bitsize, newval)
+     struct internalvar *var;
+     int offset, bitpos, bitsize;
+     value newval;
+{
+  register char *addr = VALUE_CONTENTS (var->value) + offset;
+
+#ifdef IS_TRAPPED_INTERNALVAR
+  if (IS_TRAPPED_INTERNALVAR (var->name))
+    SET_TRAPPED_INTERNALVAR (var, newval, bitpos, bitsize, offset);
+#endif
+
+  if (bitsize)
+    modify_field (addr, (int) value_as_long (newval),
+                 bitpos, bitsize);
+  else
+    bcopy (VALUE_CONTENTS (newval), addr,
+          TYPE_LENGTH (VALUE_TYPE (newval)));
+}
+
+void
+set_internalvar (var, val)
+     struct internalvar *var;
+     value val;
+{
+#ifdef IS_TRAPPED_INTERNALVAR
+  if (IS_TRAPPED_INTERNALVAR (var->name))
+    SET_TRAPPED_INTERNALVAR (var, val, 0, 0, 0);
+#endif
+
+  free (var->value);
+  var->value = value_copy (val);
+  release_value (var->value);
+}
+
+char *
+internalvar_name (var)
+     struct internalvar *var;
+{
+  return var->name;
+}
+
+/* Free all internalvars.  Done when new symtabs are loaded,
+   because that makes the values invalid.  */
+
+void
+clear_internalvars ()
+{
+  register struct internalvar *var;
+
+  while (internalvars)
+    {
+      var = internalvars;
+      internalvars = var->next;
+      free (var->name);
+      free (var->value);
+      free (var);
+    }
+}
+
+static void
+convenience_info ()
+{
+  register struct internalvar *var;
+  int varseen = 0;
+
+  for (var = internalvars; var; var = var->next)
+    {
+#ifdef IS_TRAPPED_INTERNALVAR
+      if (IS_TRAPPED_INTERNALVAR (var->name))
+       continue;
+#endif
+      if (!varseen)
+       {
+         printf ("Debugger convenience variables:\n\n");
+         varseen = 1;
+       }
+      printf ("$%s: ", var->name);
+      value_print (var->value, stdout, 0, Val_pretty_default);
+      printf ("\n");
+    }
+  if (!varseen)
+    printf ("No debugger convenience variables now defined.\n\
+Convenience variables have names starting with \"$\";\n\
+use \"set\" as in \"set $foo = 5\" to define them.\n");
+}
+\f
+/* Extract a value as a C number (either long or double).
+   Knows how to convert fixed values to double, or
+   floating values to long.
+   Does not deallocate the value.  */
+
+LONGEST
+value_as_long (val)
+     register value val;
+{
+  return unpack_long (VALUE_TYPE (val), VALUE_CONTENTS (val));
+}
+
+double
+value_as_double (val)
+     register value val;
+{
+  double foo;
+  int inv;
+  
+  foo = unpack_double (VALUE_TYPE (val), VALUE_CONTENTS (val), &inv);
+  if (inv)
+    error ("Invalid floating value found in program.");
+  return foo;
+}
+\f
+/* Unpack raw data (copied from debugee) at VALADDR
+   as a long, or as a double, assuming the raw data is described
+   by type TYPE.  Knows how to convert different sizes of values
+   and can convert between fixed and floating point.
+
+   C++: It is assumed that the front-end has taken care of
+   all matters concerning pointers to members.  A pointer
+   to member which reaches here is considered to be equivalent
+   to an INT (or some size).  After all, it is only an offset.  */
+
+LONGEST
+unpack_long (type, valaddr)
+     struct type *type;
+     char *valaddr;
+{
+  register enum type_code code = TYPE_CODE (type);
+  register int len = TYPE_LENGTH (type);
+  register int nosign = TYPE_UNSIGNED (type);
+
+  if (code == TYPE_CODE_ENUM)
+    code = TYPE_CODE_INT;
+  if (code == TYPE_CODE_FLT)
+    {
+      if (len == sizeof (float))
+       return * (float *) valaddr;
+
+      if (len == sizeof (double))
+       return * (double *) valaddr;
+    }
+  else if (code == TYPE_CODE_INT && nosign)
+    {
+      if (len == sizeof (char))
+       return * (unsigned char *) valaddr;
+
+      if (len == sizeof (short))
+       return * (unsigned short *) valaddr;
+
+      if (len == sizeof (int))
+       return * (unsigned int *) valaddr;
+
+      if (len == sizeof (long))
+       return * (unsigned long *) valaddr;
+#ifdef LONG_LONG
+      if (len == sizeof (long long))
+       return * (unsigned long long *) valaddr;
+#endif
+    }
+  else if (code == TYPE_CODE_INT)
+    {
+      if (len == sizeof (char))
+       return * (char *) valaddr;
+
+      if (len == sizeof (short))
+       return * (short *) valaddr;
+
+      if (len == sizeof (int))
+       return * (int *) valaddr;
+
+      if (len == sizeof (long))
+       return * (long *) valaddr;
+
+#ifdef LONG_LONG
+      if (len == sizeof (long long))
+       return * (long long *) valaddr;
+#endif
+    }
+  else if (code == TYPE_CODE_PTR
+          || code == TYPE_CODE_REF)
+    {
+      if (len == sizeof (char *))
+       return (CORE_ADDR) * (char **) valaddr;
+    }
+  else if (code == TYPE_CODE_MEMBER)
+    error ("not implemented: member types in unpack_long");
+
+  error ("Value not integer or pointer.");
+}
+
+/* Return a double value from the specified type and address.
+   INVP points to an int which is set to 0 for valid value,
+   1 for invalid value (bad float format).  In either case,
+   the returned double is OK to use.  */
+
+double
+unpack_double (type, valaddr, invp)
+     struct type *type;
+     char *valaddr;
+     int *invp;
+{
+  register enum type_code code = TYPE_CODE (type);
+  register int len = TYPE_LENGTH (type);
+  register int nosign = TYPE_UNSIGNED (type);
+
+  *invp = 0;                   /* Assume valid.   */
+  if (code == TYPE_CODE_FLT)
+    {
+      if (INVALID_FLOAT (valaddr, len))
+       {
+         *invp = 1;
+         return 1.234567891011121314;
+       }
+
+      if (len == sizeof (float))
+       return * (float *) valaddr;
+
+      if (len == sizeof (double))
+       {
+         /* Some machines require doubleword alignment for doubles.
+            This code works on them, and on other machines.  */
+         double temp;
+         bcopy ((char *) valaddr, (char *) &temp, sizeof (double));
+         return temp;
+       }
+    }
+  else if (code == TYPE_CODE_INT && nosign)
+    {
+      if (len == sizeof (char))
+       return * (unsigned char *) valaddr;
+
+      if (len == sizeof (short))
+       return * (unsigned short *) valaddr;
+
+      if (len == sizeof (int))
+       return * (unsigned int *) valaddr;
+
+      if (len == sizeof (long))
+       return * (unsigned long *) valaddr;
+
+#ifdef LONG_LONG
+      if (len == sizeof (long long))
+       return * (unsigned long long *) valaddr;
+#endif
+    }
+  else if (code == TYPE_CODE_INT)
+    {
+      if (len == sizeof (char))
+       return * (char *) valaddr;
+
+      if (len == sizeof (short))
+       return * (short *) valaddr;
+
+      if (len == sizeof (int))
+       return * (int *) valaddr;
+
+      if (len == sizeof (long))
+       return * (long *) valaddr;
+
+#ifdef LONG_LONG
+      if (len == sizeof (long long))
+       return * (long long *) valaddr;
+#endif
+    }
+
+  error ("Value not floating number.");
+  /* NOTREACHED */
+  return (double) 0;           /* To silence compiler warning.  */
+}
+\f
+/* Given a value ARG1 of a struct or union type,
+   extract and return the value of one of its fields.
+   FIELDNO says which field.
+
+   For C++, must also be able to return values from static fields */
+
+value
+value_field (arg1, fieldno)
+     register value arg1;
+     register int fieldno;
+{
+  register value v;
+  register struct type *type = TYPE_FIELD_TYPE (VALUE_TYPE (arg1), fieldno);
+  register int offset;
+
+  /* Handle packed fields */
+
+  offset = TYPE_FIELD_BITPOS (VALUE_TYPE (arg1), fieldno) / 8;
+  if (TYPE_FIELD_BITSIZE (VALUE_TYPE (arg1), fieldno))
+    {
+      v = value_from_long (type,
+                          unpack_field_as_long (VALUE_TYPE (arg1),
+                                                VALUE_CONTENTS (arg1),
+                                                fieldno));
+      VALUE_BITPOS (v) = TYPE_FIELD_BITPOS (VALUE_TYPE (arg1), fieldno) % 8;
+      VALUE_BITSIZE (v) = TYPE_FIELD_BITSIZE (VALUE_TYPE (arg1), fieldno);
+    }
+  else
+    {
+      v = allocate_value (type);
+      bcopy (VALUE_CONTENTS (arg1) + offset,
+            VALUE_CONTENTS (v),
+            TYPE_LENGTH (type));
+    }
+  VALUE_LVAL (v) = VALUE_LVAL (arg1);
+  if (VALUE_LVAL (arg1) == lval_internalvar)
+    VALUE_LVAL (v) = lval_internalvar_component;
+  VALUE_ADDRESS (v) = VALUE_ADDRESS (arg1);
+  VALUE_OFFSET (v) = offset + VALUE_OFFSET (arg1);
+  return v;
+}
+
+value
+value_fn_field (arg1, fieldno, subfieldno)
+     register value arg1;
+     register int fieldno;
+{
+  register value v;
+  struct fn_field *f = TYPE_FN_FIELDLIST1 (VALUE_TYPE (arg1), fieldno);
+  register struct type *type = TYPE_FN_FIELD_TYPE (f, subfieldno);
+  struct symbol *sym;
+
+  sym = lookup_symbol (TYPE_FN_FIELD_PHYSNAME (f, subfieldno),
+                      0, VAR_NAMESPACE, 0);
+  if (! sym) error ("Internal error: could not find physical method named %s",
+                   TYPE_FN_FIELD_PHYSNAME (f, subfieldno));
+  
+  v = allocate_value (type);
+  VALUE_ADDRESS (v) = BLOCK_START (SYMBOL_BLOCK_VALUE (sym));
+  VALUE_TYPE (v) = type;
+  return v;
+}
+
+/* Return a virtual function as a value.
+   ARG1 is the object which provides the virtual function
+   table pointer.
+   F is the list of member functions which contains the desired virtual
+   function.
+   J is an index into F which provides the desired virtual function.
+   TYPE is the basetype which first provides the virtual function table.  */
+value
+value_virtual_fn_field (arg1, f, j, type)
+     value arg1;
+     struct fn_field *f;
+     int j;
+     struct type *type;
+{
+  /* First, get the virtual function table pointer.  That comes
+     with a strange type, so cast it to type `pointer to long' (which
+     should serve just fine as a function type).  Then, index into
+     the table, and convert final value to appropriate function type.  */
+  value vfn, vtbl;
+  value vi = value_from_long (builtin_type_int, 
+                             (LONGEST) TYPE_FN_FIELD_VOFFSET (f, j));
+  VALUE_TYPE (arg1) = TYPE_VPTR_BASETYPE (type);
+
+  /* This type may have been defined before its virtual function table
+     was.  If so, fill in the virtual function table entry for the
+     type now.  */
+  if (TYPE_VPTR_FIELDNO (type) < 0)
+    TYPE_VPTR_FIELDNO (type)
+      = fill_in_vptr_fieldno (type);
+
+  /* The virtual function table is now an array of structures
+     which have the form { int16 offset, delta; void *pfn; }.  */
+  vtbl = value_ind (value_field (arg1, TYPE_VPTR_FIELDNO (type)));
+
+  /* Index into the virtual function table.  This is hard-coded because
+     looking up a field is not cheap, and it may be important to save
+     time, e.g. if the user has set a conditional breakpoint calling
+     a virtual function.  */
+  vfn = value_field (value_subscript (vtbl, vi), 2);
+
+  /* Reinstantiate the function pointer with the correct type.  */
+  VALUE_TYPE (vfn) = lookup_pointer_type (TYPE_FN_FIELD_TYPE (f, j));
+  return vfn;
+}
+
+/* The value of a static class member does not depend
+   on its instance, only on its type.  If FIELDNO >= 0,
+   then fieldno is a valid field number and is used directly.
+   Otherwise, FIELDNAME is the name of the field we are
+   searching for.  If it is not a static field name, an
+   error is signaled.  TYPE is the type in which we look for the
+   static field member.  */
+value
+value_static_field (type, fieldname, fieldno)
+     register struct type *type;
+     char *fieldname;
+     register int fieldno;
+{
+  register value v;
+  struct symbol *sym;
+
+  if (fieldno < 0)
+    {
+      register struct type *t = type;
+      /* Look for static field.  */
+      while (t)
+       {
+         int i;
+         for (i = TYPE_NFIELDS (t) - 1; i >= 0; i--)
+           if (! strcmp (TYPE_FIELD_NAME (t, i), fieldname))
+             {
+               if (TYPE_FIELD_STATIC (t, i))
+                 {
+                   fieldno = i;
+                   goto found;
+                 }
+               else
+                 error ("field `%s' is not static");
+             }
+         t = TYPE_BASECLASSES (t) ? TYPE_BASECLASS (t, 1) : 0;
+       }
+
+      t = type;
+
+      if (destructor_name_p (fieldname, t))
+       error ("use `info method' command to print out value of destructor");
+
+      while (t)
+       {
+         int i, j;
+
+         for (i = TYPE_NFN_FIELDS (t) - 1; i >= 0; i--)
+           {
+             if (! strcmp (TYPE_FN_FIELDLIST_NAME (t, i), fieldname))
+               {
+                 error ("use `info method' command to print value of method \"%s\"", fieldname);
+               }
+           }
+         t = TYPE_BASECLASSES (t) ? TYPE_BASECLASS (t, 1) : 0;
+       }
+      error("there is no field named %s", fieldname);
+    }
+
+ found:
+
+  sym = lookup_symbol (TYPE_FIELD_STATIC_PHYSNAME (type, fieldno),
+                      0, VAR_NAMESPACE, 0);
+  if (! sym) error ("Internal error: could not find physical static variable named %s", TYPE_FIELD_BITSIZE (type, fieldno));
+
+  type = TYPE_FIELD_TYPE (type, fieldno);
+  v = value_at (type, (CORE_ADDR)SYMBOL_BLOCK_VALUE (sym));
+  return v;
+}
+
+long
+unpack_field_as_long (type, valaddr, fieldno)
+     struct type *type;
+     char *valaddr;
+     int fieldno;
+{
+  long val;
+  int bitpos = TYPE_FIELD_BITPOS (type, fieldno);
+  int bitsize = TYPE_FIELD_BITSIZE (type, fieldno);
+
+  bcopy (valaddr + bitpos / 8, &val, sizeof val);
+
+  /* Extracting bits depends on endianness of the machine.  */
+#ifdef BITS_BIG_ENDIAN
+  val = val >> (sizeof val * 8 - bitpos % 8 - bitsize);
+#else
+  val = val >> (bitpos % 8);
+#endif
+
+  val &= (1 << bitsize) - 1;
+  return val;
+}
+
+void
+modify_field (addr, fieldval, bitpos, bitsize)
+     char *addr;
+     int fieldval;
+     int bitpos, bitsize;
+{
+  long oword;
+
+  /* Reject values too big to fit in the field in question.
+     Otherwise adjoining fields may be corrupted.  */
+  if (fieldval & ~((1<<bitsize)-1))
+    error ("Value %d does not fit in %d bits.", fieldval, bitsize);
+  
+  bcopy (addr, &oword, sizeof oword);
+
+  /* Shifting for bit field depends on endianness of the machine.  */
+#ifdef BITS_BIG_ENDIAN
+  bitpos = sizeof (oword) * 8 - bitpos - bitsize;
+#endif
+
+  oword &= ~(((1 << bitsize) - 1) << bitpos);
+  oword |= fieldval << bitpos;
+  bcopy (&oword, addr, sizeof oword);
+}
+\f
+/* Convert C numbers into newly allocated values */
+
+value
+value_from_long (type, num)
+     struct type *type;
+     register LONGEST num;
+{
+  register value val = allocate_value (type);
+  register enum type_code code = TYPE_CODE (type);
+  register int len = TYPE_LENGTH (type);
+
+  if (code == TYPE_CODE_INT || code == TYPE_CODE_ENUM)
+    {
+      if (len == sizeof (char))
+       * (char *) VALUE_CONTENTS (val) = num;
+      else if (len == sizeof (short))
+       * (short *) VALUE_CONTENTS (val) = num;
+      else if (len == sizeof (int))
+       * (int *) VALUE_CONTENTS (val) = num;
+      else if (len == sizeof (long))
+       * (long *) VALUE_CONTENTS (val) = num;
+#ifdef LONG_LONG
+      else if (len == sizeof (long long))
+       * (long long *) VALUE_CONTENTS (val) = num;
+#endif
+      else
+       error ("Integer type encountered with unexpected data length.");
+    }
+  else
+    error ("Unexpected type encountered for integer constant.");
+
+  return val;
+}
+
+value
+value_from_double (type, num)
+     struct type *type;
+     double num;
+{
+  register value val = allocate_value (type);
+  register enum type_code code = TYPE_CODE (type);
+  register int len = TYPE_LENGTH (type);
+
+  if (code == TYPE_CODE_FLT)
+    {
+      if (len == sizeof (float))
+       * (float *) VALUE_CONTENTS (val) = num;
+      else if (len == sizeof (double))
+       * (double *) VALUE_CONTENTS (val) = num;
+      else
+       error ("Floating type encountered with unexpected data length.");
+    }
+  else
+    error ("Unexpected type encountered for floating constant.");
+
+  return val;
+}
+\f
+/* Deal with the value that is "about to be returned".  */
+
+/* Return the value that a function returning now
+   would be returning to its caller, assuming its type is VALTYPE.
+   RETBUF is where we look for what ought to be the contents
+   of the registers (in raw form).  This is because it is often
+   desirable to restore old values to those registers
+   after saving the contents of interest, and then call
+   this function using the saved values.
+   struct_return is non-zero when the function in question is
+   using the structure return conventions on the machine in question;
+   0 when it is using the value returning conventions (this often
+   means returning pointer to where structure is vs. returning value). */
+
+value
+value_being_returned (valtype, retbuf, struct_return)
+     register struct type *valtype;
+     char retbuf[REGISTER_BYTES];
+     int struct_return;
+{
+  register value val;
+
+  if (struct_return)
+    return value_at (valtype, EXTRACT_STRUCT_VALUE_ADDRESS (retbuf));
+
+  val = allocate_value (valtype);
+  EXTRACT_RETURN_VALUE (valtype, retbuf, VALUE_CONTENTS (val));
+
+  return val;
+}
+
+/* Return true if the function specified is using the structure returning
+   convention on this machine to return arguments, or 0 if it is using
+   the value returning convention.  FUNCTION is the value representing
+   the function, FUNCADDR is the address of the function, and VALUE_TYPE
+   is the type returned by the function */
+
+struct block *block_for_pc ();
+
+int
+using_struct_return (function, funcaddr, value_type)
+     value function;
+     CORE_ADDR funcaddr;
+     struct type *value_type;
+{
+  register enum type_code code = TYPE_CODE (value_type);
+
+  if (code == TYPE_CODE_STRUCT ||
+      code == TYPE_CODE_UNION ||
+      code == TYPE_CODE_ARRAY)
+    {
+      struct block *b = block_for_pc (funcaddr);
+
+      if (!(BLOCK_GCC_COMPILED (b) && TYPE_LENGTH (value_type) < 8))
+       return 1;
+    }
+
+  return 0;
+}
+
+/* Store VAL so it will be returned if a function returns now.
+   Does not verify that VAL's type matches what the current
+   function wants to return.  */
+
+void
+set_return_value (val)
+     value val;
+{
+  register enum type_code code = TYPE_CODE (VALUE_TYPE (val));
+  char regbuf[REGISTER_BYTES];
+  double dbuf;
+  LONGEST lbuf;
+
+  if (code == TYPE_CODE_STRUCT
+      || code == TYPE_CODE_UNION)
+    error ("Specifying a struct or union return value is not supported.");
+
+  if (code == TYPE_CODE_FLT)
+    {
+      dbuf = value_as_double (val);
+
+      STORE_RETURN_VALUE (VALUE_TYPE (val), &dbuf);
+    }
+  else
+    {
+      lbuf = value_as_long (val);
+      STORE_RETURN_VALUE (VALUE_TYPE (val), &lbuf);
+    }
+}
+\f
+void
+_initialize_values ()
+{
+  add_info ("convenience", convenience_info,
+           "Debugger convenience (\"$foo\") variables.\n\
+These variables are created when you assign them values;\n\
+thus, \"print $foo=1\" gives \"$foo\" the value 1.  Values may be any type.\n\n\
+A few convenience variables are given values automatically GDB:\n\
+\"$_\"holds the last address examined with \"x\" or \"info lines\",\n\
+\"$__\" holds the contents of the last address examined with \"x\".");
+
+  add_info ("values", value_history_info,
+           "Elements of value history (around item number IDX, or last ten).");
+  add_info_alias ("history", value_history_info, 0);
+}