ODR warnings for "struct coff_symbol"
[binutils-gdb.git] / gdb / source.c
index 066666c7ca3f0ca50428c545c7d6a40bf03bf25c..8691113c729019e1dc9894df7fd30fc9f09f7e17 100644 (file)
@@ -1,5 +1,5 @@
 /* List lines of source files for GDB, the GNU debugger.
-   Copyright (C) 1986-2019 Free Software Foundation, Inc.
+   Copyright (C) 1986-2022 Free Software Foundation, Inc.
 
    This file is part of GDB.
 
@@ -31,7 +31,7 @@
 #include <sys/types.h>
 #include <fcntl.h>
 #include "gdbcore.h"
-#include "gdb_regex.h"
+#include "gdbsupport/gdb_regex.h"
 #include "symfile.h"
 #include "objfiles.h"
 #include "annotate.h"
 #include "filenames.h"         /* for DOSish file names */
 #include "completer.h"
 #include "ui-out.h"
-#include "readline/readline.h"
+#include "readline/tilde.h"
 #include "gdbsupport/enum-flags.h"
 #include "gdbsupport/scoped_fd.h"
 #include <algorithm>
 #include "gdbsupport/pathstuff.h"
 #include "source-cache.h"
+#include "cli/cli-style.h"
+#include "observable.h"
+#include "build-id.h"
+#include "debuginfod-support.h"
+#include "gdbsupport/buildargv.h"
 
 #define OPEN_MODE (O_RDONLY | O_BINARY)
 #define FDOPEN_MODE FOPEN_RB
 /* Path of directories to search for source files.
    Same format as the PATH environment variable's value.  */
 
-char *source_path;
+std::string source_path;
 
 /* Support for source path substitution commands.  */
 
 struct substitute_path_rule
 {
-  char *from;
-  char *to;
-  struct substitute_path_rule *next;
+  substitute_path_rule (const char *from_, const char *to_)
+    : from (from_),
+      to (to_)
+  {
+  }
+
+  std::string from;
+  std::string to;
 };
 
-static struct substitute_path_rule *substitute_path_rules = NULL;
+static std::list<substitute_path_rule> substitute_path_rules;
 
-/* Symtab of default file for listing lines of.  */
+/* An instance of this is attached to each program space.  */
 
-static struct symtab *current_source_symtab;
+struct current_source_location
+{
+public:
+
+  current_source_location () = default;
+
+  /* Set the value.  */
+  void set (struct symtab *s, int l)
+  {
+    m_symtab = s;
+    m_line = l;
+    gdb::observers::current_source_symtab_and_line_changed.notify ();
+  }
+
+  /* Get the symtab.  */
+  struct symtab *symtab () const
+  {
+    return m_symtab;
+  }
+
+  /* Get the line number.  */
+  int line () const
+  {
+    return m_line;
+  }
 
-/* Default next line to list.  */
+private:
 
-static int current_source_line;
+  /* Symtab of default file for listing lines of.  */
 
-static struct program_space *current_source_pspace;
+  struct symtab *m_symtab = nullptr;
+
+  /* Default next line to list.  */
+
+  int m_line = 0;
+};
+
+static program_space_key<current_source_location> current_source_key;
 
 /* Default number of lines to print with commands like "list".
    This is based on guessing how many long (i.e. more than chars_per_line
@@ -87,10 +128,10 @@ static void
 show_lines_to_list (struct ui_file *file, int from_tty,
                    struct cmd_list_element *c, const char *value)
 {
-  fprintf_filtered (file,
-                   _("Number of source lines gdb "
-                     "will list by default is %s.\n"),
-                   value);
+  gdb_printf (file,
+             _("Number of source lines gdb "
+               "will list by default is %s.\n"),
+             value);
 }
 
 /* Possible values of 'set filename-display'.  */
@@ -111,9 +152,23 @@ static void
 show_filename_display_string (struct ui_file *file, int from_tty,
                              struct cmd_list_element *c, const char *value)
 {
-  fprintf_filtered (file, _("Filenames are displayed as \"%s\".\n"), value);
+  gdb_printf (file, _("Filenames are displayed as \"%s\".\n"), value);
 }
+
+/* When true GDB will stat and open source files as required, but when
+   false, GDB will avoid accessing source files as much as possible.  */
+
+static bool source_open = true;
+
+/* Implement 'show source open'.  */
+
+static void
+show_source_open (struct ui_file *file, int from_tty,
+                 struct cmd_list_element *c, const char *value)
+{
+  gdb_printf (file, _("Source opening is \"%s\".\n"), value);
+}
+
 /* Line number of last line printed.  Default for various commands.
    current_source_line is usually, but not always, the same as this.  */
 
@@ -162,6 +217,19 @@ get_lines_to_list (void)
   return lines_to_list;
 }
 
+/* A helper to return the current source location object for PSPACE,
+   creating it if it does not exist.  */
+
+static current_source_location *
+get_source_location (program_space *pspace)
+{
+  current_source_location *loc
+    = current_source_key.get (pspace);
+  if (loc == nullptr)
+    loc = current_source_key.emplace (pspace);
+  return loc;
+}
+
 /* Return the current source file for listing and next line to list.
    NOTE: The returned sal pc and end fields are not valid.  */
    
@@ -169,10 +237,11 @@ struct symtab_and_line
 get_current_source_symtab_and_line (void)
 {
   symtab_and_line cursal;
+  current_source_location *loc = get_source_location (current_program_space);
 
-  cursal.pspace = current_source_pspace;
-  cursal.symtab = current_source_symtab;
-  cursal.line = current_source_line;
+  cursal.pspace = current_program_space;
+  cursal.symtab = loc->symtab ();
+  cursal.line = loc->line ();
   cursal.pc = 0;
   cursal.end = 0;
   
@@ -194,7 +263,8 @@ set_default_source_symtab_and_line (void)
     error (_("No symbol table is loaded.  Use the \"file\" command."));
 
   /* Pull in a current source symtab if necessary.  */
-  if (current_source_symtab == 0)
+  current_source_location *loc = get_source_location (current_program_space);
+  if (loc->symtab () == nullptr)
     select_source_symtab (0);
 }
 
@@ -208,15 +278,15 @@ set_current_source_symtab_and_line (const symtab_and_line &sal)
 {
   symtab_and_line cursal;
 
-  cursal.pspace = current_source_pspace;
-  cursal.symtab = current_source_symtab;
-  cursal.line = current_source_line;
+  current_source_location *loc = get_source_location (sal.pspace);
+
+  cursal.pspace = sal.pspace;
+  cursal.symtab = loc->symtab ();
+  cursal.line = loc->line ();
   cursal.pc = 0;
   cursal.end = 0;
 
-  current_source_pspace = sal.pspace;
-  current_source_symtab = sal.symtab;
-  current_source_line = sal.line;
+  loc->set (sal.symtab, sal.line);
 
   /* Force the next "list" to center around the current line.  */
   clear_lines_listed_range ();
@@ -229,8 +299,8 @@ set_current_source_symtab_and_line (const symtab_and_line &sal)
 void
 clear_current_source_symtab_and_line (void)
 {
-  current_source_symtab = 0;
-  current_source_line = 0;
+  current_source_location *loc = get_source_location (current_program_space);
+  loc->set (nullptr, 0);
 }
 
 /* See source.h.  */
@@ -240,66 +310,67 @@ select_source_symtab (struct symtab *s)
 {
   if (s)
     {
-      current_source_symtab = s;
-      current_source_line = 1;
-      current_source_pspace = SYMTAB_PSPACE (s);
+      current_source_location *loc
+       = get_source_location (s->compunit ()->objfile ()->pspace);
+      loc->set (s, 1);
       return;
     }
 
-  if (current_source_symtab)
+  current_source_location *loc = get_source_location (current_program_space);
+  if (loc->symtab () != nullptr)
     return;
 
   /* Make the default place to list be the function `main'
      if one exists.  */
-  if (lookup_symbol (main_name (), 0, VAR_DOMAIN, 0).symbol)
+  block_symbol bsym = lookup_symbol (main_name (), 0, VAR_DOMAIN, 0);
+  if (bsym.symbol != nullptr && bsym.symbol->aclass () == LOC_BLOCK)
     {
-      std::vector<symtab_and_line> sals
-       = decode_line_with_current_source (main_name (),
-                                          DECODE_LINE_FUNFIRSTLINE);
-      const symtab_and_line &sal = sals[0];
-      current_source_pspace = sal.pspace;
-      current_source_symtab = sal.symtab;
-      current_source_line = std::max (sal.line - (lines_to_list - 1), 1);
-      if (current_source_symtab)
-       return;
+      symtab_and_line sal = find_function_start_sal (bsym.symbol, true);
+      if (sal.symtab == NULL)
+       /* We couldn't find the location of `main', possibly due to missing
+          line number info, fall back to line 1 in the corresponding file.  */
+       loc->set (bsym.symbol->symtab (), 1);
+      else
+       loc->set (sal.symtab, std::max (sal.line - (lines_to_list - 1), 1));
+      return;
     }
 
   /* Alright; find the last file in the symtab list (ignoring .h's
      and namespace symtabs).  */
 
-  current_source_line = 1;
+  struct symtab *new_symtab = nullptr;
 
   for (objfile *ofp : current_program_space->objfiles ())
     {
       for (compunit_symtab *cu : ofp->compunits ())
        {
-         for (symtab *symtab : compunit_filetabs (cu))
+         for (symtab *symtab : cu->filetabs ())
            {
              const char *name = symtab->filename;
              int len = strlen (name);
 
              if (!(len > 2 && (strcmp (&name[len - 2], ".h") == 0
                                || strcmp (name, "<<C++-namespaces>>") == 0)))
-               {
-                 current_source_pspace = current_program_space;
-                 current_source_symtab = symtab;
-               }
+               new_symtab = symtab;
            }
        }
     }
 
-  if (current_source_symtab)
+  loc->set (new_symtab, 1);
+  if (new_symtab != nullptr)
     return;
 
   for (objfile *objfile : current_program_space->objfiles ())
     {
-      if (objfile->sf)
-       s = objfile->sf->qf->find_last_source_symtab (objfile);
+      s = objfile->find_last_source_symtab ();
       if (s)
-       current_source_symtab = s;
+       new_symtab = s;
+    }
+  if (new_symtab != nullptr)
+    {
+      loc->set (new_symtab,1);
+      return;
     }
-  if (current_source_symtab)
-    return;
 
   error (_("Can't find a default source file"));
 }
@@ -314,17 +385,15 @@ set_directories_command (const char *args,
 {
   /* This is the value that was set.
      It needs to be processed to maintain $cdir:$cwd and remove dups.  */
-  char *set_path = source_path;
+  std::string set_path = source_path;
 
   /* We preserve the invariant that $cdir:$cwd begins life at the end of
      the list by calling init_source_path.  If they appear earlier in
      SET_PATH then mod_path will move them appropriately.
      mod_path will also remove duplicates.  */
   init_source_path ();
-  if (*set_path != '\0')
-    mod_path (set_path, &source_path);
-
-  xfree (set_path);
+  if (!set_path.empty ())
+    mod_path (set_path.c_str (), source_path);
 }
 
 /* Print the list of source directories.
@@ -332,11 +401,11 @@ set_directories_command (const char *args,
    function.  */
 
 static void
-show_directories_1 (char *ignore, int from_tty)
+show_directories_1 (ui_file *file, char *ignore, int from_tty)
 {
-  puts_filtered ("Source directories searched: ");
-  puts_filtered (source_path);
-  puts_filtered ("\n");
+  gdb_puts ("Source directories searched: ", file);
+  gdb_puts (source_path.c_str (), file);
+  gdb_puts ("\n", file);
 }
 
 /* Handler for "show directories" command.  */
@@ -345,7 +414,7 @@ static void
 show_directories_command (struct ui_file *file, int from_tty,
                          struct cmd_list_element *c, const char *value)
 {
-  show_directories_1 (NULL, from_tty);
+  show_directories_1 (file, NULL, from_tty);
 }
 
 /* See source.h.  */
@@ -355,7 +424,7 @@ forget_cached_source_info_for_objfile (struct objfile *objfile)
 {
   for (compunit_symtab *cu : objfile->compunits ())
     {
-      for (symtab *s : compunit_filetabs (cu))
+      for (symtab *s : cu->filetabs ())
        {
          if (s->fullname != NULL)
            {
@@ -365,8 +434,7 @@ forget_cached_source_info_for_objfile (struct objfile *objfile)
        }
     }
 
-  if (objfile->sf)
-    objfile->sf->qf->forget_cached_source_info (objfile);
+  objfile->forget_cached_source_info ();
 }
 
 /* See source.h.  */
@@ -374,9 +442,7 @@ forget_cached_source_info_for_objfile (struct objfile *objfile)
 void
 forget_cached_source_info (void)
 {
-  struct program_space *pspace;
-
-  ALL_PSPACES (pspace)
+  for (struct program_space *pspace : program_spaces)
     for (objfile *objfile : pspace->objfiles ())
       {
        forget_cached_source_info_for_objfile (objfile);
@@ -389,10 +455,7 @@ forget_cached_source_info (void)
 void
 init_source_path (void)
 {
-  char buf[20];
-
-  xsnprintf (buf, sizeof (buf), "$cdir%c$cwd", DIRNAME_SEPARATOR);
-  source_path = xstrdup (buf);
+  source_path = string_printf ("$cdir%c$cwd", DIRNAME_SEPARATOR);
   forget_cached_source_info ();
 }
 
@@ -401,23 +464,30 @@ init_source_path (void)
 static void
 directory_command (const char *dirname, int from_tty)
 {
+  bool value_changed = false;
   dont_repeat ();
   /* FIXME, this goes to "delete dir"...  */
   if (dirname == 0)
     {
       if (!from_tty || query (_("Reinitialize source path to empty? ")))
        {
-         xfree (source_path);
          init_source_path ();
+         value_changed = true;
        }
     }
   else
     {
-      mod_path (dirname, &source_path);
+      mod_path (dirname, source_path);
       forget_cached_source_info ();
+      value_changed = true;
+    }
+  if (value_changed)
+    {
+      gdb::observers::command_param_changed.notify ("directories",
+                                                   source_path.c_str ());
+      if (from_tty)
+       show_directories_1 (gdb_stdout, (char *) 0, from_tty);
     }
-  if (from_tty)
-    show_directories_1 ((char *) 0, from_tty);
 }
 
 /* Add a path given with the -d command line switch.
@@ -426,13 +496,13 @@ directory_command (const char *dirname, int from_tty)
 void
 directory_switch (const char *dirname, int from_tty)
 {
-  add_path (dirname, &source_path, 0);
+  add_path (dirname, source_path, 0);
 }
 
 /* Add zero or more directories to the front of an arbitrary path.  */
 
 void
-mod_path (const char *dirname, char **which_path)
+mod_path (const char *dirname, std::string &which_path)
 {
   add_path (dirname, which_path, 1);
 }
@@ -467,21 +537,22 @@ add_path (const char *dirname, char **which_path, int parse_separators)
 
   for (const gdb::unique_xmalloc_ptr<char> &name_up : dir_vec)
     {
-      char *name = name_up.get ();
+      const char *name = name_up.get ();
       char *p;
       struct stat st;
-      gdb::unique_xmalloc_ptr<char> new_name_holder;
+      std::string new_name_holder;
 
       /* Spaces and tabs will have been removed by buildargv().
-         NAME is the start of the directory.
+        NAME is the start of the directory.
         P is the '\0' following the end.  */
-      p = name + strlen (name);
+      p = name_up.get () + strlen (name);
 
       while (!(IS_DIR_SEPARATOR (*name) && p <= name + 1)      /* "/" */
 #ifdef HAVE_DOS_BASED_FILE_SYSTEM
       /* On MS-DOS and MS-Windows, h:\ is different from h: */
             && !(p == name + 3 && name[1] == ':')              /* "d:/" */
 #endif
+            && p > name
             && IS_DIR_SEPARATOR (p[-1]))
        /* Sigh.  "foo/" => "foo" */
        --p;
@@ -515,18 +586,21 @@ add_path (const char *dirname, char **which_path, int parse_separators)
            break;
        }
 
+      if (name[0] == '\0')
+        goto skip_dup;
       if (name[0] == '~')
-       new_name_holder.reset (tilde_expand (name));
+       new_name_holder
+         = gdb::unique_xmalloc_ptr<char[]> (tilde_expand (name)).get ();
 #ifdef HAVE_DOS_BASED_FILE_SYSTEM
       else if (IS_ABSOLUTE_PATH (name) && p == name + 2) /* "d:" => "d:." */
-       new_name_holder.reset (concat (name, ".", (char *) NULL));
+       new_name_holder = std::string (name) + ".";
 #endif
       else if (!IS_ABSOLUTE_PATH (name) && name[0] != '$')
-       new_name_holder.reset (concat (current_directory, SLASH_STRING, name,
-                                      (char *) NULL));
+       new_name_holder = gdb_abspath (name);
       else
-       new_name_holder.reset (savestring (name, p - name));
-      name = new_name_holder.get ();
+       new_name_holder = std::string (name, p - name);
+
+      name = new_name_holder.c_str ();
 
       /* Unless it's a variable, check existence.  */
       if (name[0] != '$')
@@ -544,7 +618,7 @@ add_path (const char *dirname, char **which_path, int parse_separators)
            {
              int save_errno = errno;
 
-             fprintf_unfiltered (gdb_stderr, "Warning: ");
+             gdb_printf (gdb_stderr, "Warning: ");
              print_sys_errmsg (name, save_errno);
            }
          else if ((st.st_mode & S_IFMT) != S_IFDIR)
@@ -594,15 +668,10 @@ add_path (const char *dirname, char **which_path, int parse_separators)
           more.  */
        if (prefix)
          {
-           char *temp, c;
-
-           c = old[prefix];
-           old[prefix] = '\0';
-           temp = concat (old, tinybuf, name, (char *)NULL);
-           old[prefix] = c;
-           *which_path = concat (temp, "", &old[prefix], (char *) NULL);
-           prefix = strlen (temp);
-           xfree (temp);
+           std::string temp = std::string (old, prefix) + tinybuf + name;
+           *which_path = concat (temp.c_str (), &old[prefix],
+                                 (char *) nullptr);
+           prefix = temp.length ();
          }
        else
          {
@@ -618,42 +687,86 @@ add_path (const char *dirname, char **which_path, int parse_separators)
     }
 }
 
+/* add_path would need to be re-written to work on an std::string, but this is
+   not trivial.  Hence this overload which copies to a `char *` and back.  */
+
+void
+add_path (const char *dirname, std::string &which_path, int parse_separators)
+{
+  char *which_path_copy = xstrdup (which_path.data ());
+  add_path (dirname, &which_path_copy, parse_separators);
+  which_path = which_path_copy;
+  xfree (which_path_copy);
+}
 
 static void
 info_source_command (const char *ignore, int from_tty)
 {
-  struct symtab *s = current_source_symtab;
+  current_source_location *loc
+    = get_source_location (current_program_space);
+  struct symtab *s = loc->symtab ();
   struct compunit_symtab *cust;
 
   if (!s)
     {
-      printf_filtered (_("No current source file.\n"));
+      gdb_printf (_("No current source file.\n"));
       return;
     }
 
-  cust = SYMTAB_COMPUNIT (s);
-  printf_filtered (_("Current source file is %s\n"), s->filename);
-  if (SYMTAB_DIRNAME (s) != NULL)
-    printf_filtered (_("Compilation directory is %s\n"), SYMTAB_DIRNAME (s));
+  cust = s->compunit ();
+  gdb_printf (_("Current source file is %s\n"), s->filename);
+  if (s->compunit ()->dirname () != NULL)
+    gdb_printf (_("Compilation directory is %s\n"), s->compunit ()->dirname ());
   if (s->fullname)
-    printf_filtered (_("Located in %s\n"), s->fullname);
+    gdb_printf (_("Located in %s\n"), s->fullname);
   const std::vector<off_t> *offsets;
   if (g_source_cache.get_line_charpos (s, &offsets))
-    printf_filtered (_("Contains %d line%s.\n"), (int) offsets->size (),
-                    offsets->size () == 1 ? "" : "s");
-
-  printf_filtered (_("Source language is %s.\n"), language_str (s->language));
-  printf_filtered (_("Producer is %s.\n"),
-                  COMPUNIT_PRODUCER (cust) != NULL
-                  ? COMPUNIT_PRODUCER (cust) : _("unknown"));
-  printf_filtered (_("Compiled with %s debugging format.\n"),
-                  COMPUNIT_DEBUGFORMAT (cust));
-  printf_filtered (_("%s preprocessor macro info.\n"),
-                  COMPUNIT_MACRO_TABLE (cust) != NULL
-                  ? "Includes" : "Does not include");
+    gdb_printf (_("Contains %d line%s.\n"), (int) offsets->size (),
+               offsets->size () == 1 ? "" : "s");
+
+  gdb_printf (_("Source language is %s.\n"),
+             language_str (s->language ()));
+  gdb_printf (_("Producer is %s.\n"),
+             (cust->producer ()) != nullptr
+             ? cust->producer () : _("unknown"));
+  gdb_printf (_("Compiled with %s debugging format.\n"),
+             cust->debugformat ());
+  gdb_printf (_("%s preprocessor macro info.\n"),
+             (cust->macro_table () != nullptr
+              ? "Includes" : "Does not include"));
 }
 \f
 
+/* Helper function to remove characters from the start of PATH so that
+   PATH can then be appended to a directory name.  We remove leading drive
+   letters (for dos) as well as leading '/' characters and './'
+   sequences.  */
+
+static const char *
+prepare_path_for_appending (const char *path)
+{
+  /* For dos paths, d:/foo -> /foo, and d:foo -> foo.  */
+  if (HAS_DRIVE_SPEC (path))
+    path = STRIP_DRIVE_SPEC (path);
+
+  const char *old_path;
+  do
+    {
+      old_path = path;
+
+      /* /foo => foo, to avoid multiple slashes that Emacs doesn't like.  */
+      while (IS_DIR_SEPARATOR(path[0]))
+       path++;
+
+      /* ./foo => foo */
+      while (path[0] == '.' && IS_DIR_SEPARATOR (path[1]))
+       path += 2;
+    }
+  while (old_path != path);
+
+  return path;
+}
+
 /* Open a file named STRING, searching path PATH (dir names sep by some char)
    using mode MODE in the calls to open.  You cannot use this function to
    create files (O_CREAT).
@@ -729,7 +842,7 @@ openp (const char *path, openp_flags opts, const char *string,
        {
          filename = (char *) alloca (strlen (string) + 1);
          strcpy (filename, string);
-         fd = gdb_open_cloexec (filename, mode, 0);
+         fd = gdb_open_cloexec (filename, mode, 0).release ();
          if (fd >= 0)
            goto done;
          last_errno = errno;
@@ -747,17 +860,9 @@ openp (const char *path, openp_flags opts, const char *string,
            goto done;
     }
 
-  /* For dos paths, d:/foo -> /foo, and d:foo -> foo.  */
-  if (HAS_DRIVE_SPEC (string))
-    string = STRIP_DRIVE_SPEC (string);
-
-  /* /foo => foo, to avoid multiple slashes that Emacs doesn't like.  */
-  while (IS_DIR_SEPARATOR(string[0]))
-    string++;
-
-  /* ./foo => foo */
-  while (string[0] == '.' && IS_DIR_SEPARATOR (string[1]))
-    string += 2;
+  /* Remove characters from the start of PATH that we don't need when PATH
+     is appended to a directory name.  */
+  string = prepare_path_for_appending (string);
 
   alloclen = strlen (path) + strlen (string) + 2;
   filename = (char *) alloca (alloclen);
@@ -829,7 +934,7 @@ openp (const char *path, openp_flags opts, const char *string,
 
       if (is_regular_file (filename, &reg_file_errno))
        {
-         fd = gdb_open_cloexec (filename, mode, 0);
+         fd = gdb_open_cloexec (filename, mode, 0).release ();
          if (fd >= 0)
            break;
          last_errno = errno;
@@ -847,7 +952,8 @@ done:
       else if ((opts & OPF_RETURN_REALPATH) != 0)
        *filename_opened = gdb_realpath (filename);
       else
-       *filename_opened = gdb_abspath (filename);
+       *filename_opened
+         = make_unique_xstrdup (gdb_abspath (filename).c_str ());
     }
 
   errno = last_errno;
@@ -872,7 +978,7 @@ source_full_path_of (const char *filename,
 {
   int fd;
 
-  fd = openp (source_path,
+  fd = openp (source_path.c_str (),
              OPF_TRY_CWD_FIRST | OPF_SEARCH_IN_PATH | OPF_RETURN_REALPATH,
              filename, O_RDONLY, full_pathname);
   if (fd < 0)
@@ -890,9 +996,9 @@ source_full_path_of (const char *filename,
 
 static int
 substitute_path_rule_matches (const struct substitute_path_rule *rule,
-                              const char *path)
+                             const char *path)
 {
-  const int from_len = strlen (rule->from);
+  const int from_len = rule->from.length ();
   const int path_len = strlen (path);
 
   if (path_len < from_len)
@@ -901,7 +1007,7 @@ substitute_path_rule_matches (const struct substitute_path_rule *rule,
   /* The substitution rules are anchored at the start of the path,
      so the path should start with rule->from.  */
 
-  if (filename_ncmp (path, rule->from, from_len) != 0)
+  if (filename_ncmp (path, rule->from.c_str (), from_len) != 0)
     return 0;
 
   /* Make sure that the region in the path that matches the substitution
@@ -920,12 +1026,11 @@ substitute_path_rule_matches (const struct substitute_path_rule *rule,
 static struct substitute_path_rule *
 get_substitute_path_rule (const char *path)
 {
-  struct substitute_path_rule *rule = substitute_path_rules;
+  for (substitute_path_rule &rule : substitute_path_rules)
+    if (substitute_path_rule_matches (&rule, path))
+      return &rule;
 
-  while (rule != NULL && !substitute_path_rule_matches (rule, path))
-    rule = rule->next;
-
-  return rule;
+  return nullptr;
 }
 
 /* If the user specified a source path substitution rule that applies
@@ -938,22 +1043,14 @@ gdb::unique_xmalloc_ptr<char>
 rewrite_source_path (const char *path)
 {
   const struct substitute_path_rule *rule = get_substitute_path_rule (path);
-  char *new_path;
-  int from_len;
-  
-  if (rule == NULL)
-    return NULL;
 
-  from_len = strlen (rule->from);
+  if (rule == nullptr)
+    return nullptr;
 
   /* Compute the rewritten path and return it.  */
 
-  new_path =
-    (char *) xmalloc (strlen (path) + 1 + strlen (rule->to) - from_len);
-  strcpy (new_path, rule->to);
-  strcat (new_path, path + from_len);
-
-  return gdb::unique_xmalloc_ptr<char> (new_path);
+  return (gdb::unique_xmalloc_ptr<char>
+         (concat (rule->to.c_str (), path + rule->from.length (), nullptr)));
 }
 
 /* See source.h.  */
@@ -963,31 +1060,33 @@ find_and_open_source (const char *filename,
                      const char *dirname,
                      gdb::unique_xmalloc_ptr<char> *fullname)
 {
-  char *path = source_path;
+  const char *path = source_path.c_str ();
+  std::string expanded_path_holder;
   const char *p;
-  int result;
 
-  /* Quick way out if we already know its full name.  */
+  /* If reading of source files is disabled then return a result indicating
+     the attempt to read this source file failed.  GDB will then display
+     the filename and line number instead.  */
+  if (!source_open)
+    return scoped_fd (-1);
 
+  /* Quick way out if we already know its full name.  */
   if (*fullname)
     {
       /* The user may have requested that source paths be rewritten
-         according to substitution rules he provided.  If a substitution
-         rule applies to this path, then apply it.  */
+        according to substitution rules he provided.  If a substitution
+        rule applies to this path, then apply it.  */
       gdb::unique_xmalloc_ptr<char> rewritten_fullname
        = rewrite_source_path (fullname->get ());
 
       if (rewritten_fullname != NULL)
        *fullname = std::move (rewritten_fullname);
 
-      result = gdb_open_cloexec (fullname->get (), OPEN_MODE, 0);
-      if (result >= 0)
+      scoped_fd result = gdb_open_cloexec (fullname->get (), OPEN_MODE, 0);
+      if (result.get () >= 0)
        {
-         if (basenames_may_differ)
-           *fullname = gdb_realpath (fullname->get ());
-         else
-           *fullname = gdb_abspath (fullname->get ());
-         return scoped_fd (result);
+         *fullname = gdb_realpath (fullname->get ());
+         return result;
        }
 
       /* Didn't work -- free old one, try again.  */
@@ -998,7 +1097,7 @@ find_and_open_source (const char *filename,
   if (dirname != NULL)
     {
       /* If necessary, rewrite the compilation directory name according
-         to the source path substitution rules specified by the user.  */
+        to the source path substitution rules specified by the user.  */
 
       rewritten_dirname = rewrite_source_path (dirname);
 
@@ -1008,19 +1107,22 @@ find_and_open_source (const char *filename,
       /* Replace a path entry of $cdir with the compilation directory
         name.  */
 #define        cdir_len        5
-      p = strstr (source_path, "$cdir");
+      p = strstr (source_path.c_str (), "$cdir");
       if (p && (p == path || p[-1] == DIRNAME_SEPARATOR)
          && (p[cdir_len] == DIRNAME_SEPARATOR || p[cdir_len] == '\0'))
        {
-         int len;
-
-         path = (char *)
-           alloca (strlen (source_path) + 1 + strlen (dirname) + 1);
-         len = p - source_path;
-         strncpy (path, source_path, len);     /* Before $cdir */
-         strcpy (path + len, dirname);         /* new stuff */
-         strcat (path + len, source_path + len + cdir_len);    /* After
-                                                                  $cdir */
+         int len = p - source_path.c_str ();
+
+         /* Before $cdir */
+         expanded_path_holder = source_path.substr (0, len);
+
+         /* new stuff */
+         expanded_path_holder += dirname;
+
+         /* After $cdir */
+         expanded_path_holder += source_path.c_str () + len + cdir_len;
+
+         path = expanded_path_holder.c_str ();
        }
     }
 
@@ -1030,16 +1132,39 @@ find_and_open_source (const char *filename,
   if (rewritten_filename != NULL)
     filename = rewritten_filename.get ();
 
-  openp_flags flags = OPF_SEARCH_IN_PATH;
-  if (basenames_may_differ)
-    flags |= OPF_RETURN_REALPATH;
-  result = openp (path, flags, filename, OPEN_MODE, fullname);
+  /* Try to locate file using filename.  */
+  int result = openp (path, OPF_SEARCH_IN_PATH | OPF_RETURN_REALPATH, filename,
+                     OPEN_MODE, fullname);
+  if (result < 0 && dirname != NULL)
+    {
+      /* Remove characters from the start of PATH that we don't need when
+        PATH is appended to a directory name.  */
+      const char *filename_start = prepare_path_for_appending (filename);
+
+      /* Try to locate file using compilation dir + filename.  This is
+        helpful if part of the compilation directory was removed,
+        e.g. using gcc's -fdebug-prefix-map, and we have added the missing
+        prefix to source_path.  */
+      std::string cdir_filename (dirname);
+
+      /* Remove any trailing directory separators.  */
+      while (IS_DIR_SEPARATOR (cdir_filename.back ()))
+       cdir_filename.pop_back ();
+
+      /* Add our own directory separator.  */
+      cdir_filename.append (SLASH_STRING);
+      cdir_filename.append (filename_start);
+
+      result = openp (path, OPF_SEARCH_IN_PATH | OPF_RETURN_REALPATH,
+                     cdir_filename.c_str (), OPEN_MODE, fullname);
+    }
   if (result < 0)
     {
       /* Didn't work.  Try using just the basename.  */
       p = lbasename (filename);
       if (p != filename)
-       result = openp (path, flags, p, OPEN_MODE, fullname);
+       result = openp (path, OPF_SEARCH_IN_PATH | OPF_RETURN_REALPATH, p,
+                       OPEN_MODE, fullname);
     }
 
   return scoped_fd (result);
@@ -1048,7 +1173,7 @@ find_and_open_source (const char *filename,
 /* Open a source file given a symtab S.  Returns a file descriptor or
    negative number for error.  
    
-   This function is a convience function to find_and_open_source.  */
+   This function is a convenience function to find_and_open_source.  */
 
 scoped_fd
 open_source_file (struct symtab *s)
@@ -1058,12 +1183,68 @@ open_source_file (struct symtab *s)
 
   gdb::unique_xmalloc_ptr<char> fullname (s->fullname);
   s->fullname = NULL;
-  scoped_fd fd = find_and_open_source (s->filename, SYMTAB_DIRNAME (s),
+  scoped_fd fd = find_and_open_source (s->filename, s->compunit ()->dirname (),
                                       &fullname);
+
+  if (fd.get () < 0)
+    {
+      if (s->compunit () != nullptr)
+       {
+         const objfile *ofp = s->compunit ()->objfile ();
+
+         std::string srcpath;
+         if (IS_ABSOLUTE_PATH (s->filename))
+           srcpath = s->filename;
+         else if (s->compunit ()->dirname () != nullptr)
+           {
+             srcpath = s->compunit ()->dirname ();
+             srcpath += SLASH_STRING;
+             srcpath += s->filename;
+           }
+
+         const struct bfd_build_id *build_id = build_id_bfd_get (ofp->obfd);
+
+         /* Query debuginfod for the source file.  */
+         if (build_id != nullptr && !srcpath.empty ())
+           fd = debuginfod_source_query (build_id->data,
+                                         build_id->size,
+                                         srcpath.c_str (),
+                                         &fullname);
+       }
+    }
+
   s->fullname = fullname.release ();
   return fd;
 }
 
+/* See source.h.  */
+
+gdb::unique_xmalloc_ptr<char>
+find_source_or_rewrite (const char *filename, const char *dirname)
+{
+  gdb::unique_xmalloc_ptr<char> fullname;
+
+  scoped_fd fd = find_and_open_source (filename, dirname, &fullname);
+  if (fd.get () < 0)
+    {
+      /* rewrite_source_path would be applied by find_and_open_source, we
+        should report the pathname where GDB tried to find the file.  */
+
+      if (dirname == nullptr || IS_ABSOLUTE_PATH (filename))
+       fullname.reset (xstrdup (filename));
+      else
+       fullname.reset (concat (dirname, SLASH_STRING,
+                               filename, (char *) nullptr));
+
+      gdb::unique_xmalloc_ptr<char> rewritten
+       = rewrite_source_path (fullname.get ());
+      if (rewritten != nullptr)
+       fullname = std::move (rewritten);
+    }
+
+  return fullname;
+}
+
 /* Finds the fullname that a symtab represents.
 
    This functions finds the fullname and saves it in s->fullname.
@@ -1090,10 +1271,11 @@ symtab_to_fullname (struct symtab *s)
          /* rewrite_source_path would be applied by find_and_open_source, we
             should report the pathname where GDB tried to find the file.  */
 
-         if (SYMTAB_DIRNAME (s) == NULL || IS_ABSOLUTE_PATH (s->filename))
+         if (s->compunit ()->dirname () == nullptr
+             || IS_ABSOLUTE_PATH (s->filename))
            fullname.reset (xstrdup (s->filename));
          else
-           fullname.reset (concat (SYMTAB_DIRNAME (s), SLASH_STRING,
+           fullname.reset (concat (s->compunit ()->dirname (), SLASH_STRING,
                                    s->filename, (char *) NULL));
 
          s->fullname = rewrite_source_path (fullname.get ()).release ();
@@ -1134,13 +1316,16 @@ print_source_lines_base (struct symtab *s, int line, int stopline,
   struct ui_out *uiout = current_uiout;
 
   /* Regardless of whether we can open the file, set current_source_symtab.  */
-  current_source_symtab = s;
-  current_source_line = line;
+  current_source_location *loc
+    = get_source_location (current_program_space);
+
+  loc->set (s, line);
   first_line_listed = line;
+  last_line_listed = line;
 
   /* If printing of source lines is disabled, just print file and line
      number.  */
-  if (uiout->test_flags (ui_source_list))
+  if (uiout->test_flags (ui_source_list) && source_open)
     {
       /* Only prints "No such file or directory" once.  */
       if (s == last_source_visited)
@@ -1188,9 +1373,9 @@ print_source_lines_base (struct symtab *s, int line, int stopline,
             not for TUI.  */
          if (uiout->is_mi_like_p () || uiout->test_flags (ui_source_list))
            uiout->field_string ("file", symtab_to_filename_for_display (s),
-                                ui_out_style_kind::FILE);
+                                file_name_style.style ());
          if (uiout->is_mi_like_p () || !uiout->test_flags (ui_source_list))
-           {
+           {
              const char *s_fullname = symtab_to_fullname (s);
              char *local_fullname;
 
@@ -1201,7 +1386,7 @@ print_source_lines_base (struct symtab *s, int line, int stopline,
              strcpy (local_fullname, s_fullname);
 
              uiout->field_string ("fullname", local_fullname);
-           }
+           }
 
          uiout->text ("\n");
        }
@@ -1225,17 +1410,18 @@ print_source_lines_base (struct symtab *s, int line, int stopline,
     }
 
   const char *iter = lines.c_str ();
+  int new_lineno = loc->line ();
   while (nlines-- > 0 && *iter != '\0')
     {
       char buf[20];
 
-      last_line_listed = current_source_line;
+      last_line_listed = loc->line ();
       if (flags & PRINT_SOURCE_LINES_FILENAME)
-        {
-          uiout->text (symtab_to_filename_for_display (s));
-          uiout->text (":");
-        }
-      xsnprintf (buf, sizeof (buf), "%d\t", current_source_line++);
+       {
+         uiout->text (symtab_to_filename_for_display (s));
+         uiout->text (":");
+       }
+      xsnprintf (buf, sizeof (buf), "%d\t", new_lineno++);
       uiout->text (buf);
 
       while (*iter != '\0')
@@ -1261,7 +1447,7 @@ print_source_lines_base (struct symtab *s, int line, int stopline,
          if (iter > start)
            {
              std::string text (start, iter);
-             uiout->text (text.c_str ());
+             uiout->text (text);
            }
          if (*iter == '\r')
            {
@@ -1290,6 +1476,8 @@ print_source_lines_base (struct symtab *s, int line, int stopline,
        }
       uiout->text ("\n");
     }
+
+  loc->set (loc->symtab (), new_lineno);
 }
 \f
 
@@ -1327,12 +1515,14 @@ info_line_command (const char *arg, int from_tty)
 
   if (arg == 0)
     {
-      curr_sal.symtab = current_source_symtab;
+      current_source_location *loc
+       = get_source_location (current_program_space);
+      curr_sal.symtab = loc->symtab ();
       curr_sal.pspace = current_program_space;
       if (last_line_listed != 0)
        curr_sal.line = last_line_listed;
       else
-       curr_sal.line = current_source_line;
+       curr_sal.line = loc->line ();
 
       sals = curr_sal;
     }
@@ -1356,49 +1546,48 @@ info_line_command (const char *arg, int from_tty)
        {
          struct gdbarch *gdbarch = get_current_arch ();
 
-         printf_filtered (_("No line number information available"));
+         gdb_printf (_("No line number information available"));
          if (sal.pc != 0)
            {
              /* This is useful for "info line *0x7f34".  If we can't tell the
-                user about a source line, at least let them have the symbolic
-                address.  */
-             printf_filtered (" for address ");
-             wrap_here ("  ");
+                user about a source line, at least let them have the symbolic
+                address.  */
+             gdb_printf (" for address ");
+             gdb_stdout->wrap_here (2);
              print_address (gdbarch, sal.pc, gdb_stdout);
            }
          else
-           printf_filtered (".");
-         printf_filtered ("\n");
+           gdb_printf (".");
+         gdb_printf ("\n");
        }
       else if (sal.line > 0
               && find_line_pc_range (sal, &start_pc, &end_pc))
        {
-         struct gdbarch *gdbarch
-           = get_objfile_arch (SYMTAB_OBJFILE (sal.symtab));
+         gdbarch *gdbarch = sal.symtab->compunit ()->objfile ()->arch ();
 
          if (start_pc == end_pc)
            {
-             printf_filtered ("Line %d of \"%s\"",
-                              sal.line,
-                              symtab_to_filename_for_display (sal.symtab));
-             wrap_here ("  ");
-             printf_filtered (" is at address ");
+             gdb_printf ("Line %d of \"%s\"",
+                         sal.line,
+                         symtab_to_filename_for_display (sal.symtab));
+             gdb_stdout->wrap_here (2);
+             gdb_printf (" is at address ");
              print_address (gdbarch, start_pc, gdb_stdout);
-             wrap_here ("  ");
-             printf_filtered (" but contains no code.\n");
+             gdb_stdout->wrap_here (2);
+             gdb_printf (" but contains no code.\n");
            }
          else
            {
-             printf_filtered ("Line %d of \"%s\"",
-                              sal.line,
-                              symtab_to_filename_for_display (sal.symtab));
-             wrap_here ("  ");
-             printf_filtered (" starts at address ");
+             gdb_printf ("Line %d of \"%s\"",
+                         sal.line,
+                         symtab_to_filename_for_display (sal.symtab));
+             gdb_stdout->wrap_here (2);
+             gdb_printf (" starts at address ");
              print_address (gdbarch, start_pc, gdb_stdout);
-             wrap_here ("  ");
-             printf_filtered (" and ends at ");
+             gdb_stdout->wrap_here (2);
+             gdb_printf (" and ends at ");
              print_address (gdbarch, end_pc, gdb_stdout);
-             printf_filtered (".\n");
+             gdb_printf (".\n");
            }
 
          /* x/i should display this line's code.  */
@@ -1409,15 +1598,15 @@ info_line_command (const char *arg, int from_tty)
 
          /* If this is the only line, show the source code.  If it could
             not find the file, don't do anything special.  */
-         if (sals.size () == 1)
+         if (annotation_level > 0 && sals.size () == 1)
            annotate_source_line (sal.symtab, sal.line, 0, start_pc);
        }
       else
        /* Is there any case in which we get here, and have an address
           which the user would want to see?  If we have debugging symbols
           and no line numbers?  */
-       printf_filtered (_("Line number %d is out of range for \"%s\".\n"),
-                        sal.line, symtab_to_filename_for_display (sal.symtab));
+       gdb_printf (_("Line number %d is out of range for \"%s\".\n"),
+                   sal.line, symtab_to_filename_for_display (sal.symtab));
     }
 }
 \f
@@ -1434,12 +1623,17 @@ search_command_helper (const char *regex, int from_tty, bool forward)
   if (msg)
     error (("%s"), msg);
 
-  if (current_source_symtab == 0)
+  current_source_location *loc
+    = get_source_location (current_program_space);
+  if (loc->symtab () == nullptr)
     select_source_symtab (0);
 
-  scoped_fd desc (open_source_file (current_source_symtab));
+  if (!source_open)
+    error (_("source code access disabled"));
+
+  scoped_fd desc (open_source_file (loc->symtab ()));
   if (desc.get () < 0)
-    perror_with_name (symtab_to_filename_for_display (current_source_symtab));
+    perror_with_name (symtab_to_filename_for_display (loc->symtab ()));
 
   int line = (forward
              ? last_line_listed + 1
@@ -1447,12 +1641,12 @@ search_command_helper (const char *regex, int from_tty, bool forward)
 
   const std::vector<off_t> *offsets;
   if (line < 1
-      || !g_source_cache.get_line_charpos (current_source_symtab, &offsets)
+      || !g_source_cache.get_line_charpos (loc->symtab (), &offsets)
       || line > offsets->size ())
     error (_("Expression not found"));
 
   if (lseek (desc.get (), (*offsets)[line - 1], 0) < 0)
-    perror_with_name (symtab_to_filename_for_display (current_source_symtab));
+    perror_with_name (symtab_to_filename_for_display (loc->symtab ()));
 
   gdb_file_up stream = desc.to_file (FDOPEN_MODE);
   clearerr (stream.get ());
@@ -1474,7 +1668,7 @@ search_command_helper (const char *regex, int from_tty, bool forward)
       while (c != '\n' && (c = fgetc (stream.get ())) >= 0);
 
       /* Remove the \r, if any, at the end of the line, otherwise
-         regular expressions that end with $ or \n won't work.  */
+        regular expressions that end with $ or \n won't work.  */
       size_t sz = buf.size ();
       if (sz >= 2 && buf[sz - 2] == '\r')
        {
@@ -1487,9 +1681,9 @@ search_command_helper (const char *regex, int from_tty, bool forward)
       if (re_exec (buf.data ()) > 0)
        {
          /* Match!  */
-         print_source_lines (current_source_symtab, line, line + 1, 0);
+         print_source_lines (loc->symtab (), line, line + 1, 0);
          set_internalvar_integer (lookup_internalvar ("_"), line);
-         current_source_line = std::max (line - lines_to_list / 2, 1);
+         loc->set (loc->symtab (), std::max (line - lines_to_list / 2, 1));
          return;
        }
 
@@ -1503,13 +1697,13 @@ search_command_helper (const char *regex, int from_tty, bool forward)
          if (fseek (stream.get (), (*offsets)[line - 1], 0) < 0)
            {
              const char *filename
-               = symtab_to_filename_for_display (current_source_symtab);
+               = symtab_to_filename_for_display (loc->symtab ());
              perror_with_name (filename);
            }
        }
     }
 
-  printf_filtered (_("Expression not found\n"));
+  gdb_printf (_("Expression not found\n"));
 }
 
 static void
@@ -1538,79 +1732,13 @@ strip_trailing_directory_separator (char *path)
     path[last] = '\0';
 }
 
-/* Return the path substitution rule that matches FROM.
-   Return NULL if no rule matches.  */
-
-static struct substitute_path_rule *
-find_substitute_path_rule (const char *from)
-{
-  struct substitute_path_rule *rule = substitute_path_rules;
-
-  while (rule != NULL)
-    {
-      if (FILENAME_CMP (rule->from, from) == 0)
-        return rule;
-      rule = rule->next;
-    }
-
-  return NULL;
-}
-
 /* Add a new substitute-path rule at the end of the current list of rules.
    The new rule will replace FROM into TO.  */
 
 void
-add_substitute_path_rule (char *from, char *to)
+add_substitute_path_rule (const char *from, const char *to)
 {
-  struct substitute_path_rule *rule;
-  struct substitute_path_rule *new_rule = XNEW (struct substitute_path_rule);
-
-  new_rule->from = xstrdup (from);
-  new_rule->to = xstrdup (to);
-  new_rule->next = NULL;
-
-  /* If the list of rules are empty, then insert the new rule
-     at the head of the list.  */
-
-  if (substitute_path_rules == NULL)
-    {
-      substitute_path_rules = new_rule;
-      return;
-    }
-
-  /* Otherwise, skip to the last rule in our list and then append
-     the new rule.  */
-
-  rule = substitute_path_rules;
-  while (rule->next != NULL)
-    rule = rule->next;
-
-  rule->next = new_rule;
-}
-
-/* Remove the given source path substitution rule from the current list
-   of rules.  The memory allocated for that rule is also deallocated.  */
-
-static void
-delete_substitute_path_rule (struct substitute_path_rule *rule)
-{
-  if (rule == substitute_path_rules)
-    substitute_path_rules = rule->next;
-  else
-    {
-      struct substitute_path_rule *prev = substitute_path_rules;
-
-      while (prev != NULL && prev->next != rule)
-        prev = prev->next;
-
-      gdb_assert (prev != NULL);
-
-      prev->next = rule->next;
-    }
-
-  xfree (rule->from);
-  xfree (rule->to);
-  xfree (rule);
+  substitute_path_rules.emplace_back (from, to);
 }
 
 /* Implement the "show substitute-path" command.  */
@@ -1618,7 +1746,6 @@ delete_substitute_path_rule (struct substitute_path_rule *rule)
 static void
 show_substitute_path_command (const char *args, int from_tty)
 {
-  struct substitute_path_rule *rule = substitute_path_rules;
   char *from = NULL;
   
   gdb_argv argv (args);
@@ -1634,16 +1761,16 @@ show_substitute_path_command (const char *args, int from_tty)
   /* Print the substitution rules.  */
 
   if (from != NULL)
-    printf_filtered
+    gdb_printf
       (_("Source path substitution rule matching `%s':\n"), from);
   else
-    printf_filtered (_("List of all source path substitution rules:\n"));
+    gdb_printf (_("List of all source path substitution rules:\n"));
 
-  while (rule != NULL)
+  for (substitute_path_rule &rule : substitute_path_rules)
     {
-      if (from == NULL || substitute_path_rule_matches (rule, from) != 0)
-        printf_filtered ("  `%s' -> `%s'.\n", rule->from, rule->to);
-      rule = rule->next;
+      if (from == NULL || substitute_path_rule_matches (&rule, from) != 0)
+       gdb_printf ("  `%s' -> `%s'.\n", rule.from.c_str (),
+                   rule.to.c_str ());
     }
 }
 
@@ -1652,10 +1779,8 @@ show_substitute_path_command (const char *args, int from_tty)
 static void
 unset_substitute_path_command (const char *args, int from_tty)
 {
-  struct substitute_path_rule *rule = substitute_path_rules;
   gdb_argv argv (args);
   char *from = NULL;
-  int rule_found = 0;
 
   /* This function takes either 0 or 1 argument.  */
 
@@ -1676,24 +1801,27 @@ unset_substitute_path_command (const char *args, int from_tty)
   /* Delete the rule matching the argument.  No argument means that
      all rules should be deleted.  */
 
-  while (rule != NULL)
+  if (from == nullptr)
+    substitute_path_rules.clear ();
+  else
     {
-      struct substitute_path_rule *next = rule->next;
-
-      if (from == NULL || FILENAME_CMP (from, rule->from) == 0)
-        {
-          delete_substitute_path_rule (rule);
-          rule_found = 1;
-        }
-
-      rule = next;
+      auto iter
+       = std::remove_if (substitute_path_rules.begin (),
+                         substitute_path_rules.end (),
+                         [&] (const substitute_path_rule &rule)
+                         {
+                           return FILENAME_CMP (from,
+                                                rule.from.c_str ()) == 0;
+                         });
+      bool rule_found = iter != substitute_path_rules.end ();
+      substitute_path_rules.erase (iter, substitute_path_rules.end ());
+
+      /* If the user asked for a specific rule to be deleted but
+        we could not find it, then report an error.  */
+
+      if (!rule_found)
+       error (_("No substitution rule defined for `%s'"), from);
     }
-  
-  /* If the user asked for a specific rule to be deleted but
-     we could not find it, then report an error.  */
-
-  if (from != NULL && !rule_found)
-    error (_("No substitution rule defined for `%s'"), from);
 
   forget_cached_source_info ();
 }
@@ -1703,8 +1831,6 @@ unset_substitute_path_command (const char *args, int from_tty)
 static void
 set_substitute_path_command (const char *args, int from_tty)
 {
-  struct substitute_path_rule *rule;
-  
   gdb_argv argv (args);
 
   if (argv == NULL || argv[0] == NULL || argv [1] == NULL)
@@ -1724,10 +1850,15 @@ set_substitute_path_command (const char *args, int from_tty)
   /* If a rule with the same "from" was previously defined, then
      delete it.  This new rule replaces it.  */
 
-  rule = find_substitute_path_rule (argv[0]);
-  if (rule != NULL)
-    delete_substitute_path_rule (rule);
-      
+  auto iter
+    = std::remove_if (substitute_path_rules.begin (),
+                     substitute_path_rules.end (),
+                     [&] (const substitute_path_rule &rule)
+                     {
+                       return FILENAME_CMP (argv[0], rule.from.c_str ()) == 0;
+                     });
+  substitute_path_rules.erase (iter, substitute_path_rules.end ());
+
   /* Insert the new substitution rule.  */
 
   add_substitute_path_rule (argv[0], argv[1]);
@@ -1761,13 +1892,27 @@ source_lines_range::source_lines_range (int startline,
     }
 }
 
+/* Handle the "set source" base command.  */
+
+static void
+set_source (const char *arg, int from_tty)
+{
+  help_list (setsourcelist, "set source ", all_commands, gdb_stdout);
+}
+
+/* Handle the "show source" base command.  */
+
+static void
+show_source (const char *args, int from_tty)
+{
+  help_list (showsourcelist, "show source ", all_commands, gdb_stdout);
+}
+
 \f
+void _initialize_source ();
 void
-_initialize_source (void)
+_initialize_source ()
 {
-  struct cmd_list_element *c;
-
-  current_source_symtab = 0;
   init_source_path ();
 
   /* The intention is to use POSIX Basic Regular Expressions.
@@ -1776,7 +1921,8 @@ _initialize_source (void)
      just an approximation.  */
   re_set_syntax (RE_SYNTAX_GREP);
 
-  c = add_cmd ("directory", class_files, directory_command, _("\
+  cmd_list_element *directory_cmd
+    = add_cmd ("directory", class_files, directory_command, _("\
 Add directory DIR to beginning of search path for source files.\n\
 Forget cached info on source file locations and line positions.\n\
 DIR can also be $cwd for the current working directory, or $cdir for the\n\
@@ -1784,10 +1930,7 @@ directory in which the source file was compiled into object code.\n\
 With no argument, reset the search path to $cdir:$cwd, the default."),
               &cmdlist);
 
-  if (dbx_commands)
-    add_com_alias ("use", "directory", class_files, 0);
-
-  set_cmd_completer (c, filename_completer);
+  set_cmd_completer (directory_cmd, filename_completer);
 
   add_setshow_optional_filename_cmd ("directories",
                                     class_files,
@@ -1821,16 +1964,18 @@ 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, _("\
+  cmd_list_element *forward_search_cmd
+    = add_com ("forward-search", class_files, forward_search_command, _("\
 Search for regular expression (see regex(3)) from last line listed.\n\
 The matching line number is also stored as the value of \"$_\"."));
-  add_com_alias ("search", "forward-search", class_files, 0);
-  add_com_alias ("fo", "forward-search", class_files, 1);
+  add_com_alias ("search", forward_search_cmd, class_files, 0);
+  add_com_alias ("fo", forward_search_cmd, class_files, 1);
 
-  add_com ("reverse-search", class_files, reverse_search_command, _("\
+  cmd_list_element *reverse_search_cmd
+    = add_com ("reverse-search", class_files, reverse_search_command, _("\
 Search backward for regular expression (see regex(3)) from last line listed.\n\
 The matching line number is also stored as the value of \"$_\"."));
-  add_com_alias ("rev", "reverse-search", class_files, 1);
+  add_com_alias ("rev", reverse_search_cmd, class_files, 1);
 
   add_setshow_integer_cmd ("listsize", class_support, &lines_to_list, _("\
 Set number of source lines gdb will list by default."), _("\
@@ -1843,27 +1988,31 @@ A value of \"unlimited\", or zero, means there's no limit."),
                            &setlist, &showlist);
 
   add_cmd ("substitute-path", class_files, set_substitute_path_command,
-           _("\
+          _("\
+Add a substitution rule to rewrite the source directories.\n\
 Usage: set substitute-path FROM TO\n\
-Add a substitution rule replacing FROM into TO in source file names.\n\
+The rule is applied only if the directory name starts with FROM\n\
+directly followed by a directory separator.\n\
 If a substitution rule was previously set for FROM, the old rule\n\
 is replaced by the new one."),
-           &setlist);
+          &setlist);
 
   add_cmd ("substitute-path", class_files, unset_substitute_path_command,
-           _("\
+          _("\
+Delete one or all substitution rules rewriting the source directories.\n\
 Usage: unset substitute-path [FROM]\n\
-Delete the rule for substituting FROM in source file names.  If FROM\n\
+Delete the rule for substituting FROM in source directories.  If FROM\n\
 is not specified, all substituting rules are deleted.\n\
 If the debugger cannot find a rule for FROM, it will display a warning."),
-           &unsetlist);
+          &unsetlist);
 
   add_cmd ("substitute-path", class_files, show_substitute_path_command,
-           _("\
+          _("\
+Show one or all substitution rules rewriting the source directories.\n\
 Usage: show substitute-path [FROM]\n\
-Print the rule for substituting FROM in source file names. If FROM\n\
+Print the rule for substituting FROM in source directories. If FROM\n\
 is not specified, print all substitution rules."),
-           &showlist);
+          &showlist);
 
   add_setshow_enum_cmd ("filename-display", class_files,
                        filename_display_kind_names,
@@ -1879,4 +2028,25 @@ By default, relative filenames are displayed."),
                        show_filename_display_string,
                        &setlist, &showlist);
 
+  add_prefix_cmd ("source", no_class, set_source,
+                  _("Generic command for setting how sources are handled."),
+                  &setsourcelist, 0, &setlist);
+
+  add_prefix_cmd ("source", no_class, show_source,
+                  _("Generic command for showing source settings."),
+                  &showsourcelist, 0, &showlist);
+
+  add_setshow_boolean_cmd ("open", class_files, &source_open, _("\
+Set whether GDB should open source files."), _("\
+Show whether GDB should open source files."), _("\
+When this option is on GDB will open source files and display the\n\
+contents when appropriate, for example, when GDB stops, or the list\n\
+command is used.\n\
+When this option is off GDB will not try to open source files, instead\n\
+GDB will print the file and line number that would have been displayed.\n\
+This can be useful if access to source code files is slow, for example\n\
+due to the source being located over a slow network connection."),
+                           NULL,
+                           show_source_open,
+                           &setsourcelist, &showsourcelist);
 }