386BSD 0.0 development
authorWilliam F. Jolitz <wjolitz@soda.berkeley.edu>
Tue, 14 May 1991 05:14:04 +0000 (21:14 -0800)
committerWilliam F. Jolitz <wjolitz@soda.berkeley.edu>
Tue, 14 May 1991 05:14:04 +0000 (21:14 -0800)
Work on file usr/src/usr.bin/gdb/main.c

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

usr/src/usr.bin/gdb/main.c [new file with mode: 0644]

diff --git a/usr/src/usr.bin/gdb/main.c b/usr/src/usr.bin/gdb/main.c
new file mode 100644 (file)
index 0000000..06e1b9c
--- /dev/null
@@ -0,0 +1,2240 @@
+/*-
+ * 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[] = "@(#)main.c     6.6 (Berkeley) 5/13/91";
+#endif /* not lint */
+
+/* Top level 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 "command.h"
+#include "param.h"
+#include "expression.h"
+
+#ifdef USG
+#include <sys/types.h>
+#include <unistd.h>
+#endif
+
+#include <sys/file.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <ctype.h>
+
+#ifdef SET_STACK_LIMIT_HUGE
+#include <sys/time.h>
+#include <sys/resource.h>
+
+int original_stack_limit;
+#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 void free ();
+
+/* Version number of GDB, as a string.  */
+
+extern char *version;
+
+/*
+ * Declare all cmd_list_element's
+ */
+
+/* Chain containing all defined commands.  */
+
+struct cmd_list_element *cmdlist;
+
+/* Chain containing all defined info subcommands.  */
+
+struct cmd_list_element *infolist;
+
+/* Chain containing all defined enable subcommands. */
+
+struct cmd_list_element *enablelist;
+
+/* Chain containing all defined disable subcommands. */
+
+struct cmd_list_element *disablelist;
+
+/* Chain containing all defined delete subcommands. */
+
+struct cmd_list_element *deletelist;
+
+/* Chain containing all defined "enable breakpoint" subcommands. */
+
+struct cmd_list_element *enablebreaklist;
+
+/* Chain containing all defined set subcommands */
+
+struct cmd_list_element *setlist;
+
+/* Chain containing all defined \"set history\".  */
+
+struct cmd_list_element *sethistlist;
+
+/* Chain containing all defined \"unset history\".  */
+
+struct cmd_list_element *unsethistlist;
+
+/* stdio stream that command input is being read from.  */
+
+FILE *instream;
+
+/* Current working directory.  */
+
+char *current_directory;
+
+/* The directory name is actually stored here (usually).  */
+static char dirbuf[MAXPATHLEN];
+
+#ifdef KERNELDEBUG
+/* Nonzero if we're debugging /dev/mem or a kernel crash dump */
+
+int kernel_debugging;
+#endif
+
+/* Nonzero to inhibit confirmation of quitting or restarting 
+   a stopped inferior. */
+int inhibit_confirm;
+  
+/* Nonzero if we can write in text or core file */
+
+int writeable_text;
+
+/* The number of lines on a page, and the number of spaces
+   in a line.  */
+int linesize, pagesize;
+
+/* Nonzero if we should refrain from using an X window.  */
+
+int inhibit_windows = 0;
+
+/* Function to call before reading a command, if nonzero.
+   The function receives two args: an input stream,
+   and a prompt string.  */
+   
+void (*window_hook) ();
+
+extern int frame_file_full_name;
+int xgdb_verbose;
+
+void execute_command();
+void free_command_lines ();
+char *gdb_readline ();
+char *command_line_input ();
+static void initialize_main ();
+static void initialize_cmd_lists ();
+void command_loop ();
+static void source_command ();
+static void print_gdb_version ();
+static void float_handler ();
+static void cd_command ();
+
+char *getenv ();
+
+/* gdb prints this when reading a command interactively */
+static char *prompt;
+
+/* Buffer used for reading command lines, and the size
+   allocated for it so far.  */
+
+char *line;
+int linesize;
+
+\f
+/* This is how `error' returns to command level.  */
+
+jmp_buf to_top_level;
+
+void
+return_to_top_level ()
+{
+  quit_flag = 0;
+  immediate_quit = 0;
+  clear_breakpoint_commands ();
+  clear_momentary_breakpoints ();
+  disable_current_display ();
+  do_cleanups (0);
+  longjmp (to_top_level, 1);
+}
+
+/* Call FUNC with arg ARG, catching any errors.
+   If there is no error, return the value returned by FUNC.
+   If there is an error, return zero after printing ERRSTRING
+    (which is in addition to the specific error message already printed).  */
+
+int
+catch_errors (func, arg, errstring)
+     int (*func) ();
+     int arg;
+     char *errstring;
+{
+  jmp_buf saved;
+  int val;
+  struct cleanup *saved_cleanup_chain;
+
+  saved_cleanup_chain = save_cleanups ();
+
+  bcopy (to_top_level, saved, sizeof (jmp_buf));
+
+  if (setjmp (to_top_level) == 0)
+    val = (*func) (arg);
+  else
+    {
+      fprintf (stderr, "%s\n", errstring);
+      val = 0;
+    }
+
+  restore_cleanups (saved_cleanup_chain);
+
+  bcopy (saved, to_top_level, sizeof (jmp_buf));
+  return val;
+}
+
+/* Handler for SIGHUP.  */
+
+static void
+disconnect ()
+{
+  kill_inferior_fast ();
+  signal (SIGHUP, SIG_DFL);
+  kill (getpid (), SIGHUP);
+}
+\f
+/* Clean up on error during a "source" command (or execution of a
+   user-defined command).
+   Close the file opened by the command
+   and restore the previous input stream.  */
+
+static void
+source_cleanup (stream)
+     FILE *stream;
+{
+  /* Instream may be 0; set to it when executing user-defined command. */
+  if (instream)
+    fclose (instream);
+  instream = stream;
+}
+
+/*
+ * Source $HOME/.gdbinit and $cwd/.gdbinit.
+ * If X is enabled, also $HOME/.xgdbinit and $cwd/.xgdbinit.source
+ */
+void
+source_init_files()
+{
+       char *homedir, initfile[256];
+       int samedir = 0;
+
+       /* Read init file, if it exists in home directory  */
+       homedir = getenv ("HOME");
+       if (homedir) {
+               struct stat homebuf, cwdbuf;
+
+               sprintf(initfile, "%s/.gdbinit", homedir);
+               if (access (initfile, R_OK) == 0)
+                       if (!setjmp (to_top_level))
+                               source_command (initfile);
+               if (!inhibit_windows) {
+                       sprintf(initfile, "%s/.xgdbinit", homedir);
+                       if (access (initfile, R_OK) == 0)
+                               if (!setjmp (to_top_level))
+                                       source_command (initfile);
+               }
+               /* Determine if current directory is the same as the home
+                  directory, so we don't source the same file twice. */
+       
+               bzero (&homebuf, sizeof (struct stat));
+               bzero (&cwdbuf, sizeof (struct stat));
+       
+               stat(homedir, &homebuf);
+               stat(".", &cwdbuf);
+               
+               samedir = bcmp(&homebuf, &cwdbuf, sizeof(struct stat)) == 0;
+       }
+       /* Read the input file in the current directory, *if* it isn't
+          the same file (it should exist, also).  */
+       if (!samedir) {
+               if (access (".gdbinit", R_OK) == 0)
+                       if (!setjmp (to_top_level))
+                               source_command (".gdbinit");
+               if (access (".xgdbinit", R_OK) == 0)
+                       if (!setjmp (to_top_level))
+                               source_command (".xgdbinit");
+       }
+}
+
+\f
+int
+main (argc, argv, envp)
+     int argc;
+     char **argv;
+     char **envp;
+{
+  int count;
+  int inhibit_gdbinit = 0;
+  int quiet = 1;
+  int batch = 0;
+  register int i;
+  char *cp;
+
+  /* XXX Windows only for xgdb. */
+  char *strrchr();
+  if (cp = strrchr(argv[0], '/'))
+         ++cp;
+  else
+         cp = argv[0];
+  if (*cp != 'x')
+         inhibit_windows = 1;
+
+#if defined (ALIGN_STACK_ON_STARTUP)
+  i = (int) &count & 0x3;
+  if (i != 0)
+    alloca (4 - i);
+#endif
+
+  quit_flag = 0;
+  linesize = 100;
+  line = (char *) xmalloc (linesize);
+  *line = 0;
+  instream = stdin;
+
+  getwd (dirbuf);
+  current_directory = dirbuf;
+
+#ifdef SET_STACK_LIMIT_HUGE
+  {
+    struct rlimit rlim;
+
+    /* Set the stack limit huge so that alloca (particularly stringtab
+     * in dbxread.c) does not fail. */
+    getrlimit (RLIMIT_STACK, &rlim);
+    original_stack_limit = rlim.rlim_cur;
+    rlim.rlim_cur = rlim.rlim_max;
+    setrlimit (RLIMIT_STACK, &rlim);
+  }
+#endif /* SET_STACK_LIMIT_HUGE */
+
+  /* Look for flag arguments.  */
+
+  for (i = 1; i < argc; i++)
+    {
+      if (!strcmp (argv[i], "-q") || !strcmp (argv[i], "-quiet"))
+       quiet = 1;
+      else if (!strcmp (argv[i], "-nx"))
+       inhibit_gdbinit = 1;
+      else if (!strcmp (argv[i], "-nw"))
+       inhibit_windows = 1;
+      else if (!strcmp (argv[i], "-batch"))
+       batch = 1, quiet = 1;
+      else if (!strcmp (argv[i], "-fullname"))
+       frame_file_full_name = 1;
+      else if (!strcmp (argv[i], "-xgdb_verbose"))
+       xgdb_verbose = 1;
+      /* -help: print a summary of command line switches.  */
+      else if (!strcmp (argv[i], "-help"))
+       {
+         fputs ("\
+This is GDB, the GNU debugger.  Use the command\n\
+    gdb [options] [executable [core-file]]\n\
+to enter the debugger.\n\
+\n\
+Options available are:\n\
+  -help             Print this message.\n\
+  -quiet            Do not print version number on startup.\n\
+  -fullname         Output information used by emacs-GDB interface.\n\
+  -batch            Exit after processing options.\n\
+  -nx               Do not read .gdbinit file.\n\
+  -tty TTY          Use TTY for input/output by the program being debugged.\n\
+  -cd DIR           Change current directory to DIR.\n\
+  -directory DIR    Search for source files in DIR.\n\
+  -command FILE     Execute GDB commands from FILE.\n\
+  -symbols SYMFILE  Read symbols from SYMFILE.\n\
+  -exec EXECFILE    Use EXECFILE as the executable.\n\
+  -se FILE          Use FILE as symbol file and executable file.\n\
+  -core COREFILE    Analyze the core dump COREFILE.\n\
+  -k                Kernel debugging.\n\
+  -w                Writeable text.\n\
+  -v                Print GNU message and version number on startup.\n\
+  -nc               Don't confirm quit or run commands.\n\
+\n\
+For more information, type \"help\" from within GDB, or consult the\n\
+GDB manual (available as on-line info or a printed manual).\n", stderr);
+         /* Exiting after printing this message seems like
+            the most useful thing to do.  */
+         exit (0);
+       }
+#ifdef KERNELDEBUG
+      else if (!strcmp (argv[i], "-k"))
+       kernel_debugging = 1;
+#endif
+      else if (!strcmp (argv[i], "-w"))
+       writeable_text = 1;
+      else if (!strcmp (argv[i], "-v"))
+       quiet = 0;
+      else if (!strcmp (argv[i], "-nc"))
+             inhibit_confirm = 1;
+      else if (argv[i][0] == '-')
+       /* Other options take arguments, so don't confuse an
+          argument with an option.  */
+       i++;
+    }
+
+  /* Run the init function of each source file */
+
+  initialize_cmd_lists ();     /* This needs to be done first */
+  initialize_all_files ();
+  initialize_main ();          /* But that omits this file!  Do it now */
+  initialize_signals ();
+
+  if (!quiet)
+    print_gdb_version ();
+
+  /* Process the command line arguments.  */
+
+  count = 0;
+  for (i = 1; i < argc; i++)
+    {
+      register char *arg = argv[i];
+      /* Args starting with - say what to do with the following arg
+        as a filename.  */
+      if (arg[0] == '-')
+       {
+         extern void exec_file_command (), symbol_file_command ();
+         extern void core_file_command (), directory_command ();
+         extern void tty_command ();
+
+         if (!strcmp (arg, "-q") || !strcmp (arg, "-nx")
+             || !strcmp (arg, "-quiet") || !strcmp (arg, "-batch")
+             || !strcmp (arg, "-fullname") || !strcmp (arg, "-nw")
+             || !strcmp (arg, "-xgdb_verbose")
+             || !strcmp (arg, "-help")
+             || !strcmp (arg, "-k")
+             || !strcmp (arg, "-w")
+             || !strcmp (arg, "-v")
+             || !strcmp (arg, "-nc"))
+           /* Already processed above */
+           continue;
+
+         if (++i == argc)
+           fprintf (stderr, "No argument follows \"%s\".\n", arg);
+         if (!setjmp (to_top_level))
+           {
+             /* -s foo: get syms from foo.  -e foo: execute foo.
+                -se foo: do both with foo.  -c foo: use foo as core dump.  */
+             if (!strcmp (arg, "-se"))
+               {
+                 exec_file_command (argv[i], !batch);
+                 symbol_file_command (argv[i], !batch);
+               }
+             else if (!strcmp (arg, "-s") || !strcmp (arg, "-symbols"))
+               symbol_file_command (argv[i], !batch);
+             else if (!strcmp (arg, "-e") || !strcmp (arg, "-exec"))
+               exec_file_command (argv[i], !batch);
+             else if (!strcmp (arg, "-c") || !strcmp (arg, "-core"))
+               core_file_command (argv[i], !batch);
+             /* -x foo: execute commands from foo.  */
+             else if (!strcmp (arg, "-x") || !strcmp (arg, "-command")
+                      || !strcmp (arg, "-commands"))
+               source_command (argv[i]);
+             /* -d foo: add directory `foo' to source-file directory
+                        search-list */
+             else if (!strcmp (arg, "-d") || !strcmp (arg, "-dir")
+                      || !strcmp (arg, "-directory"))
+               directory_command (argv[i], 0);
+             /* -cd FOO: specify current directory as FOO.
+                GDB remembers the precise string FOO as the dirname.  */
+             else if (!strcmp (arg, "-cd"))
+               {
+                 cd_command (argv[i], 0);
+                 init_source_path ();
+               }
+             /* -t /def/ttyp1: use /dev/ttyp1 for inferior I/O.  */
+             else if (!strcmp (arg, "-t") || !strcmp (arg, "-tty"))
+               tty_command (argv[i], 0);
+
+             else
+               error ("Unknown command-line switch: \"%s\"\n", arg);
+           }
+       }
+      else
+       {
+         /* Args not thus accounted for
+            are treated as, first, the symbol/executable file
+            and, second, the core dump file.  */
+         count++;
+         if (!setjmp (to_top_level))
+           switch (count)
+             {
+             case 1:
+               exec_file_command (arg, !batch);
+               symbol_file_command (arg, !batch);
+               break;
+
+             case 2:
+               core_file_command (arg, !batch);
+               break;
+
+             case 3:
+               fprintf (stderr, "Excess command line args ignored. (%s%s)\n",
+                        arg, (i == argc - 1) ? "" : " ...");
+             }
+       }
+    }
+
+  if (!inhibit_gdbinit)
+         source_init_files();
+
+  if (batch)
+    {
+#if 0
+      fatal ("Attempt to read commands from stdin in batch mode.");
+#endif
+      /* We have hit the end of the batch file.  */
+      exit (0);
+    }
+
+  if (!quiet)
+    printf ("Type \"help\" for a list of commands.\n");
+
+  /* The command loop.  */
+
+  while (1)
+    {
+      if (!setjmp (to_top_level))
+       command_loop ();
+      if (ISATTY(stdin))
+        clearerr (stdin);      /* Don't get hung if C-d is typed.  */
+      else if (feof(instream)) /* Avoid endless loops for redirected stdin */
+       break;
+    }
+    exit (0);
+}
+
+
+static void
+do_nothing ()
+{
+}
+
+/* Read commands from `instream' and execute them
+   until end of file.  */
+void
+command_loop ()
+{
+  struct cleanup *old_chain;
+  register int toplevel = (instream == stdin);
+  register int interactive = (toplevel && ISATTY(stdin));
+
+  while (!feof (instream))
+    {
+      register char *cmd_line;
+
+      quit_flag = 0;
+      if (interactive)
+       reinitialize_more_filter ();
+      old_chain = make_cleanup (do_nothing, 0);
+      cmd_line = command_line_input (prompt, toplevel);
+      execute_command (cmd_line, toplevel);
+      /* Do any commands attached to breakpoint we stopped at.  */
+      do_breakpoint_commands ();
+      do_cleanups (old_chain);
+    }
+}
+\f
+/* Commands call this if they do not want to be repeated by null lines.  */
+
+void
+dont_repeat ()
+{
+  /* If we aren't reading from standard input, we are saving the last
+     thing read from stdin in line and don't want to delete it.  Null lines
+     won't repeat here in any case.  */
+  if (instream == stdin)
+    *line = 0;
+}
+\f
+/* Read a line from the stream "instream" without command line editing.
+
+   It prints PROMPT once at the start.
+   Action is compatible with "readline" (i.e., space for typing is
+   malloced & should be freed by caller). */
+char *
+gdb_readline (prompt)
+     char *prompt;
+{
+  int c;
+  char *result;
+  int input_index = 0;
+  int result_size = 80;
+
+  if (prompt)
+    {
+      printf (prompt);
+      fflush (stdout);
+    }
+  
+  result = (char *) xmalloc (result_size);
+
+  while (1)
+    {
+      c = fgetc (instream ? instream : stdin);
+      if (c == EOF)
+       {
+         free(result);
+         return ((char *)0);
+       }
+      if (c == '\n')
+       break;
+
+      result[input_index++] = c;
+      if (input_index >= result_size)
+       {
+         result_size <= 1;
+         result = (char *)xrealloc(result, result_size);
+       }
+    }
+    result[input_index++] = '\0';
+    return result;
+}
+
+/* Declaration for fancy readline with command line editing.  */
+char *readline ();
+
+/* Variables which control command line editing and history
+   substitution.  These variables are given default values at the end
+   of this file.  */
+static int command_editing_p;
+static int history_expansion_p;
+static int write_history_p;
+static int history_size;
+static char *history_filename;
+
+/* Variables which are necessary for fancy command line editing.  */
+char *gdb_completer_word_break_characters =
+  " \t\n!@#$%^&*()-+=|~`}{[]\"';:?/>.<,";
+
+/* Functions that are used as part of the fancy command line editing.  */
+
+/* Generate symbol names one by one for the completer.  If STATE is
+   zero, then we need to initialize, otherwise the initialization has
+   already taken place.  TEXT is what we expect the symbol to start
+   with.  RL_LINE_BUFFER is available to be looked at; it contains the
+   entire text of the line.  RL_POINT is the offset in that line of
+   the cursor.  You should pretend that the line ends at RL_POINT.  */
+char *
+symbol_completion_function (text, state)
+     char *text;
+     int state;
+{
+  char **make_symbol_completion_list ();
+  static char **list = (char **)NULL;
+  static int index;
+  char *output;
+  extern char *rl_line_buffer;
+  extern int rl_point;
+  char *tmp_command, *p;
+  struct cmd_list_element *c, *result_list;
+
+  if (!state)
+    {
+      /* Free the storage used by LIST, but not by the strings inside.  This is
+        because rl_complete_internal () frees the strings. */
+      if (list)
+       free (list);
+      list = 0;
+      index = 0;
+
+      /* Decide whether to complete on a list of gdb commands or on
+        symbols.  */
+      tmp_command = (char *) alloca (rl_point + 1);
+      p = tmp_command;
+      
+      strncpy (tmp_command, rl_line_buffer, rl_point);
+      tmp_command[rl_point] = '\0';
+
+      if (rl_point == 0)
+       {
+         /* An empty line we want to consider ambiguous; that is,
+            it could be any command.  */
+         c = (struct cmd_list_element *) -1;
+         result_list = 0;
+       }
+      else
+       c = lookup_cmd_1 (&p, cmdlist, &result_list, 1);
+
+      /* Move p up to the next interesting thing.  */
+      while (*p == ' ' || *p == '\t')
+       p++;
+
+      if (!c)
+       /* He's typed something unrecognizable.  Sigh.  */
+       list = (char **) 0;
+      else if (c == (struct cmd_list_element *) -1)
+       {
+         if (p + strlen(text) != tmp_command + rl_point)
+           error ("Unrecognized command.");
+         
+         /* He's typed something ambiguous.  This is easier.  */
+         if (result_list)
+           list = complete_on_cmdlist (*result_list->prefixlist, text);
+         else
+           list = complete_on_cmdlist (cmdlist, text);
+       }
+      else
+       {
+         /* If we've gotten this far, gdb has recognized a full
+            command.  There are several possibilities:
+
+            1) We need to complete on the command.
+            2) We need to complete on the possibilities coming after
+            the command.
+            2) We need to complete the text of what comes after the
+            command.   */
+
+         if (!*p && *text)
+           /* Always (might be longer versions of thie command).  */
+           list = complete_on_cmdlist (result_list, text);
+         else if (!*p && !*text)
+           {
+             if (c->prefixlist)
+               list = complete_on_cmdlist (*c->prefixlist, "");
+             else
+               list = make_symbol_completion_list ("");
+           }
+         else
+           {
+             if (c->prefixlist && !c->allow_unknown)
+               {
+                 *p = '\0';
+                 error ("\"%s\" command requires a subcommand.",
+                        tmp_command);
+               }
+             else
+               list = make_symbol_completion_list (text);
+           }
+       }
+    }
+
+  /* If the debugged program wasn't compiled with symbols, or if we're
+     clearly completing on a command and no command matches, return
+     NULL.  */
+  if (!list)
+    return ((char *)NULL);
+
+  output = list[index];
+  if (output)
+    index++;
+
+  return (output);
+}
+\f
+
+void
+print_prompt ()
+{
+  if (prompt)
+    {
+      printf ("%s", prompt);
+      fflush (stdout);
+    }
+}
+\f
+
+#ifdef HAVE_TERMIO
+#include <termio.h>
+static struct termio norm_tty;
+
+static void
+suspend_sig()
+{
+       int tty = fileno(stdin);
+       struct termio cur_tty;
+
+       ioctl(tty, TCGETA, &cur_tty);
+       ioctl(tty, TCSETAW, &norm_tty);
+
+       (void) sigsetmask(0);
+       signal(SIGTSTP, SIG_DFL);
+       kill(0, SIGTSTP);
+
+       /*
+        * we've just been resumed -- current tty params become new
+        * 'normal' params (in case tset/stty was done while we were
+        * suspended).  Merge values that readline might have changed
+        * into new params, then restore term mode.
+        */
+       ioctl(tty, TCGETA, &norm_tty);
+       cur_tty.c_lflag = (cur_tty.c_lflag & (ICANON|ECHO|ISIG)) |
+                         (norm_tty.c_lflag &~ (ICANON|ECHO|ISIG));
+       cur_tty.c_iflag = (cur_tty.c_iflag & (IXON|ISTRIP|INPCK)) |
+                         (norm_tty.c_iflag &~ (IXON|ISTRIP|INPCK));
+       ioctl(tty, TCSETAW, &cur_tty);
+
+       signal(SIGTSTP, suspend_sig);
+       print_prompt();
+
+       /*
+        * Forget about any previous command -- null line now will do
+        * nothing.
+        */
+       dont_repeat();
+}
+
+#else
+
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <sgtty.h>
+
+static struct sgttyb norm_tty;
+static struct tchars norm_tchars;
+static struct ltchars norm_ltchars;
+static int norm_lflags;
+
+#ifdef PASS8
+#define RL_TFLAGS (RAW|CRMOD|ECHO|CBREAK|PASS8)
+#else
+#define RL_TFLAGS (RAW|CRMOD|ECHO|CBREAK)
+#endif
+
+static void
+suspend_sig()
+{
+       int tty = fileno(stdin);
+       struct sgttyb cur_tty;
+       struct tchars cur_tchars;
+       struct ltchars cur_ltchars;
+       int cur_lflags;
+       int cur_flags;
+
+       ioctl(tty, TIOCGETP, &cur_tty);
+       ioctl(tty, TIOCGETC, &cur_tchars);
+       ioctl(tty, TIOCLGET, &cur_lflags);
+       ioctl(tty, TIOCGLTC, &cur_ltchars);
+
+       ioctl(tty, TIOCSETP, &norm_tty);
+       ioctl(tty, TIOCSETC, &norm_tchars);
+       ioctl(tty, TIOCLSET, &norm_lflags);
+       ioctl(tty, TIOCSLTC, &norm_ltchars);
+
+       (void) sigsetmask(0);
+       signal(SIGTSTP, SIG_DFL);
+       kill(0, SIGTSTP);
+
+       /*
+        * we've just been resumed -- current tty params become new
+        * 'normal' params (in case tset/stty was done while we were
+        * suspended).  Merge values that readline might have changed
+        * into new params, then restore term mode.
+        */
+       ioctl(tty, TIOCGETP, &norm_tty);
+       cur_flags = cur_tty.sg_flags;
+       cur_tty = norm_tty;
+       cur_tty.sg_flags = (cur_tty.sg_flags &~ RL_TFLAGS)
+                          | (cur_flags & RL_TFLAGS);
+
+       ioctl(tty, TIOCLGET, &norm_lflags);
+#ifdef LPASS8
+       cur_lflags = (cur_lflags &~ LPASS8) | (cur_flags & LPASS8);
+#endif
+       ioctl(tty, TIOCGETC, &norm_tchars);
+       ioctl(tty, TIOCGLTC, &norm_ltchars);
+
+       ioctl(tty, TIOCSETP, &cur_tty);
+       ioctl(tty, TIOCSETC, &cur_tchars);
+       ioctl(tty, TIOCLSET, &cur_lflags);
+       ioctl(tty, TIOCSLTC, &cur_ltchars);
+
+       signal(SIGTSTP, suspend_sig);
+       print_prompt();
+
+       /*
+        * Forget about any previous command -- null line now will do
+        * nothing.
+        */
+       dont_repeat();
+}
+#endif /* HAVE_TERMIO */
+
+/* Initialize signal handlers. */
+initialize_signals ()
+{
+  extern void request_quit ();
+  int tty = fileno(stdin);
+
+  signal (SIGINT, request_quit);
+
+  /* If we initialize SIGQUIT to SIG_IGN, then the SIG_IGN will get
+     passed to the inferior, which we don't want.  It would be
+     possible to do a "signal (SIGQUIT, SIG_DFL)" after we fork, but
+     on BSD4.3 systems using vfork, that will (apparently) affect the
+     GDB process as well as the inferior (the signal handling tables
+     being shared between the two, apparently).  Since we establish
+     a handler for SIGQUIT, when we call exec it will set the signal
+     to SIG_DFL for us.  */
+  signal (SIGQUIT, do_nothing);
+  if (signal (SIGHUP, do_nothing) != SIG_IGN)
+    signal (SIGHUP, disconnect);
+  signal (SIGFPE, float_handler);
+
+  ioctl(tty, TIOCGETP, &norm_tty);
+  ioctl(tty, TIOCLGET, &norm_lflags);
+  ioctl(tty, TIOCGETC, &norm_tchars);
+  ioctl(tty, TIOCGLTC, &norm_ltchars);
+  signal(SIGTSTP, suspend_sig);
+}
+
+char *
+finish_command_input(inputline, repeat, interactive)
+       register char *inputline;
+       int repeat;
+       int interactive;
+{
+       static char *do_free;
+
+       if (do_free) {
+               free(do_free);
+               do_free = NULL;
+       }
+
+       /* Do history expansion if that is wished.  */
+       if (interactive && history_expansion_p) {
+               int expanded;
+
+               expanded = history_expand(inputline, &do_free);
+               if (expanded) {
+                       /* Print the changes.  */
+                       puts(do_free);
+
+                       /* An error acts like no input. */
+                       if (expanded < 0) {
+                               *do_free = 0;
+                               return (do_free);
+                       }
+               }
+               inputline = do_free;
+       }
+       /* get rid of any leading whitespace */
+       while (isspace(*inputline))
+               ++inputline;
+       /*
+        * If we just got an empty line, and that is supposed to repeat the
+        * previous command, return the value in the global buffer.
+        */
+       if (*inputline == 0) {
+               if (repeat)
+                       return (line);
+       } else if (interactive)
+               add_history(inputline);
+
+       /*
+        * If line is a comment, clear it out.
+        * Note:  comments are added to the command history. This is useful
+        * when you type a command, and then realize you don't want to
+        * execute it quite yet.  You can comment out the command and then
+        * later fetch it from the value history and remove the '#'.
+        */
+       if (*inputline == '#')
+               *inputline = 0;
+       else if (repeat) {
+               /* Save into global buffer. */
+               register int i = strlen(inputline) + 1;
+
+               if (i > linesize) {
+                       line = xrealloc(line, i);
+                       linesize = i;
+               }
+               strcpy(line, inputline);
+       }
+       return (inputline);
+}
+
+static char *
+get_a_cmd_line(prompt, interactive)
+       char *prompt;
+       int interactive;
+{
+       register char *cp;
+
+       /* Control-C quits instantly if typed while reading input. */
+       immediate_quit++;
+       if (interactive && command_editing_p) {
+               extern void (*rl_event_hook)();
+
+               rl_event_hook = window_hook;
+               cp = readline(prompt);
+       } else {
+               if (interactive) {
+                       if (window_hook) {
+                               print_prompt();
+                               (*window_hook)();
+                       }
+               } else
+                       prompt = NULL;
+               cp = gdb_readline(prompt);
+       }
+       --immediate_quit;
+       return (cp);
+}
+
+/* Read one line from the command input stream `instream'
+   Returns the address of the start of the line.
+
+   *If* the instream == stdin & stdin is a terminal, the line read
+   is copied into the file line saver (global var char *line,
+   length linesize) so that it can be duplicated.
+
+   This routine either uses fancy command line editing or
+   simple input as the user has requested.  */
+
+char *
+command_line_input(prompt, repeat)
+       char *prompt;
+       int repeat;
+{
+       static char *do_free;
+       register int interactive = (instream == stdin && ISATTY(instream));
+       register char *cp;
+       register int i;
+
+       if (do_free) {
+               free(do_free);
+               do_free = NULL;
+       }
+       cp = get_a_cmd_line(prompt, interactive);
+
+       /*
+        * handle continued lines (this loop is not particularly
+        * efficient because it's rare).
+        */
+       while (cp && cp[i = strlen(cp) - 1] == '\\') {
+               register char *np = get_a_cmd_line(prompt, interactive);
+               register int j;
+
+               if (np == NULL) {
+                       cp[i] = 0;
+                       break;
+               }
+               j = strlen(np);
+               cp = xrealloc(cp, i + j + 1);
+               strcpy(cp + i, np);
+               free(np);
+       }
+       if (cp == NULL)
+               return ("");
+       do_free = cp;
+       return (finish_command_input(cp, repeat, interactive));
+}
+\f
+
+#define MAX_USER_ARGS 32
+
+static struct user_args {
+       struct {
+               char *arg;
+               int len;
+       } a[10];
+} uargs[MAX_USER_ARGS];
+
+static struct user_args *user_arg = uargs;
+
+static void
+arg_cleanup(ap)
+       struct user_args *ap;
+{
+       user_arg = ap;
+}
+
+/* Bind arguments $arg0, $arg1, ..., for a user defined command. */
+struct cleanup *
+setup_user_args(p)
+       char *p;
+{
+       register int i;
+       struct cleanup *old_chain = make_cleanup(arg_cleanup, user_arg);
+
+       if (++user_arg >= &uargs[MAX_USER_ARGS])
+               error("user defined functions nested too deeply\n");
+
+       bzero(user_arg, sizeof(*user_arg));
+
+       i = 0;
+       while (*p) {
+               while (isspace(*p))
+                       ++p;
+               user_arg->a[i].arg = p;
+               while (*p && ! isspace(*p))
+                       ++p;
+               user_arg->a[i].len = p - user_arg->a[i].arg;
+               ++i;
+       }
+       return (old_chain);
+}
+
+static char *
+findarg(str)
+       register char *str;
+{
+       register char *cp = str;
+       extern char *index();
+
+       while (cp = index(cp, '$')) {
+               if (strncmp(cp, "$arg", 4) == 0 && isdigit(cp[4]))
+                       return (cp);
+               ++cp;
+       }
+       return (char *)0;
+}
+
+/* expand arguments from "line" into "new" */
+static void
+expand_args(line, new)
+       register char *line, *new;
+{
+       register char *cp = findarg(line);
+
+       while (cp = findarg(line)) {
+               int i, len;
+
+               bcopy(line, new, cp - line);
+               new += cp - line;
+               i = cp[4] - '0';
+               if (len = user_arg->a[i].len) {
+                       bcopy(user_arg->a[i].arg, new, len);
+                       new += len;
+               }
+               line = cp + 5;
+       }
+       strcpy(new, line);
+}
+
+/* expand any arguments in "line" then execute the result */
+static void
+expand_and_execute(line, from_tty)
+       char *line;
+       int from_tty;
+{
+       void execute_command();
+       char new[1024];
+
+       if (! findarg(line)) {
+               execute_command(line, from_tty);
+               return;
+       }
+       expand_args(line, new);
+       execute_command(new, from_tty);
+}
+
+char *
+read_one_command_line(prompt, from_tty)
+       char *prompt;
+{
+       register char *p, *p1;
+
+       dont_repeat();
+       p = command_line_input(prompt, from_tty);
+
+       /* Remove trailing blanks.  */
+       p1 = p + strlen(p);
+       while (--p1 > p && (*p1 == ' ' || *p1 == '\t'))
+               ;
+       *++p1 = 0;
+       return (p);
+}
+
+static char cmd_prompt[] = "                                                > ";
+
+int
+parse_control_structure(rootcmd, from_tty, level)
+       struct command_line *rootcmd;
+       int from_tty;
+{
+       struct command_line *cmd = (struct command_line *)xmalloc(sizeof(*cmd));
+       char *prompt;
+
+       ++level;
+       prompt = from_tty? &cmd_prompt[sizeof(cmd_prompt) - 1 - 2*level] :
+                          (char *)0;
+       bzero(cmd, sizeof(*cmd));
+       rootcmd->body = cmd;
+       while (1) {
+               char *p = read_one_command_line(prompt, from_tty);
+
+               p = savestring(p, strlen(p));
+               cmd->line = p;
+               if (!strncmp(p, "while ", 6)) {
+                       cmd->type = CL_WHILE;
+                       if (parse_control_structure(cmd, from_tty, level))
+                               return (1);
+               } else if (!strncmp(p, "if ", 3)) {
+                       cmd->type = CL_IF;
+                       if (parse_control_structure(cmd, from_tty, level)) {
+                               struct command_line *tmp;
+                               int stat;
+
+                               cmd->elsebody = cmd->body;
+                               stat = parse_control_structure(cmd, from_tty,
+                                                              level);
+                               tmp = cmd->elsebody;
+                               cmd->elsebody = cmd->body;
+                               cmd->body = tmp;
+                               if (stat)
+                                       return (1);
+                       }
+               } else if (!strcmp(p, "else")) {
+                       cmd->type = CL_END;
+                       return (1);
+               } else if (!strcmp(p, "end")) {
+                       cmd->type = CL_END;
+                       return (0);
+               } else if (!strcmp(p, "exitloop")) {
+                       cmd->type = CL_EXITLOOP;
+               } else {
+                       cmd->type = CL_NORMAL;
+               }
+               cmd->next = (struct command_line *)xmalloc(sizeof(*cmd));
+               cmd = cmd->next;
+               bzero(cmd, sizeof(*cmd));
+       }
+       /* NOTREACHED */
+}
+
+int
+execute_control_structure(cmd)
+       register struct command_line *cmd;
+{
+       char expn[1024];
+       struct expression *cond;
+       int stat;
+
+       while (cmd) {
+               QUIT;
+               switch (cmd->type) {
+               case CL_END:
+                       return (0);
+               case CL_NORMAL:
+                       expand_and_execute(cmd->line, 0);
+                       break;
+               case CL_WHILE:
+                       expand_args(cmd->line + 6, expn);
+                       cond = parse_c_expression(expn);
+                       while (breakpoint_cond_eval(cond) == 0)
+                               if (execute_control_structure(cmd->body))
+                                       break;
+                       free(cond);
+                       break;
+               case CL_IF:
+                       expand_args(cmd->line + 3, expn);
+                       cond = parse_c_expression(expn);
+                       stat = breakpoint_cond_eval(cond);
+                       free(cond);
+                       if (stat == 0) {
+                               if (execute_control_structure(cmd->body))
+                                       return (1);
+                       } else if (cmd->elsebody) {
+                               if (execute_control_structure(cmd->elsebody))
+                                       return (1);
+                       }
+                       break;
+               case CL_EXITLOOP:
+                       return (1);
+               }
+               cmd = cmd->next;
+       }
+       free_all_values();
+}
+
+execute_command_lines(cmd)
+       struct command_line *cmd;
+{
+       struct cleanup *old_chain = make_cleanup(source_cleanup, instream);
+
+       /*
+        * Set the instream to 0, indicating execution of a user-defined
+        * function.  
+        */
+       ++immediate_quit;
+       instream = (FILE *) 0;
+       (void)execute_control_structure(cmd);
+       --immediate_quit;
+       do_cleanups(old_chain);
+}
+
+/* do following command lines if expression true */
+if_command(p, from_tty)
+       char *p;
+       int from_tty;
+{
+       struct cleanup *old_chain;
+       struct command_line *cmd = (struct command_line *)xmalloc(sizeof(*cmd));
+       char buf[128];
+
+       sprintf(buf, "if %s", p);
+
+       bzero(cmd, sizeof(*cmd));
+       old_chain = make_cleanup(free_command_lines, cmd);
+       cmd->type = CL_IF;
+       cmd->line = savestring(buf, strlen(buf));
+       /* XXX cmd->line? */
+       if (parse_control_structure(cmd, from_tty, 0)) {
+               struct command_line *tmp;
+
+               cmd->elsebody = cmd->body;
+               (void) parse_control_structure(cmd, from_tty, 0);
+               tmp = cmd->elsebody;
+               cmd->elsebody = cmd->body;
+               cmd->body = tmp;
+       }
+       (void) execute_command_lines(cmd);
+       do_cleanups(old_chain);
+}
+
+/* do following command lines while expression true */
+while_command(p, from_tty)
+       char *p;
+       int from_tty;
+{
+       struct cleanup *old_chain;
+       struct command_line *cmd = (struct command_line *)xmalloc(sizeof(*cmd));
+       char buf[128];
+
+       sprintf(buf, "while %s", p);
+
+       bzero(cmd, sizeof(*cmd));
+       old_chain = make_cleanup(free_command_lines, cmd);
+       cmd->type = CL_WHILE;
+       cmd->line = savestring(buf, strlen(buf));
+       (void)parse_control_structure(cmd, from_tty, 0);
+       (void)execute_command_lines(cmd);
+       do_cleanups(old_chain);
+}
+
+/*
+ * Execute the line P as a command.
+ * Pass FROM_TTY as second argument to the defining function.
+ */
+void
+execute_command (p, from_tty)
+       char *p;
+       int from_tty;
+{
+       register struct cmd_list_element *c;
+       register struct command_line *cmdlines;
+
+       free_all_values();
+       if (*p) {
+               c = lookup_cmd(&p, cmdlist, "", 0, 1);
+               if (c->function == 0)
+                       error("That is not a command, just a help topic.");
+               else if (c->class == (int) class_user) {
+                       struct cleanup *old_chain = setup_user_args(p);
+
+                       cmdlines = (struct command_line *) c->function;
+                       if (cmdlines)
+                               (void)execute_command_lines(cmdlines);
+
+                       do_cleanups(old_chain);
+               } else
+                       /* Pass null arg rather than an empty one.  */
+                       (*c->function) (*p ? p : 0, from_tty);
+       }
+}
+
+/*
+ * Read lines from the input stream and accumulate them in a chain of struct
+ * command_line's which is then returned.  
+ */
+struct command_line *
+read_command_lines(from_tty)
+       int from_tty;
+{
+       struct cleanup *old_chain;
+       struct command_line *cmd = (struct command_line *)xmalloc(sizeof(*cmd));
+       struct command_line *next;
+
+       bzero(cmd, sizeof(*cmd));
+       old_chain = make_cleanup(free_command_lines, cmd);
+       cmd->type = CL_NOP;
+       (void)parse_control_structure(cmd, from_tty, 0);
+       dont_repeat();
+       discard_cleanups(old_chain);
+       next = cmd->body;
+       free(cmd);
+       return (next);
+}
+
+/* Free a chain of struct command_line's.  */
+
+void
+free_command_lines(cmds)
+       struct command_line *cmds;
+{
+       struct command_line *next;
+
+       while (cmds) {
+               if (cmds->body)
+                       free(cmds->body);
+               if (cmds->elsebody)
+                       free(cmds->elsebody);
+               if (cmds->line)
+                       free(cmds->line);
+               next = cmds->next;
+               free(cmds);
+               cmds = next;
+       }
+}
+\f
+/* Add an element to the list of info subcommands.  */
+
+void
+add_info (name, fun, doc)
+     char *name;
+     void (*fun) ();
+     char *doc;
+{
+  add_cmd (name, no_class, fun, doc, &infolist);
+}
+
+/* Add an alias to the list of info subcommands.  */
+
+void
+add_info_alias (name, oldname, abbrev_flag)
+     char *name;
+     char *oldname;
+     int abbrev_flag;
+{
+  add_alias_cmd (name, oldname, 0, abbrev_flag, &infolist);
+}
+
+/* The "info" command is defined as a prefix, with allow_unknown = 0.
+   Therefore, its own definition is called only for "info" with no args.  */
+
+static void
+info_command ()
+{
+  printf ("\"info\" must be followed by the name of an info command.\n");
+  help_list (infolist, "info ", -1, stdout);
+}
+\f
+/* Add an element to the list of commands.  */
+
+void
+add_com (name, class, fun, doc)
+     char *name;
+     int class;
+     void (*fun) ();
+     char *doc;
+{
+  add_cmd (name, class, fun, doc, &cmdlist);
+}
+
+/* Add an alias or abbreviation command to the list of commands.  */
+
+void
+add_com_alias (name, oldname, class, abbrev_flag)
+     char *name;
+     char *oldname;
+     int class;
+     int abbrev_flag;
+{
+  add_alias_cmd (name, oldname, class, abbrev_flag, &cmdlist);
+}
+
+void
+error_no_arg (why)
+     char *why;
+{
+  error ("Argument required (%s).", why);
+}
+
+static void
+help_command (command, from_tty)
+     char *command;
+     int from_tty; /* Ignored */
+{
+  help_cmd (command, stdout);
+}
+\f
+static void
+validate_comname (comname)
+     char *comname;
+{
+  register char *p;
+
+  if (comname == 0)
+    error_no_arg ("name of command to define");
+
+  p = comname;
+  while (*p)
+    {
+      if (!(*p >= 'A' && *p <= 'Z')
+         && !(*p >= 'a' && *p <= 'z')
+         && !(*p >= '0' && *p <= '9')
+         && *p != '-')
+       error ("Junk in argument list: \"%s\"", p);
+      p++;
+    }
+}
+
+static void
+define_command (comname, from_tty)
+     char *comname;
+     int from_tty;
+{
+  register struct command_line *cmds;
+  register struct cmd_list_element *c;
+  char *tem = comname;
+
+  validate_comname (comname);
+
+  c = lookup_cmd (&tem, cmdlist, "", -1, 1);
+  if (c)
+    {
+      if (c->class == (int) class_user || c->class == (int) class_alias)
+       tem = "Redefine command \"%s\"? ";
+      else
+       tem = "Really redefine built-in command \"%s\"? ";
+      if (!query (tem, comname))
+       error ("Command \"%s\" not redefined.", comname);
+    }
+
+  if (from_tty)
+    {
+      printf ("Type commands for definition of \"%s\".\n\
+End with a line saying just \"end\".\n", comname);
+      fflush (stdout);
+    }
+  comname = savestring (comname, strlen (comname));
+
+  cmds = read_command_lines (from_tty);
+
+  if (c && c->class == (int) class_user)
+    free_command_lines (c->function);
+
+  add_com (comname, class_user, cmds,
+          (c && c->class == (int) class_user)
+          ? c->doc : savestring ("User-defined.", 13));
+}
+
+static void
+document_command (comname, from_tty)
+     char *comname;
+     int from_tty;
+{
+  register struct cmd_list_element *c;
+  register char *p;
+  register char *cp;
+  register char *doc = 0;
+  register int len;
+  char *tmp = comname;
+
+  validate_comname (comname);
+  c = lookup_cmd (&tmp, cmdlist, "", 0, 1);
+  if (c->class != (int) class_user)
+    error ("Command \"%s\" is built-in.", comname);
+
+  if (from_tty)
+    printf ("Type documentation for \"%s\".  \
+End with a line saying just \"end\".\n", comname);
+
+  while (p = read_one_command_line(from_tty? "> " : 0, from_tty))
+    {
+      if (strcmp(p, "end") == 0)
+       break;
+      len = strlen(p) + 1;
+      if (! doc)
+       {
+         doc = xmalloc(len);
+         cp = doc;
+       }
+      else
+       {
+         int i = cp - doc;
+         doc = xrealloc(doc, i + len);
+         cp = doc + i;
+       }
+      strcpy(cp, p);
+      cp += len;
+      cp[-1] = '\n';
+    }
+  if (doc && cp > doc)
+    cp[-1] = 0;
+  if (c->doc)
+    free (c->doc);
+  c->doc = doc;
+}
+\f
+static void
+print_gdb_version ()
+{
+  printf ("GDB %s, Copyright (C) 1989 Free Software Foundation, Inc.\n\
+There is ABSOLUTELY NO WARRANTY for GDB; type \"info warranty\" for details.\n\
+GDB is free software and you are welcome to distribute copies of it\n\
+ under certain conditions; type \"info copying\" to see the conditions.\n",
+         version);
+}
+
+static void
+version_info ()
+{
+  immediate_quit++;
+  print_gdb_version ();
+  immediate_quit--;
+}
+\f
+
+/* Command to specify a prompt string instead of "(gdb) ".  */
+
+void
+set_prompt_command (text)
+     char *text;
+{
+  char *p, *q;
+  register int c;
+  char *new;
+
+  if (text == 0)
+    error_no_arg ("string to which to set prompt");
+
+  new = (char *) xmalloc (strlen (text) + 2);
+  p = text; q = new;
+  while (c = *p++)
+    {
+      if (c == '\\')
+       {
+         /* \ at end of argument is used after spaces
+            so they won't be lost.  */
+         if (*p == 0)
+           break;
+         c = parse_escape (&p);
+         if (c == 0)
+           break; /* C loses */
+         else if (c > 0)
+           *q++ = c;
+       }
+      else
+       *q++ = c;
+    }
+  if (*(p - 1) != '\\')
+    *q++ = ' ';
+  *q++ = '\0';
+  new = (char *) xrealloc (new, q - new);
+  free (prompt);
+  prompt = new;
+}
+\f
+static void
+quit_command ()
+{
+  if (have_inferior_p ())
+    {
+      if (inhibit_confirm || query ("The program is running.  Quit anyway? "))
+       {
+         /* Prevent any warning message from reopen_exec_file, in case
+            we have a core file that's inconsistent with the exec file.  */
+         exec_file_command (0, 0);
+         kill_inferior ();
+       }
+      else
+       error ("Not confirmed.");
+    }
+  /* Save the history information if it is appropriate to do so.  */
+  if (write_history_p && history_filename)
+    write_history (history_filename);
+  exit (0);
+}
+
+int
+input_from_terminal_p ()
+{
+  return instream == stdin;
+}
+\f
+static void
+pwd_command (arg, from_tty)
+     char *arg;
+     int from_tty;
+{
+  if (arg) error ("The \"pwd\" command does not take an argument: %s", arg);
+  getwd (dirbuf);
+
+  if (strcmp (dirbuf, current_directory))
+    printf ("Working directory %s\n (canonically %s).\n",
+           current_directory, dirbuf);
+  else
+    printf ("Working directory %s.\n", current_directory);
+}
+
+static void
+cd_command (dir, from_tty)
+     char *dir;
+     int from_tty;
+{
+  int len;
+  int change;
+
+  if (dir == 0)
+    error_no_arg ("new working directory");
+
+  dir = tilde_expand (dir);
+  make_cleanup (free, dir);
+
+  len = strlen (dir);
+  dir = savestring (dir, len - (len > 1 && dir[len-1] == '/'));
+  if (dir[0] == '/')
+    current_directory = dir;
+  else
+    {
+      current_directory = concat (current_directory, "/", dir);
+      free (dir);
+    }
+
+  /* Now simplify any occurrences of `.' and `..' in the pathname.  */
+
+  change = 1;
+  while (change)
+    {
+      char *p;
+      change = 0;
+
+      for (p = current_directory; *p;)
+       {
+         if (!strncmp (p, "/./", 2)
+             && (p[2] == 0 || p[2] == '/'))
+           strcpy (p, p + 2);
+         else if (!strncmp (p, "/..", 3)
+                  && (p[3] == 0 || p[3] == '/')
+                  && p != current_directory)
+           {
+             char *q = p;
+             while (q != current_directory && q[-1] != '/') q--;
+             if (q != current_directory)
+               {
+                 strcpy (q-1, p+3);
+                 p = q-1;
+               }
+           }
+         else p++;
+       }
+    }
+
+  if (chdir (dir) < 0)
+    perror_with_name (dir);
+
+  if (from_tty)
+    pwd_command ((char *) 0, 1);
+}
+\f
+static void
+source_command (arg, from_tty)
+     char *arg;
+     int from_tty;
+{
+  FILE *stream;
+  struct cleanup *cleanups;
+  char *file = arg;
+  char *path;
+
+  if (file == 0)
+    /* Let source without arguments read .gdbinit.  */
+    file = ".gdbinit";
+
+  file = tilde_expand (file);
+  make_cleanup (free, file);
+
+#ifdef KERNELDEBUG
+  if (path = getenv(kernel_debugging? "KGDBPATH" : "GDBPATH"))
+#else
+  if (path = getenv("GDBPATH"))
+#endif
+    {
+      int fd = openp(path, 1, file, O_RDONLY, 0, 0);
+
+      if (fd == -1)
+       stream = 0;
+      else
+       stream = fdopen(fd, "r");
+    }
+  else
+    stream = fopen (file, "r");
+
+  if (stream == 0)
+    perror_with_name (file);
+
+  cleanups = make_cleanup (source_cleanup, instream);
+
+  instream = stream;
+
+  command_loop ();
+
+  do_cleanups (cleanups);
+}
+
+static void
+echo_command (text)
+     char *text;
+{
+  char *p = text;
+  register int c;
+
+  if (text)
+    while (c = *p++)
+      {
+       if (c == '\\')
+         {
+           /* \ at end of argument is used after spaces
+              so they won't be lost.  */
+           if (*p == 0)
+             return;
+
+           c = parse_escape (&p);
+           if (c >= 0)
+             fputc (c, stdout);
+         }
+       else
+         fputc (c, stdout);
+      }
+      fflush(stdout);
+}
+
+static void
+dump_me_command ()
+{
+  if (query ("Should GDB dump core? "))
+    {
+      signal (SIGQUIT, SIG_DFL);
+      kill (getpid (), SIGQUIT);
+    }
+}
+\f
+int
+parse_binary_operation (caller, arg)
+     char *caller, *arg;
+{
+  int length;
+
+  if (!arg || !*arg)
+    return 1;
+
+  length = strlen (arg);
+
+  while (arg[length - 1] == ' ' || arg[length - 1] == '\t')
+    length--;
+
+  if (!strncmp (arg, "on", length)
+      || !strncmp (arg, "1", length)
+      || !strncmp (arg, "yes", length))
+    return 1;
+  else
+    if (!strncmp (arg, "off", length)
+       || !strncmp (arg, "0", length)
+       || !strncmp (arg, "no", length))
+      return 0;
+    else
+      error ("\"%s\" not given a binary valued argument.", caller);
+}
+
+/* Functions to manipulate command line editing control variables.  */
+
+static void
+set_editing (arg, from_tty)
+     char *arg;
+     int from_tty;
+{
+  command_editing_p = parse_binary_operation ("set command-editing", arg);
+}
+
+/* Number of commands to print in each call to editing_info.  */
+#define Hist_print 10
+static void
+editing_info (arg, from_tty)
+     char *arg;
+     int from_tty;
+{
+  /* Index for history commands.  Relative to history_base.  */
+  int offset;
+
+  /* Number of the history entry which we are planning to display next.
+     Relative to history_base.  */
+  static int num = 0;
+
+  /* The first command in the history which doesn't exist (i.e. one more
+     than the number of the last command).  Relative to history_base.  */
+  int hist_len;
+
+  struct _hist_entry {
+    char *line;
+    char *data;
+  } *history_get();
+  extern int history_base;
+
+  printf_filtered ("Interactive command editing is %s.\n",
+         command_editing_p ? "on" : "off");
+
+  printf_filtered ("History expansion of command input is %s.\n",
+         history_expansion_p ? "on" : "off");
+  printf_filtered ("Writing of a history record upon exit is %s.\n",
+         write_history_p ? "enabled" : "disabled");
+  printf_filtered ("The size of the history list (number of stored commands) is %d.\n",
+         history_size);
+  printf_filtered ("The name of the history record is \"%s\".\n\n",
+         history_filename ? history_filename : "");
+
+  /* Print out some of the commands from the command history.  */
+  /* First determine the length of the history list.  */
+  hist_len = history_size;
+  for (offset = 0; offset < history_size; offset++)
+    {
+      if (!history_get (history_base + offset))
+       {
+         hist_len = offset;
+         break;
+       }
+    }
+
+  if (arg)
+    {
+      if (arg[0] == '+' && arg[1] == '\0')
+       /* "info editing +" should print from the stored position.  */
+       ;
+      else
+       /* "info editing <exp>" should print around command number <exp>.  */
+       num = (parse_and_eval_address (arg) - history_base) - Hist_print / 2;
+    }
+  /* "info editing" means print the last Hist_print commands.  */
+  else
+    {
+      num = hist_len - Hist_print;
+    }
+
+  if (num < 0)
+    num = 0;
+
+  /* If there are at least Hist_print commands, we want to display the last
+     Hist_print rather than, say, the last 6.  */
+  if (hist_len - num < Hist_print)
+    {
+      num = hist_len - Hist_print;
+      if (num < 0)
+       num = 0;
+    }
+
+  if (num == hist_len - Hist_print)
+    printf_filtered ("The list of the last %d commands is:\n\n", Hist_print);
+  else
+    printf_filtered ("Some of the stored commands are:\n\n");
+
+  for (offset = num; offset < num + Hist_print && offset < hist_len; offset++)
+    {
+      printf_filtered ("%5d  %s\n", history_base + offset,
+             (history_get (history_base + offset))->line);
+    }
+
+  /* The next command we want to display is the next one that we haven't
+     displayed yet.  */
+  num += Hist_print;
+  
+  /* If the user repeats this command with return, it should do what
+     "info editing +" does.  This is unnecessary if arg is null,
+     because "info editing +" is not useful after "info editing".  */
+  if (from_tty && arg)
+    {
+      arg[0] = '+';
+      arg[1] = '\0';
+    }
+}
+
+static void
+set_history_expansion (arg, from_tty)
+     char *arg;
+     int from_tty;
+{
+  history_expansion_p = parse_binary_operation ("set history expansion", arg);
+}
+
+static void
+set_history_write (arg, from_tty)
+     char *arg;
+     int from_tty;
+{
+  write_history_p = parse_binary_operation ("set history write", arg);
+}
+
+static void
+set_history (arg, from_tty)
+     char *arg;
+     int from_tty;
+{
+  printf ("\"set history\" must be followed by the name of a history subcommand.\n");
+  help_list (sethistlist, "set history ", -1, stdout);
+}
+
+static void
+set_history_size (arg, from_tty)
+     char *arg;
+     int from_tty;
+{
+  if (!*arg)
+    error_no_arg ("set history size");
+
+  history_size = atoi (arg);
+}
+
+static void
+set_history_filename (arg, from_tty)
+     char *arg;
+     int from_tty;
+{
+  int i;
+
+  if (!arg)
+    error_no_arg ("history file name");
+  
+  arg = tilde_expand (arg);
+  make_cleanup (free, arg);
+
+  i = strlen (arg) - 1;
+  
+  free (history_filename);
+  
+  while (i > 0 && (arg[i] == ' ' || arg[i] == '\t'))
+    i--;
+  ++i;
+
+  if (!*arg)
+    history_filename = (char *) 0;
+  else
+    history_filename = savestring (arg, i + 1);
+  history_filename[i] = '\0';
+}
+
+int info_verbose;
+
+static void
+set_verbose_command (arg, from_tty)
+     char *arg;
+     int from_tty;
+{
+  info_verbose = parse_binary_operation ("set verbose", arg);
+}
+
+static void
+verbose_info (arg, from_tty)
+     char *arg;
+     int from_tty;
+{
+  if (arg)
+    error ("\"info verbose\" does not take any arguments.\n");
+  
+  printf ("Verbose printing of information is %s.\n",
+         info_verbose ? "on" : "off");
+}
+
+static void
+float_handler ()
+{
+  error ("Invalid floating value encountered or computed.");
+}
+
+\f
+static void
+initialize_cmd_lists ()
+{
+  cmdlist = (struct cmd_list_element *) 0;
+  infolist = (struct cmd_list_element *) 0;
+  enablelist = (struct cmd_list_element *) 0;
+  disablelist = (struct cmd_list_element *) 0;
+  deletelist = (struct cmd_list_element *) 0;
+  enablebreaklist = (struct cmd_list_element *) 0;
+  setlist = (struct cmd_list_element *) 0;
+  sethistlist = (struct cmd_list_element *) 0;
+  unsethistlist = (struct cmd_list_element *) 0;
+}
+
+static void
+initialize_main ()
+{
+  char *tmpenv;
+  /* Command line editing externals.  */
+  extern int (*rl_completion_entry_function)();
+  extern char *rl_completer_word_break_characters;
+  extern char *rl_readline_name;
+  
+  /* Set default verbose mode on.  */
+  info_verbose = 1;
+
+#ifdef KERNELDEBUG
+  if (kernel_debugging)
+         prompt = savestring ("(kgdb) ", 7);
+  else
+#endif
+  prompt = savestring ("(gdb) ", 6);
+
+  /* Set the important stuff up for command editing.  */
+  command_editing_p = 1;
+  history_expansion_p = 0;
+  write_history_p = 0;
+  
+  if (tmpenv = getenv ("HISTSIZE"))
+    history_size = atoi (tmpenv);
+  else
+    history_size = 256;
+
+  stifle_history (history_size);
+
+  if (tmpenv = getenv ("GDBHISTFILE"))
+    history_filename = savestring (tmpenv, strlen(tmpenv));
+  else
+    /* We include the current directory so that if the user changes
+       directories the file written will be the same as the one
+       that was read.  */
+    history_filename = concat (current_directory, "/.gdb_history", "");
+
+  read_history (history_filename);
+
+  /* Setup important stuff for command line editing.  */
+  rl_completion_entry_function = (int (*)()) symbol_completion_function;
+  rl_completer_word_break_characters = gdb_completer_word_break_characters;
+  rl_readline_name = "gdb";
+
+  /* Define the classes of commands.
+     They will appear in the help list in the reverse of this order.  */
+
+  add_cmd ("obscure", class_obscure, 0, "Obscure features.", &cmdlist);
+  add_cmd ("alias", class_alias, 0, "Aliases of other commands.", &cmdlist);
+  add_cmd ("user", class_user, 0, "User-defined commands.\n\
+The commands in this class are those defined by the user.\n\
+Use the \"define\" command to define a command.", &cmdlist);
+  add_cmd ("support", class_support, 0, "Support facilities.", &cmdlist);
+  add_cmd ("status", class_info, 0, "Status inquiries.", &cmdlist);
+  add_cmd ("files", class_files, 0, "Specifying and examining files.", &cmdlist);
+  add_cmd ("breakpoints", class_breakpoint, 0, "Making program stop at certain points.", &cmdlist);
+  add_cmd ("data", class_vars, 0, "Examining data.", &cmdlist);
+  add_cmd ("stack", class_stack, 0, "Examining the stack.\n\
+The stack is made up of stack frames.  Gdb assigns numbers to stack frames\n\
+counting from zero for the innermost (currently executing) frame.\n\n\
+At any time gdb identifies one frame as the \"selected\" frame.\n\
+Variable lookups are done with respect to the selected frame.\n\
+When the program being debugged stops, gdb selects the innermost frame.\n\
+The commands below can be used to select other frames by number or address.",
+          &cmdlist);
+  add_cmd ("running", class_run, 0, "Running the program.", &cmdlist);
+
+  add_com ("pwd", class_files, pwd_command,
+          "Print working directory.  This is used for your program as well.");
+  add_com ("cd", class_files, cd_command,
+          "Set working directory to DIR for debugger and program being debugged.\n\
+The change does not take effect for the program being debugged\n\
+until the next time it is started.");
+
+  add_cmd ("prompt", class_support, set_prompt_command,
+          "Change gdb's prompt from the default of \"(gdb)\"",
+          &setlist);
+  add_com ("echo", class_support, echo_command,
+          "Print a constant string.  Give string as argument.\n\
+C escape sequences may be used in the argument.\n\
+No newline is added at the end of the argument;\n\
+use \"\\n\" if you want a newline to be printed.\n\
+Since leading and trailing whitespace are ignored in command arguments,\n\
+if you want to print some you must use \"\\\" before leading whitespace\n\
+to be printed or after trailing whitespace.");
+  add_com ("document", class_support, document_command,
+          "Document a user-defined command.\n\
+Give command name as argument.  Give documentation on following lines.\n\
+End with a line of just \"end\".");
+  add_com ("define", class_support, define_command,
+          "Define a new command name.  Command name is argument.\n\
+Definition appears on following lines, one command per line.\n\
+End with a line of just \"end\".\n\
+Use the \"document\" command to give documentation for the new command.\n\
+Commands defined in this way do not take arguments.");
+
+  add_com ("source", class_support, source_command,
+          "Read commands from a file named FILE.\n\
+Note that the file \".gdbinit\" is read automatically in this way\n\
+when gdb is started.");
+  add_com ("quit", class_support, quit_command, "Exit gdb.");
+  add_com ("help", class_support, help_command, "Print list of commands.");
+  add_com_alias ("q", "quit", class_support, 1);
+  add_com_alias ("h", "help", class_support, 1);
+  add_com ("while", class_support, while_command,
+          "execute following commands while condition is true.\n\
+Expression for condition follows \"while\" keyword.");
+  add_com ("if", class_support, if_command,
+          "execute following commands if condition is true.\n\
+Expression for condition follows \"if\" keyword.");
+  add_cmd ("verbose", class_support, set_verbose_command,
+          "Change the number of informational messages gdb prints.",
+          &setlist);
+  add_info ("verbose", verbose_info,
+           "Status of gdb's verbose printing option.\n");
+
+  add_com ("dump-me", class_obscure, dump_me_command,
+          "Get fatal error; make debugger dump its core.");
+
+  add_cmd ("editing", class_support, set_editing,
+          "Enable or disable command line editing.\n\
+Use \"on\" to enable to enable the editing, and \"off\" to disable it.\n\
+Without an argument, command line editing is enabled.", &setlist);
+
+  add_prefix_cmd ("history", class_support, set_history,
+                 "Generic command for setting command history parameters.",
+                 &sethistlist, "set history ", 0, &setlist);
+
+  add_cmd ("expansion", no_class, set_history_expansion,
+          "Enable or disable history expansion on command input.\n\
+Without an argument, history expansion is enabled.", &sethistlist);
+
+  add_cmd ("write", no_class, set_history_write,
+          "Enable or disable saving of the history record on exit.\n\
+Use \"on\" to enable to enable the saving, and \"off\" to disable it.\n\
+Without an argument, saving is enabled.", &sethistlist);
+
+  add_cmd ("size", no_class, set_history_size,
+          "Set the size of the command history, \n\
+ie. the number of previous commands to keep a record of.", &sethistlist);
+
+  add_cmd ("filename", no_class, set_history_filename,
+          "Set the filename in which to record the command history\n\
+ (the list of previous commands of which a record is kept).", &sethistlist);
+
+  add_prefix_cmd ("info", class_info, info_command,
+                 "Generic command for printing status.",
+                 &infolist, "info ", 0, &cmdlist);
+  add_com_alias ("i", "info", class_info, 1);
+
+  add_info ("editing", editing_info, "Status of command editor.");
+
+  add_info ("version", version_info, "Report what version of GDB this is.");
+}