Use an accessor function for general_symbol_info::language
[deliverable/binutils-gdb.git] / gdb / symtab.c
index 8a551f1575a31579b030e6ac927bf6eb78e99346..a082ee21a90898cc3f1b3310940175ccf12fceac 100644 (file)
@@ -68,7 +68,9 @@
 #include "filename-seen-cache.h"
 #include "arch-utils.h"
 #include <algorithm>
+#include "gdbsupport/gdb_string_view.h"
 #include "gdbsupport/pathstuff.h"
+#include "gdbsupport/common-utils.h"
 
 /* Forward declarations for local functions.  */
 
@@ -181,6 +183,16 @@ struct symbol_cache_slot
   } value;
 };
 
+/* Clear out SLOT.  */
+
+static void
+symbol_cache_clear_slot (struct symbol_cache_slot *slot)
+{
+  if (slot->state == SYMBOL_SLOT_NOT_FOUND)
+    xfree (slot->value.not_found.name);
+  slot->state = SYMBOL_SLOT_UNUSED;
+}
+
 /* Symbols don't specify global vs static block.
    So keep them in separate caches.  */
 
@@ -199,6 +211,19 @@ struct block_symbol_cache
   struct symbol_cache_slot symbols[1];
 };
 
+/* Clear all slots of BSC and free BSC.  */
+
+static void
+destroy_block_symbol_cache (struct block_symbol_cache *bsc)
+{
+  if (bsc != nullptr)
+    {
+      for (unsigned int i = 0; i < bsc->size; i++)
+       symbol_cache_clear_slot (&bsc->symbols[i]);
+      xfree (bsc);
+    }
+}
+
 /* The symbol cache.
 
    Searching for symbols in the static and global blocks over multiple objfiles
@@ -215,8 +240,8 @@ struct symbol_cache
 
   ~symbol_cache ()
   {
-    xfree (global_symbols);
-    xfree (static_symbols);
+    destroy_block_symbol_cache (global_symbols);
+    destroy_block_symbol_cache (static_symbols);
   }
 
   struct block_symbol_cache *global_symbols = nullptr;
@@ -297,6 +322,7 @@ search_domain_name (enum search_domain e)
     case VARIABLES_DOMAIN: return "VARIABLES_DOMAIN";
     case FUNCTIONS_DOMAIN: return "FUNCTIONS_DOMAIN";
     case TYPES_DOMAIN: return "TYPES_DOMAIN";
+    case MODULES_DOMAIN: return "MODULES_DOMAIN";
     case ALL_DOMAIN: return "ALL_DOMAIN";
     default: gdb_assert_not_reached ("bad search_domain");
     }
@@ -478,6 +504,9 @@ iterate_over_some_symtabs (const char *name,
 
              gdb_assert (IS_ABSOLUTE_PATH (real_path));
              gdb_assert (IS_ABSOLUTE_PATH (name));
+             gdb::unique_xmalloc_ptr<char> fullname_real_path
+               = gdb_realpath (fullname);
+             fullname = fullname_real_path.get ();
              if (FILENAME_CMP (real_path, fullname) == 0)
                {
                  if (callback (s))
@@ -646,7 +675,7 @@ symbol_set_demangled_name (struct general_symbol_info *gsymbol,
                            const char *name,
                            struct obstack *obstack)
 {
-  if (gsymbol->language == language_ada)
+  if (gsymbol->language () == language_ada)
     {
       if (name == NULL)
        {
@@ -668,7 +697,7 @@ symbol_set_demangled_name (struct general_symbol_info *gsymbol,
 const char *
 symbol_get_demangled_name (const struct general_symbol_info *gsymbol)
 {
-  if (gsymbol->language == language_ada)
+  if (gsymbol->language () == language_ada)
     {
       if (!gsymbol->ada_mangled)
        return NULL;
@@ -687,16 +716,16 @@ symbol_set_language (struct general_symbol_info *gsymbol,
                      enum language language,
                     struct obstack *obstack)
 {
-  gsymbol->language = language;
-  if (gsymbol->language == language_cplus
-      || gsymbol->language == language_d
-      || gsymbol->language == language_go
-      || gsymbol->language == language_objc
-      || gsymbol->language == language_fortran)
+  gsymbol->m_language = language;
+  if (language == language_cplus
+      || language == language_d
+      || language == language_go
+      || language == language_objc
+      || language == language_fortran)
     {
       symbol_set_demangled_name (gsymbol, NULL, obstack);
     }
-  else if (gsymbol->language == language_ada)
+  else if (language == language_ada)
     {
       gdb_assert (gsymbol->ada_mangled == 0);
       gsymbol->language_specific.obstack = obstack;
@@ -713,9 +742,12 @@ symbol_set_language (struct general_symbol_info *gsymbol,
 /* Objects of this type are stored in the demangled name hash table.  */
 struct demangled_name_entry
 {
-  const char *mangled;
-  ENUM_BITFIELD(language) language : LANGUAGE_BITS;
-  char demangled[1];
+  demangled_name_entry (gdb::string_view mangled_name)
+    : mangled (mangled_name) {}
+
+  gdb::string_view mangled;
+  enum language language;
+  gdb::unique_xmalloc_ptr<char> demangled;
 };
 
 /* Hash function for the demangled name hash.  */
@@ -726,7 +758,7 @@ hash_demangled_name_entry (const void *data)
   const struct demangled_name_entry *e
     = (const struct demangled_name_entry *) data;
 
-  return htab_hash_string (e->mangled);
+  return fast_hash (e->mangled.data (), e->mangled.length ());
 }
 
 /* Equality function for the demangled name hash.  */
@@ -739,7 +771,16 @@ eq_demangled_name_entry (const void *a, const void *b)
   const struct demangled_name_entry *db
     = (const struct demangled_name_entry *) b;
 
-  return strcmp (da->mangled, db->mangled) == 0;
+  return da->mangled == db->mangled;
+}
+
+static void
+free_demangled_name_entry (void *data)
+{
+  struct demangled_name_entry *e
+    = (struct demangled_name_entry *) data;
+
+  e->~demangled_name_entry();
 }
 
 /* Create the hash table used for demangled names.  Each hash entry is
@@ -752,32 +793,38 @@ create_demangled_names_hash (struct objfile_per_bfd_storage *per_bfd)
   /* Choose 256 as the starting size of the hash table, somewhat arbitrarily.
      The hash table code will round this up to the next prime number.
      Choosing a much larger table size wastes memory, and saves only about
-     1% in symbol reading.  */
+     1% in symbol reading.  However, if the minsym count is already
+     initialized (e.g. because symbol name setting was deferred to
+     a background thread) we can initialize the hashtable with a count
+     based on that, because we will almost certainly have at least that
+     many entries.  If we have a nonzero number but less than 256,
+     we still stay with 256 to have some space for psymbols, etc.  */
+
+  /* htab will expand the table when it is 3/4th full, so we account for that
+     here.  +2 to round up.  */
+  int minsym_based_count = (per_bfd->minimal_symbol_count + 2) / 3 * 4;
+  int count = std::max (per_bfd->minimal_symbol_count, minsym_based_count);
 
   per_bfd->demangled_names_hash.reset (htab_create_alloc
-    (256, hash_demangled_name_entry, eq_demangled_name_entry,
-     NULL, xcalloc, xfree));
+    (count, hash_demangled_name_entry, eq_demangled_name_entry,
+     free_demangled_name_entry, xcalloc, xfree));
 }
 
-/* Try to determine the demangled name for a symbol, based on the
-   language of that symbol.  If the language is set to language_auto,
-   it will attempt to find any demangling algorithm that works and
-   then set the language appropriately.  The returned name is allocated
-   by the demangler and should be xfree'd.  */
+/* See symtab.h  */
 
-static char *
+char *
 symbol_find_demangled_name (struct general_symbol_info *gsymbol,
                            const char *mangled)
 {
   char *demangled = NULL;
   int i;
 
-  if (gsymbol->language == language_unknown)
-    gsymbol->language = language_auto;
+  if (gsymbol->language () == language_unknown)
+    gsymbol->m_language = language_auto;
 
-  if (gsymbol->language != language_auto)
+  if (gsymbol->language () != language_auto)
     {
-      const struct language_defn *lang = language_def (gsymbol->language);
+      const struct language_defn *lang = language_def (gsymbol->language ());
 
       language_sniff_from_mangled_name (lang, mangled, &demangled);
       return demangled;
@@ -790,7 +837,7 @@ symbol_find_demangled_name (struct general_symbol_info *gsymbol,
 
       if (language_sniff_from_mangled_name (lang, mangled, &demangled))
        {
-         gsymbol->language = l;
+         gsymbol->m_language = l;
          return demangled;
        }
     }
@@ -811,27 +858,25 @@ symbol_find_demangled_name (struct general_symbol_info *gsymbol,
 
 void
 symbol_set_names (struct general_symbol_info *gsymbol,
-                 const char *linkage_name, int len, bool copy_name,
-                 struct objfile_per_bfd_storage *per_bfd)
+                 gdb::string_view linkage_name, bool copy_name,
+                 struct objfile_per_bfd_storage *per_bfd,
+                 gdb::optional<hashval_t> hash)
 {
   struct demangled_name_entry **slot;
-  /* A 0-terminated copy of the linkage name.  */
-  const char *linkage_name_copy;
-  struct demangled_name_entry entry;
 
-  if (gsymbol->language == language_ada)
+  if (gsymbol->language () == language_ada)
     {
       /* In Ada, we do the symbol lookups using the mangled name, so
          we can save some space by not storing the demangled name.  */
       if (!copy_name)
-       gsymbol->name = linkage_name;
+       gsymbol->name = linkage_name.data ();
       else
        {
          char *name = (char *) obstack_alloc (&per_bfd->storage_obstack,
-                                              len + 1);
+                                              linkage_name.length () + 1);
 
-         memcpy (name, linkage_name, len);
-         name[len] = '\0';
+         memcpy (name, linkage_name.data (), linkage_name.length ());
+         name[linkage_name.length ()] = '\0';
          gsymbol->name = name;
        }
       symbol_set_demangled_name (gsymbol, NULL, &per_bfd->storage_obstack);
@@ -842,132 +887,133 @@ symbol_set_names (struct general_symbol_info *gsymbol,
   if (per_bfd->demangled_names_hash == NULL)
     create_demangled_names_hash (per_bfd);
 
-  if (linkage_name[len] != '\0')
-    {
-      char *alloc_name;
-
-      alloc_name = (char *) alloca (len + 1);
-      memcpy (alloc_name, linkage_name, len);
-      alloc_name[len] = '\0';
-
-      linkage_name_copy = alloc_name;
-    }
-  else
-    linkage_name_copy = linkage_name;
-
-  entry.mangled = linkage_name_copy;
+  struct demangled_name_entry entry (linkage_name);
+  if (!hash.has_value ())
+    hash = hash_demangled_name_entry (&entry);
   slot = ((struct demangled_name_entry **)
-         htab_find_slot (per_bfd->demangled_names_hash.get (),
-                         &entry, INSERT));
+          htab_find_slot_with_hash (per_bfd->demangled_names_hash.get (),
+                                   &entry, *hash, INSERT));
 
   /* If this name is not in the hash table, add it.  */
   if (*slot == NULL
       /* A C version of the symbol may have already snuck into the table.
         This happens to, e.g., main.init (__go_init_main).  Cope.  */
-      || (gsymbol->language == language_go
-         && (*slot)->demangled[0] == '\0'))
+      || (gsymbol->language () == language_go && (*slot)->demangled == nullptr))
     {
-      char *demangled_name_ptr
-       = symbol_find_demangled_name (gsymbol, linkage_name_copy);
-      gdb::unique_xmalloc_ptr<char> demangled_name (demangled_name_ptr);
-      int demangled_len = demangled_name ? strlen (demangled_name.get ()) : 0;
+      /* A 0-terminated copy of the linkage name.  Callers must set COPY_NAME
+         to true if the string might not be nullterminated.  We have to make
+         this copy because demangling needs a nullterminated string.  */
+      gdb::string_view linkage_name_copy;
+      if (copy_name)
+       {
+         char *alloc_name = (char *) alloca (linkage_name.length () + 1);
+         memcpy (alloc_name, linkage_name.data (), linkage_name.length ());
+         alloc_name[linkage_name.length ()] = '\0';
+
+         linkage_name_copy = gdb::string_view (alloc_name,
+                                               linkage_name.length ());
+       }
+      else
+       linkage_name_copy = linkage_name;
+
+      /* The const_cast is safe because the only reason it is already
+         initialized is if we purposefully set it from a background
+         thread to avoid doing the work here.  However, it is still
+         allocated from the heap and needs to be freed by us, just
+         like if we called symbol_find_demangled_name here.  */
+      gdb::unique_xmalloc_ptr<char> demangled_name
+       (gsymbol->language_specific.demangled_name
+        ? const_cast<char *> (gsymbol->language_specific.demangled_name)
+        : symbol_find_demangled_name (gsymbol, linkage_name_copy.data ()));
 
       /* Suppose we have demangled_name==NULL, copy_name==0, and
         linkage_name_copy==linkage_name.  In this case, we already have the
         mangled name saved, and we don't have a demangled name.  So,
         you might think we could save a little space by not recording
         this in the hash table at all.
-        
+
         It turns out that it is actually important to still save such
         an entry in the hash table, because storing this name gives
         us better bcache hit rates for partial symbols.  */
-      if (!copy_name && linkage_name_copy == linkage_name)
+      if (!copy_name)
        {
          *slot
            = ((struct demangled_name_entry *)
               obstack_alloc (&per_bfd->storage_obstack,
-                             offsetof (struct demangled_name_entry, demangled)
-                             + demangled_len + 1));
-         (*slot)->mangled = linkage_name;
+                             sizeof (demangled_name_entry)));
+         new (*slot) demangled_name_entry (linkage_name);
        }
       else
        {
-         char *mangled_ptr;
-
          /* If we must copy the mangled name, put it directly after
-            the demangled name so we can have a single
-            allocation.  */
+            the struct so we can have a single allocation.  */
          *slot
            = ((struct demangled_name_entry *)
               obstack_alloc (&per_bfd->storage_obstack,
-                             offsetof (struct demangled_name_entry, demangled)
-                             + len + demangled_len + 2));
-         mangled_ptr = &((*slot)->demangled[demangled_len + 1]);
-         strcpy (mangled_ptr, linkage_name_copy);
-         (*slot)->mangled = mangled_ptr;
+                             sizeof (demangled_name_entry)
+                             + linkage_name.length () + 1));
+         char *mangled_ptr = reinterpret_cast<char *> (*slot + 1);
+         memcpy (mangled_ptr, linkage_name.data (), linkage_name.length ());
+         mangled_ptr [linkage_name.length ()] = '\0';
+         new (*slot) demangled_name_entry
+           (gdb::string_view (mangled_ptr, linkage_name.length ()));
        }
-      (*slot)->language = gsymbol->language;
-
-      if (demangled_name != NULL)
-       strcpy ((*slot)->demangled, demangled_name.get ());
-      else
-       (*slot)->demangled[0] = '\0';
+      (*slot)->demangled = std::move (demangled_name);
+      (*slot)->language = gsymbol->language ();
     }
-  else if (gsymbol->language == language_unknown
-          || gsymbol->language == language_auto)
-    gsymbol->language = (*slot)->language;
+  else if (gsymbol->language () == language_unknown
+          || gsymbol->language () == language_auto)
+    gsymbol->m_language = (*slot)->language;
 
-  gsymbol->name = (*slot)->mangled;
-  if ((*slot)->demangled[0] != '\0')
-    symbol_set_demangled_name (gsymbol, (*slot)->demangled,
+  gsymbol->name = (*slot)->mangled.data ();
+  if ((*slot)->demangled != nullptr)
+    symbol_set_demangled_name (gsymbol, (*slot)->demangled.get (),
                               &per_bfd->storage_obstack);
   else
     symbol_set_demangled_name (gsymbol, NULL, &per_bfd->storage_obstack);
 }
 
-/* Return the source code name of a symbol.  In languages where
-   demangling is necessary, this is the demangled name.  */
+/* See symtab.h.  */
 
 const char *
-symbol_natural_name (const struct general_symbol_info *gsymbol)
+general_symbol_info::natural_name () const
 {
-  switch (gsymbol->language)
+  switch (language ())
     {
     case language_cplus:
     case language_d:
     case language_go:
     case language_objc:
     case language_fortran:
-      if (symbol_get_demangled_name (gsymbol) != NULL)
-       return symbol_get_demangled_name (gsymbol);
+      if (symbol_get_demangled_name (this) != NULL)
+       return symbol_get_demangled_name (this);
       break;
     case language_ada:
-      return ada_decode_symbol (gsymbol);
+      return ada_decode_symbol (this);
     default:
       break;
     }
-  return gsymbol->name;
+  return name;
 }
 
-/* Return the demangled name for a symbol based on the language for
-   that symbol.  If no demangled name exists, return NULL.  */
+/* See symtab.h.  */
 
 const char *
-symbol_demangled_name (const struct general_symbol_info *gsymbol)
+general_symbol_info::demangled_name () const
 {
   const char *dem_name = NULL;
 
-  switch (gsymbol->language)
+  switch (language ())
     {
     case language_cplus:
     case language_d:
     case language_go:
     case language_objc:
     case language_fortran:
-      dem_name = symbol_get_demangled_name (gsymbol);
+      dem_name = symbol_get_demangled_name (this);
       break;
     case language_ada:
-      dem_name = ada_decode_symbol (gsymbol);
+      dem_name = ada_decode_symbol (this);
       break;
     default:
       break;
@@ -975,18 +1021,15 @@ symbol_demangled_name (const struct general_symbol_info *gsymbol)
   return dem_name;
 }
 
-/* Return the search name of a symbol---generally the demangled or
-   linkage name of the symbol, depending on how it will be searched for.
-   If there is no distinct demangled name, then returns the same value
-   (same pointer) as SYMBOL_LINKAGE_NAME.  */
+/* See symtab.h.  */
 
 const char *
-symbol_search_name (const struct general_symbol_info *gsymbol)
+general_symbol_info::search_name () const
 {
-  if (gsymbol->language == language_ada)
-    return gsymbol->name;
+  if (language () == language_ada)
+    return name;
   else
-    return symbol_natural_name (gsymbol);
+    return natural_name ();
 }
 
 /* See symtab.h.  */
@@ -996,8 +1039,8 @@ symbol_matches_search_name (const struct general_symbol_info *gsymbol,
                            const lookup_name_info &name)
 {
   symbol_name_matcher_ftype *name_match
-    = get_symbol_name_matcher (language_def (gsymbol->language), name);
-  return name_match (symbol_search_name (gsymbol), name, NULL);
+    = get_symbol_name_matcher (language_def (gsymbol->language ()), name);
+  return name_match (gsymbol->search_name (), name, NULL);
 }
 
 \f
@@ -1137,7 +1180,7 @@ eq_symbol_entry (const struct symbol_cache_slot *slot,
     }
   else
     {
-      slot_name = SYMBOL_SEARCH_NAME (slot->value.found.symbol);
+      slot_name = slot->value.found.symbol->search_name ();
       slot_domain = SYMBOL_DOMAIN (slot->value.found.symbol);
     }
 
@@ -1154,7 +1197,7 @@ eq_symbol_entry (const struct symbol_cache_slot *slot,
       /* It's important that we use the same comparison that was done
         the first time through.  If the slot records a found symbol,
         then this means using the symbol name comparison function of
-        the symbol's language with SYMBOL_SEARCH_NAME.  See
+        the symbol's language with symbol->search_name ().  See
         dictionary.c.  It also means using symbol_matches_domain for
         found symbols.  See block.c.
 
@@ -1176,8 +1219,7 @@ eq_symbol_entry (const struct symbol_cache_slot *slot,
          if (!SYMBOL_MATCHES_SEARCH_NAME (sym, lookup_name))
            return 0;
 
-         if (!symbol_matches_domain (SYMBOL_LANGUAGE (sym),
-                                     slot_domain, domain))
+         if (!symbol_matches_domain (sym->language (), slot_domain, domain))
            return 0;
        }
     }
@@ -1214,8 +1256,8 @@ resize_symbol_cache (struct symbol_cache *cache, unsigned int new_size)
          && new_size == 0))
     return;
 
-  xfree (cache->global_symbols);
-  xfree (cache->static_symbols);
+  destroy_block_symbol_cache (cache->global_symbols);
+  destroy_block_symbol_cache (cache->static_symbols);
 
   if (new_size == 0)
     {
@@ -1353,16 +1395,6 @@ symbol_cache_lookup (struct symbol_cache *cache,
   return {};
 }
 
-/* Clear out SLOT.  */
-
-static void
-symbol_cache_clear_slot (struct symbol_cache_slot *slot)
-{
-  if (slot->state == SYMBOL_SLOT_NOT_FOUND)
-    xfree (slot->value.not_found.name);
-  slot->state = SYMBOL_SLOT_UNUSED;
-}
-
 /* Mark SYMBOL as found in SLOT.
    OBJFILE_CONTEXT is the current objfile when the lookup was done, or NULL
    if it's not needed to distinguish lookups (STATIC_BLOCK).  It is *not*
@@ -1503,7 +1535,7 @@ symbol_cache_dump (const struct symbol_cache *cache)
 
                printf_filtered ("  [%4u] = %s, %s %s\n", i,
                                 host_address_to_string (context),
-                                SYMBOL_PRINT_NAME (found),
+                                found->print_name (),
                                 domain_name (SYMBOL_DOMAIN (found)));
                break;
              }
@@ -1750,7 +1782,7 @@ fixup_symbol_section (struct symbol *sym, struct objfile *objfile)
       return sym;
     }
 
-  fixup_section (&sym->ginfo, addr, objfile);
+  fixup_section (sym, addr, objfile);
 
   return sym;
 }
@@ -1936,7 +1968,7 @@ lookup_language_this (const struct language_defn *lang,
          if (symbol_lookup_debug > 1)
            {
              fprintf_unfiltered (gdb_stdlog, " = %s (%s, block %s)\n",
-                                 SYMBOL_PRINT_NAME (sym),
+                                 sym->print_name (),
                                  host_address_to_string (sym),
                                  host_address_to_string (block));
            }
@@ -2813,8 +2845,7 @@ iterate_over_symbols (const struct block *block,
 
   ALL_BLOCK_SYMBOLS_WITH_NAME (block, name, iter, sym)
     {
-      if (symbol_matches_domain (SYMBOL_LANGUAGE (sym),
-                                SYMBOL_DOMAIN (sym), domain))
+      if (symbol_matches_domain (sym->language (), SYMBOL_DOMAIN (sym), domain))
        {
          struct block_symbol block_sym = {sym, block};
 
@@ -3108,7 +3139,7 @@ find_pc_sect_line (CORE_ADDR pc, struct obj_section *section, int notcurrent)
     if (MSYMBOL_TYPE (msymbol.minsym) == mst_solib_trampoline)
       {
        struct bound_minimal_symbol mfunsym
-         = lookup_minimal_symbol_text (MSYMBOL_LINKAGE_NAME (msymbol.minsym),
+         = lookup_minimal_symbol_text (msymbol.minsym->linkage_name (),
                                        NULL);
 
        if (mfunsym.minsym == NULL)
@@ -3122,7 +3153,7 @@ find_pc_sect_line (CORE_ADDR pc, struct obj_section *section, int notcurrent)
           * but the "break" still works, and the warning is annoying.
           * So I commented out the warning.  RT */
          /* warning ("In stub for %s; unable to find real function/line info",
-            SYMBOL_LINKAGE_NAME (msymbol)); */
+            msymbol->linkage_name ()); */
          ;
        /* fall through */
        else if (BMSYMBOL_VALUE_ADDRESS (mfunsym)
@@ -3130,7 +3161,7 @@ find_pc_sect_line (CORE_ADDR pc, struct obj_section *section, int notcurrent)
          /* Avoid infinite recursion */
          /* See above comment about why warning is commented out.  */
          /* warning ("In stub for %s; unable to find real function/line info",
-            SYMBOL_LINKAGE_NAME (msymbol)); */
+            msymbol->linkage_name ()); */
          ;
        /* fall through */
        else
@@ -3701,7 +3732,7 @@ skip_prologue_sal (struct symtab_and_line *sal)
       objfile = symbol_objfile (sym);
       pc = BLOCK_ENTRY_PC (SYMBOL_BLOCK_VALUE (sym));
       section = SYMBOL_OBJ_SECTION (objfile, sym);
-      name = SYMBOL_LINKAGE_NAME (sym);
+      name = sym->linkage_name ();
     }
   else
     {
@@ -3714,7 +3745,7 @@ skip_prologue_sal (struct symtab_and_line *sal)
       objfile = msymbol.objfile;
       pc = BMSYMBOL_VALUE_ADDRESS (msymbol);
       section = MSYMBOL_OBJ_SECTION (objfile, msymbol.minsym);
-      name = MSYMBOL_LINKAGE_NAME (msymbol.minsym);
+      name = msymbol.minsym->linkage_name ();
     }
 
   gdbarch = get_objfile_arch (objfile);
@@ -4026,7 +4057,7 @@ operator_chars (const char *p, const char **end)
          }
        else
          {
-           /* Gratuitous qoute: skip it and move on.  */
+           /* Gratuitous quote: skip it and move on.  */
            p++;
            continue;
          }
@@ -4330,31 +4361,28 @@ info_sources_command (const char *args, int from_tty)
   printf_filtered ("\n");
 }
 
-/* Compare FILE against all the NFILES entries of FILES.  If BASENAMES is
-   non-zero compare only lbasename of FILES.  */
+/* Compare FILE against all the entries of FILENAMES.  If BASENAMES is
+   true compare only lbasename of FILENAMES.  */
 
-static int
-file_matches (const char *file, const char *files[], int nfiles, int basenames)
+static bool
+file_matches (const char *file, const std::vector<const char *> &filenames,
+             bool basenames)
 {
-  int i;
+  if (filenames.empty ())
+    return true;
 
-  if (file != NULL && nfiles != 0)
+  for (const char *name : filenames)
     {
-      for (i = 0; i < nfiles; i++)
-       {
-         if (compare_filenames_for_search (file, (basenames
-                                                  ? lbasename (files[i])
-                                                  : files[i])))
-           return 1;
-       }
+      name = (basenames ? lbasename (name) : name);
+      if (compare_filenames_for_search (file, name))
+       return true;
     }
-  else if (nfiles == 0)
-    return 1;
-  return 0;
+
+  return false;
 }
 
-/* Helper function for sort_search_symbols_remove_dups and qsort.  Can only
-   sort symbols, not minimal symbols.  */
+/* Helper function for std::sort on symbol_search objects.  Can only sort
+   symbols, not minimal symbols.  */
 
 int
 symbol_search::compare_search_syms (const symbol_search &sym_a,
@@ -4370,8 +4398,7 @@ symbol_search::compare_search_syms (const symbol_search &sym_a,
   if (sym_a.block != sym_b.block)
     return sym_a.block - sym_b.block;
 
-  return strcmp (SYMBOL_PRINT_NAME (sym_a.symbol),
-                SYMBOL_PRINT_NAME (sym_b.symbol));
+  return strcmp (sym_a.symbol->print_name (), sym_b.symbol->print_name ());
 }
 
 /* Returns true if the type_name of symbol_type of SYM matches TREG.
@@ -4388,7 +4415,7 @@ treg_matches_sym_type_name (const compiled_regex &treg,
     {
       fprintf_unfiltered (gdb_stdlog,
                          "treg_matches_sym_type_name\n     sym %s\n",
-                         SYMBOL_NATURAL_NAME (sym));
+                         sym->natural_name ());
     }
 
   sym_type = SYMBOL_TYPE (sym);
@@ -4416,79 +4443,255 @@ treg_matches_sym_type_name (const compiled_regex &treg,
   return treg.exec (printed_sym_type_name.c_str (), 0, NULL, 0) == 0;
 }
 
+/* See symtab.h.  */
 
-/* Sort the symbols in RESULT and remove duplicates.  */
+bool
+global_symbol_searcher::is_suitable_msymbol
+       (const enum search_domain kind, const minimal_symbol *msymbol)
+{
+  switch (MSYMBOL_TYPE (msymbol))
+    {
+    case mst_data:
+    case mst_bss:
+    case mst_file_data:
+    case mst_file_bss:
+      return kind == VARIABLES_DOMAIN;
+    case mst_text:
+    case mst_file_text:
+    case mst_solib_trampoline:
+    case mst_text_gnu_ifunc:
+      return kind == FUNCTIONS_DOMAIN;
+    default:
+      return false;
+    }
+}
 
-static void
-sort_search_symbols_remove_dups (std::vector<symbol_search> *result)
+/* See symtab.h.  */
+
+bool
+global_symbol_searcher::expand_symtabs
+       (objfile *objfile, const gdb::optional<compiled_regex> &preg) const
+{
+  enum search_domain kind = m_kind;
+  bool found_msymbol = false;
+
+  if (objfile->sf)
+    objfile->sf->qf->expand_symtabs_matching
+      (objfile,
+       [&] (const char *filename, bool basenames)
+       {
+        return file_matches (filename, filenames, basenames);
+       },
+       lookup_name_info::match_any (),
+       [&] (const char *symname)
+       {
+        return (!preg.has_value ()
+                || preg->exec (symname, 0, NULL, 0) == 0);
+       },
+       NULL,
+       kind);
+
+  /* Here, we search through the minimal symbol tables for functions and
+     variables that match, and force their symbols to be read.  This is in
+     particular necessary for demangled variable names, which are no longer
+     put into the partial symbol tables.  The symbol will then be found
+     during the scan of symtabs later.
+
+     For functions, find_pc_symtab should succeed if we have debug info for
+     the function, for variables we have to call
+     lookup_symbol_in_objfile_from_linkage_name to determine if the
+     variable has debug info.  If the lookup fails, set found_msymbol so
+     that we will rescan to print any matching symbols without debug info.
+     We only search the objfile the msymbol came from, we no longer search
+     all objfiles.  In large programs (1000s of shared libs) searching all
+     objfiles is not worth the pain.  */
+  if (filenames.empty ()
+      && (kind == VARIABLES_DOMAIN || kind == FUNCTIONS_DOMAIN))
+    {
+      for (minimal_symbol *msymbol : objfile->msymbols ())
+       {
+         QUIT;
+
+         if (msymbol->created_by_gdb)
+           continue;
+
+         if (is_suitable_msymbol (kind, msymbol))
+           {
+             if (!preg.has_value ()
+                 || preg->exec (msymbol->natural_name (), 0,
+                                NULL, 0) == 0)
+               {
+                 /* An important side-effect of these lookup functions is
+                    to expand the symbol table if msymbol is found, later
+                    in the process we will add matching symbols or
+                    msymbols to the results list, and that requires that
+                    the symbols tables are expanded.  */
+                 if (kind == FUNCTIONS_DOMAIN
+                     ? (find_pc_compunit_symtab
+                        (MSYMBOL_VALUE_ADDRESS (objfile, msymbol))
+                        == NULL)
+                     : (lookup_symbol_in_objfile_from_linkage_name
+                        (objfile, msymbol->linkage_name (),
+                         VAR_DOMAIN)
+                        .symbol == NULL))
+                   found_msymbol = true;
+               }
+           }
+       }
+    }
+
+  return found_msymbol;
+}
+
+/* See symtab.h.  */
+
+bool
+global_symbol_searcher::add_matching_symbols
+       (objfile *objfile,
+        const gdb::optional<compiled_regex> &preg,
+        const gdb::optional<compiled_regex> &treg,
+        std::set<symbol_search> *result_set) const
 {
-  std::sort (result->begin (), result->end ());
-  result->erase (std::unique (result->begin (), result->end ()),
-                result->end ());
+  enum search_domain kind = m_kind;
+
+  /* Add matching symbols (if not already present).  */
+  for (compunit_symtab *cust : objfile->compunits ())
+    {
+      const struct blockvector *bv  = COMPUNIT_BLOCKVECTOR (cust);
+
+      for (block_enum block : { GLOBAL_BLOCK, STATIC_BLOCK })
+       {
+         struct block_iterator iter;
+         struct symbol *sym;
+         const struct block *b = BLOCKVECTOR_BLOCK (bv, block);
+
+         ALL_BLOCK_SYMBOLS (b, iter, sym)
+           {
+             struct symtab *real_symtab = symbol_symtab (sym);
+
+             QUIT;
+
+             /* Check first sole REAL_SYMTAB->FILENAME.  It does
+                not need to be a substring of symtab_to_fullname as
+                it may contain "./" etc.  */
+             if ((file_matches (real_symtab->filename, filenames, false)
+                  || ((basenames_may_differ
+                       || file_matches (lbasename (real_symtab->filename),
+                                        filenames, true))
+                      && file_matches (symtab_to_fullname (real_symtab),
+                                       filenames, false)))
+                 && ((!preg.has_value ()
+                      || preg->exec (sym->natural_name (), 0,
+                                     NULL, 0) == 0)
+                     && ((kind == VARIABLES_DOMAIN
+                          && SYMBOL_CLASS (sym) != LOC_TYPEDEF
+                          && SYMBOL_CLASS (sym) != LOC_UNRESOLVED
+                          && SYMBOL_CLASS (sym) != LOC_BLOCK
+                          /* LOC_CONST can be used for more than
+                             just enums, e.g., c++ static const
+                             members.  We only want to skip enums
+                             here.  */
+                          && !(SYMBOL_CLASS (sym) == LOC_CONST
+                               && (TYPE_CODE (SYMBOL_TYPE (sym))
+                                   == TYPE_CODE_ENUM))
+                          && (!treg.has_value ()
+                              || treg_matches_sym_type_name (*treg, sym)))
+                         || (kind == FUNCTIONS_DOMAIN
+                             && SYMBOL_CLASS (sym) == LOC_BLOCK
+                             && (!treg.has_value ()
+                                 || treg_matches_sym_type_name (*treg,
+                                                                sym)))
+                         || (kind == TYPES_DOMAIN
+                             && SYMBOL_CLASS (sym) == LOC_TYPEDEF
+                             && SYMBOL_DOMAIN (sym) != MODULE_DOMAIN)
+                         || (kind == MODULES_DOMAIN
+                             && SYMBOL_DOMAIN (sym) == MODULE_DOMAIN
+                             && SYMBOL_LINE (sym) != 0))))
+               {
+                 if (result_set->size () < m_max_search_results)
+                   {
+                     /* Match, insert if not already in the results.  */
+                     symbol_search ss (block, sym);
+                     if (result_set->find (ss) == result_set->end ())
+                       result_set->insert (ss);
+                   }
+                 else
+                   return false;
+               }
+           }
+       }
+    }
+
+  return true;
 }
 
-/* Search the symbol table for matches to the regular expression REGEXP,
-   returning the results.
+/* See symtab.h.  */
 
-   Only symbols of KIND are searched:
-   VARIABLES_DOMAIN - search all symbols, excluding functions, type names,
-                      and constants (enums).
-                     if T_REGEXP is not NULL, only returns var that have
-                     a type matching regular expression T_REGEXP.
-   FUNCTIONS_DOMAIN - search all functions
-   TYPES_DOMAIN     - search all type names
-   ALL_DOMAIN       - an internal error for this function
+bool
+global_symbol_searcher::add_matching_msymbols
+       (objfile *objfile, const gdb::optional<compiled_regex> &preg,
+        std::vector<symbol_search> *results) const
+{
+  enum search_domain kind = m_kind;
 
-   Within each file the results are sorted locally; each symtab's global and
-   static blocks are separately alphabetized.
-   Duplicate entries are removed.
+  for (minimal_symbol *msymbol : objfile->msymbols ())
+    {
+      QUIT;
 
-   When EXCLUDE_MINSYMS is false then matching minsyms are also returned,
-   otherwise they are excluded.  */
+      if (msymbol->created_by_gdb)
+       continue;
+
+      if (is_suitable_msymbol (kind, msymbol))
+       {
+         if (!preg.has_value ()
+             || preg->exec (msymbol->natural_name (), 0,
+                            NULL, 0) == 0)
+           {
+             /* For functions we can do a quick check of whether the
+                symbol might be found via find_pc_symtab.  */
+             if (kind != FUNCTIONS_DOMAIN
+                 || (find_pc_compunit_symtab
+                     (MSYMBOL_VALUE_ADDRESS (objfile, msymbol))
+                     == NULL))
+               {
+                 if (lookup_symbol_in_objfile_from_linkage_name
+                     (objfile, msymbol->linkage_name (),
+                      VAR_DOMAIN).symbol == NULL)
+                   {
+                     /* Matching msymbol, add it to the results list.  */
+                     if (results->size () < m_max_search_results)
+                       results->emplace_back (GLOBAL_BLOCK, msymbol, objfile);
+                     else
+                       return false;
+                   }
+               }
+           }
+       }
+    }
+
+  return true;
+}
+
+/* See symtab.h.  */
 
 std::vector<symbol_search>
-search_symbols (const char *regexp, enum search_domain kind,
-               const char *t_regexp,
-               int nfiles, const char *files[],
-               bool exclude_minsyms)
+global_symbol_searcher::search () const
 {
-  const struct blockvector *bv;
-  const struct block *b;
-  int i = 0;
-  struct block_iterator iter;
-  struct symbol *sym;
-  int found_misc = 0;
-  static const enum minimal_symbol_type types[]
-    = {mst_data, mst_text, mst_unknown};
-  static const enum minimal_symbol_type types2[]
-    = {mst_bss, mst_file_text, mst_unknown};
-  static const enum minimal_symbol_type types3[]
-    = {mst_file_data, mst_solib_trampoline, mst_unknown};
-  static const enum minimal_symbol_type types4[]
-    = {mst_file_bss, mst_text_gnu_ifunc, mst_unknown};
-  enum minimal_symbol_type ourtype;
-  enum minimal_symbol_type ourtype2;
-  enum minimal_symbol_type ourtype3;
-  enum minimal_symbol_type ourtype4;
-  std::vector<symbol_search> result;
   gdb::optional<compiled_regex> preg;
   gdb::optional<compiled_regex> treg;
 
-  gdb_assert (kind <= TYPES_DOMAIN);
-
-  ourtype = types[kind];
-  ourtype2 = types2[kind];
-  ourtype3 = types3[kind];
-  ourtype4 = types4[kind];
+  gdb_assert (m_kind != ALL_DOMAIN);
 
-  if (regexp != NULL)
+  if (m_symbol_name_regexp != NULL)
     {
+      const char *symbol_name_regexp = m_symbol_name_regexp;
+
       /* Make sure spacing is right for C++ operators.
          This is just a courtesy to make the matching less sensitive
          to how many spaces the user leaves between 'operator'
          and <TYPENAME> or <OPERATOR>.  */
       const char *opend;
-      const char *opname = operator_chars (regexp, &opend);
+      const char *opname = operator_chars (symbol_name_regexp, &opend);
 
       if (*opname)
        {
@@ -4513,244 +4716,80 @@ search_symbols (const char *regexp, enum search_domain kind,
              char *tmp = (char *) alloca (8 + fix + strlen (opname) + 1);
 
              sprintf (tmp, "operator%.*s%s", fix, " ", opname);
-             regexp = tmp;
+             symbol_name_regexp = tmp;
            }
        }
 
       int cflags = REG_NOSUB | (case_sensitivity == case_sensitive_off
                                ? REG_ICASE : 0);
-      preg.emplace (regexp, cflags, _("Invalid regexp"));
+      preg.emplace (symbol_name_regexp, cflags,
+                   _("Invalid regexp"));
     }
 
-  if (t_regexp != NULL)
+  if (m_symbol_type_regexp != NULL)
     {
       int cflags = REG_NOSUB | (case_sensitivity == case_sensitive_off
                                ? REG_ICASE : 0);
-      treg.emplace (t_regexp, cflags, _("Invalid regexp"));
-    }
-
-  /* Search through the partial symtabs *first* for all symbols
-     matching the regexp.  That way we don't have to reproduce all of
-     the machinery below.  */
-  expand_symtabs_matching ([&] (const char *filename, bool basenames)
-                          {
-                            return file_matches (filename, files, nfiles,
-                                                 basenames);
-                          },
-                          lookup_name_info::match_any (),
-                          [&] (const char *symname)
-                          {
-                            return (!preg.has_value ()
-                                    || preg->exec (symname,
-                                                   0, NULL, 0) == 0);
-                          },
-                          NULL,
-                          kind);
-
-  /* Here, we search through the minimal symbol tables for functions
-     and variables that match, and force their symbols to be read.
-     This is in particular necessary for demangled variable names,
-     which are no longer put into the partial symbol tables.
-     The symbol will then be found during the scan of symtabs below.
-
-     For functions, find_pc_symtab should succeed if we have debug info
-     for the function, for variables we have to call
-     lookup_symbol_in_objfile_from_linkage_name to determine if the variable
-     has debug info.
-     If the lookup fails, set found_misc so that we will rescan to print
-     any matching symbols without debug info.
-     We only search the objfile the msymbol came from, we no longer search
-     all objfiles.  In large programs (1000s of shared libs) searching all
-     objfiles is not worth the pain.  */
-
-  if (nfiles == 0 && (kind == VARIABLES_DOMAIN || kind == FUNCTIONS_DOMAIN))
-    {
-      for (objfile *objfile : current_program_space->objfiles ())
-       {
-         for (minimal_symbol *msymbol : objfile->msymbols ())
-           {
-             QUIT;
-
-             if (msymbol->created_by_gdb)
-               continue;
-
-             if (MSYMBOL_TYPE (msymbol) == ourtype
-                 || MSYMBOL_TYPE (msymbol) == ourtype2
-                 || MSYMBOL_TYPE (msymbol) == ourtype3
-                 || MSYMBOL_TYPE (msymbol) == ourtype4)
-               {
-                 if (!preg.has_value ()
-                     || preg->exec (MSYMBOL_NATURAL_NAME (msymbol), 0,
-                                    NULL, 0) == 0)
-                   {
-                     /* Note: An important side-effect of these
-                        lookup functions is to expand the symbol
-                        table if msymbol is found, for the benefit of
-                        the next loop on compunits.  */
-                     if (kind == FUNCTIONS_DOMAIN
-                         ? (find_pc_compunit_symtab
-                            (MSYMBOL_VALUE_ADDRESS (objfile, msymbol))
-                            == NULL)
-                         : (lookup_symbol_in_objfile_from_linkage_name
-                            (objfile, MSYMBOL_LINKAGE_NAME (msymbol),
-                             VAR_DOMAIN)
-                            .symbol == NULL))
-                       found_misc = 1;
-                   }
-               }
-           }
-       }
+      treg.emplace (m_symbol_type_regexp, cflags,
+                   _("Invalid regexp"));
     }
 
+  bool found_msymbol = false;
+  std::set<symbol_search> result_set;
   for (objfile *objfile : current_program_space->objfiles ())
     {
-      for (compunit_symtab *cust : objfile->compunits ())
-       {
-         bv = COMPUNIT_BLOCKVECTOR (cust);
-         for (i = GLOBAL_BLOCK; i <= STATIC_BLOCK; i++)
-           {
-             b = BLOCKVECTOR_BLOCK (bv, i);
-             ALL_BLOCK_SYMBOLS (b, iter, sym)
-               {
-                 struct symtab *real_symtab = symbol_symtab (sym);
-
-                 QUIT;
-
-                 /* Check first sole REAL_SYMTAB->FILENAME.  It does
-                    not need to be a substring of symtab_to_fullname as
-                    it may contain "./" etc.  */
-                 if ((file_matches (real_symtab->filename, files, nfiles, 0)
-                      || ((basenames_may_differ
-                           || file_matches (lbasename (real_symtab->filename),
-                                            files, nfiles, 1))
-                          && file_matches (symtab_to_fullname (real_symtab),
-                                           files, nfiles, 0)))
-                     && ((!preg.has_value ()
-                          || preg->exec (SYMBOL_NATURAL_NAME (sym), 0,
-                                         NULL, 0) == 0)
-                         && ((kind == VARIABLES_DOMAIN
-                              && SYMBOL_CLASS (sym) != LOC_TYPEDEF
-                              && SYMBOL_CLASS (sym) != LOC_UNRESOLVED
-                              && SYMBOL_CLASS (sym) != LOC_BLOCK
-                              /* LOC_CONST can be used for more than
-                                 just enums, e.g., c++ static const
-                                 members.  We only want to skip enums
-                                 here.  */
-                              && !(SYMBOL_CLASS (sym) == LOC_CONST
-                                   && (TYPE_CODE (SYMBOL_TYPE (sym))
-                                       == TYPE_CODE_ENUM))
-                              && (!treg.has_value ()
-                                  || treg_matches_sym_type_name (*treg, sym)))
-                             || (kind == FUNCTIONS_DOMAIN
-                                 && SYMBOL_CLASS (sym) == LOC_BLOCK
-                                 && (!treg.has_value ()
-                                     || treg_matches_sym_type_name (*treg,
-                                                                    sym)))
-                             || (kind == TYPES_DOMAIN
-                                 && SYMBOL_CLASS (sym) == LOC_TYPEDEF
-                                 && SYMBOL_DOMAIN (sym) != MODULE_DOMAIN))))
-                   {
-                     /* match */
-                     result.emplace_back (i, sym);
-                   }
-               }
-           }
-       }
-    }
+      /* Expand symtabs within objfile that possibly contain matching
+        symbols.  */
+      found_msymbol |= expand_symtabs (objfile, preg);
 
-  if (!result.empty ())
-    sort_search_symbols_remove_dups (&result);
+      /* Find matching symbols within OBJFILE and add them in to the
+        RESULT_SET set.  Use a set here so that we can easily detect
+        duplicates as we go, and can therefore track how many unique
+        matches we have found so far.  */
+      if (!add_matching_symbols (objfile, preg, treg, &result_set))
+       break;
+    }
 
-  /* If there are no eyes, avoid all contact.  I mean, if there are
-     no debug symbols, then add matching minsyms.  But if the user wants
-     to see symbols matching a type regexp, then never give a minimal symbol,
-     as we assume that a minimal symbol does not have a type.  */
+  /* Convert the result set into a sorted result list, as std::set is
+     defined to be sorted then no explicit call to std::sort is needed.  */
+  std::vector<symbol_search> result (result_set.begin (), result_set.end ());
 
-  if ((found_misc || (nfiles == 0 && kind != FUNCTIONS_DOMAIN))
-      && !exclude_minsyms
+  /* If there are no debug symbols, then add matching minsyms.  But if the
+     user wants to see symbols matching a type regexp, then never give a
+     minimal symbol, as we assume that a minimal symbol does not have a
+     type.  */
+  if ((found_msymbol || (filenames.empty () && m_kind == VARIABLES_DOMAIN))
+      && !m_exclude_minsyms
       && !treg.has_value ())
     {
+      gdb_assert (m_kind == VARIABLES_DOMAIN || m_kind == FUNCTIONS_DOMAIN);
       for (objfile *objfile : current_program_space->objfiles ())
-       {
-         for (minimal_symbol *msymbol : objfile->msymbols ())
-           {
-             QUIT;
-
-             if (msymbol->created_by_gdb)
-               continue;
-
-             if (MSYMBOL_TYPE (msymbol) == ourtype
-                 || MSYMBOL_TYPE (msymbol) == ourtype2
-                 || MSYMBOL_TYPE (msymbol) == ourtype3
-                 || MSYMBOL_TYPE (msymbol) == ourtype4)
-               {
-                 if (!preg.has_value ()
-                     || preg->exec (MSYMBOL_NATURAL_NAME (msymbol), 0,
-                                    NULL, 0) == 0)
-                   {
-                     /* For functions we can do a quick check of whether the
-                        symbol might be found via find_pc_symtab.  */
-                     if (kind != FUNCTIONS_DOMAIN
-                         || (find_pc_compunit_symtab
-                             (MSYMBOL_VALUE_ADDRESS (objfile, msymbol))
-                             == NULL))
-                       {
-                         if (lookup_symbol_in_objfile_from_linkage_name
-                             (objfile, MSYMBOL_LINKAGE_NAME (msymbol),
-                              VAR_DOMAIN)
-                             .symbol == NULL)
-                           {
-                             /* match */
-                             result.emplace_back (i, msymbol, objfile);
-                           }
-                       }
-                   }
-               }
-           }
-       }
+       if (!add_matching_msymbols (objfile, preg, &result))
+         break;
     }
 
   return result;
 }
 
-/* Helper function for symtab_symbol_info, this function uses
-   the data returned from search_symbols() to print information
-   regarding the match to gdb_stdout.  If LAST is not NULL,
-   print file and line number information for the symbol as
-   well.  Skip printing the filename if it matches LAST.  */
+/* See symtab.h.  */
 
-static void
-print_symbol_info (enum search_domain kind,
-                  struct symbol *sym,
-                  int block, const char *last)
+std::string
+symbol_to_info_string (struct symbol *sym, int block,
+                      enum search_domain kind)
 {
-  scoped_switch_to_sym_language_if_auto l (sym);
-  struct symtab *s = symbol_symtab (sym);
+  std::string str;
 
-  if (last != NULL)
-    {
-      const char *s_filename = symtab_to_filename_for_display (s);
-
-      if (filename_cmp (last, s_filename) != 0)
-       {
-         printf_filtered (_("\nFile %ps:\n"),
-                          styled_string (file_name_style.style (),
-                                         s_filename));
-       }
-
-      if (SYMBOL_LINE (sym) != 0)
-       printf_filtered ("%d:\t", SYMBOL_LINE (sym));
-      else
-       puts_filtered ("\t");
-    }
+  gdb_assert (block == GLOBAL_BLOCK || block == STATIC_BLOCK);
 
   if (kind != TYPES_DOMAIN && block == STATIC_BLOCK)
-    printf_filtered ("static ");
+    str += "static ";
 
   /* Typedef that is not a C++ class.  */
   if (kind == TYPES_DOMAIN
       && SYMBOL_DOMAIN (sym) != STRUCT_DOMAIN)
     {
+      string_file tmp_stream;
+
       /* FIXME: For C (and C++) we end up with a difference in output here
         between how a typedef is printed, and non-typedefs are printed.
         The TYPEDEF_PRINT code places a ";" at the end in an attempt to
@@ -4760,25 +4799,69 @@ print_symbol_info (enum search_domain kind,
         printing of the ";" in this function, which is going to be wrong
         for languages that don't require a ";" between statements.  */
       if (TYPE_CODE (SYMBOL_TYPE (sym)) == TYPE_CODE_TYPEDEF)
-       typedef_print (SYMBOL_TYPE (sym), sym, gdb_stdout);
+       typedef_print (SYMBOL_TYPE (sym), sym, &tmp_stream);
       else
-       {
-         type_print (SYMBOL_TYPE (sym), "", gdb_stdout, -1);
-         printf_filtered ("\n");
-       }
+       type_print (SYMBOL_TYPE (sym), "", &tmp_stream, -1);
+      str += tmp_stream.string ();
     }
   /* variable, func, or typedef-that-is-c++-class.  */
   else if (kind < TYPES_DOMAIN
           || (kind == TYPES_DOMAIN
               && SYMBOL_DOMAIN (sym) == STRUCT_DOMAIN))
     {
+      string_file tmp_stream;
+
       type_print (SYMBOL_TYPE (sym),
                  (SYMBOL_CLASS (sym) == LOC_TYPEDEF
-                  ? "" : SYMBOL_PRINT_NAME (sym)),
-                 gdb_stdout, 0);
+                  ? "" : sym->print_name ()),
+                 &tmp_stream, 0);
+
+      str += tmp_stream.string ();
+      str += ";";
+    }
+  /* Printing of modules is currently done here, maybe at some future
+     point we might want a language specific method to print the module
+     symbol so that we can customise the output more.  */
+  else if (kind == MODULES_DOMAIN)
+    str += sym->print_name ();
+
+  return str;
+}
+
+/* Helper function for symbol info commands, for example 'info functions',
+   'info variables', etc.  KIND is the kind of symbol we searched for, and
+   BLOCK is the type of block the symbols was found in, either GLOBAL_BLOCK
+   or STATIC_BLOCK.  SYM is the symbol we found.  If LAST is not NULL,
+   print file and line number information for the symbol as well.  Skip
+   printing the filename if it matches LAST.  */
 
-      printf_filtered (";\n");
+static void
+print_symbol_info (enum search_domain kind,
+                  struct symbol *sym,
+                  int block, const char *last)
+{
+  scoped_switch_to_sym_language_if_auto l (sym);
+  struct symtab *s = symbol_symtab (sym);
+
+  if (last != NULL)
+    {
+      const char *s_filename = symtab_to_filename_for_display (s);
+
+      if (filename_cmp (last, s_filename) != 0)
+       {
+         printf_filtered (_("\nFile %ps:\n"),
+                          styled_string (file_name_style.style (),
+                                         s_filename));
+       }
+
+      if (SYMBOL_LINE (sym) != 0)
+       printf_filtered ("%d:\t", SYMBOL_LINE (sym));
+      else
+       puts_filtered ("\t");
     }
+
+  std::string str = symbol_to_info_string (sym, block, kind);
+  printf_filtered ("%s\n", str.c_str ());
 }
 
 /* This help function for symtab_symbol_info() prints information
@@ -4804,8 +4887,7 @@ print_msymbol_info (struct bound_minimal_symbol msymbol)
 
   printf_filtered (_("%ps  %ps\n"),
                   styled_string (address_style.style (), tmp),
-                  styled_string (sym_style,
-                                 MSYMBOL_PRINT_NAME (msymbol.minsym)));
+                  styled_string (sym_style, msymbol.minsym->print_name ()));
 }
 
 /* This is the guts of the commands "info functions", "info types", and
@@ -4819,19 +4901,19 @@ symtab_symbol_info (bool quiet, bool exclude_minsyms,
                    const char *t_regexp, int from_tty)
 {
   static const char * const classnames[] =
-    {"variable", "function", "type"};
+    {"variable", "function", "type", "module"};
   const char *last_filename = "";
   int first = 1;
 
-  gdb_assert (kind <= TYPES_DOMAIN);
+  gdb_assert (kind != ALL_DOMAIN);
 
   if (regexp != nullptr && *regexp == '\0')
     regexp = nullptr;
 
-  /* Must make sure that if we're interrupted, symbols gets freed.  */
-  std::vector<symbol_search> symbols = search_symbols (regexp, kind,
-                                                      t_regexp, 0, NULL,
-                                                      exclude_minsyms);
+  global_symbol_searcher spec (kind, regexp);
+  spec.set_symbol_type_regexp (t_regexp);
+  spec.set_exclude_minsyms (exclude_minsyms);
+  std::vector<symbol_search> symbols = spec.search ();
 
   if (!quiet)
     {
@@ -5042,23 +5124,29 @@ info_types_command_completer (struct cmd_list_element *ignore,
   symbol_completer (ignore, tracker, text, word);
 }
 
-/* Breakpoint all functions matching regular expression.  */
+/* Implement the 'info modules' command.  */
 
-void
-rbreak_command_wrapper (char *regexp, int from_tty)
+static void
+info_modules_command (const char *args, int from_tty)
 {
-  rbreak_command (regexp, from_tty);
+  info_types_options opts;
+
+  auto grp = make_info_types_options_def_group (&opts);
+  gdb::option::process_options
+    (&args, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, grp);
+  if (args != nullptr && *args == '\0')
+    args = nullptr;
+  symtab_symbol_info (opts.quiet, true, args, MODULES_DOMAIN, NULL,
+                     from_tty);
 }
 
 static void
 rbreak_command (const char *regexp, int from_tty)
 {
   std::string string;
-  const char **files = NULL;
-  const char *file_name;
-  int nfiles = 0;
+  const char *file_name = nullptr;
 
-  if (regexp)
+  if (regexp != nullptr)
     {
       const char *colon = strchr (regexp, ':');
 
@@ -5074,17 +5162,14 @@ rbreak_command (const char *regexp, int from_tty)
          while (isspace (local_name[colon_index]))
            local_name[colon_index--] = 0;
          file_name = local_name;
-         files = &file_name;
-         nfiles = 1;
          regexp = skip_spaces (colon + 1);
        }
     }
 
-  std::vector<symbol_search> symbols = search_symbols (regexp,
-                                                      FUNCTIONS_DOMAIN,
-                                                      NULL,
-                                                      nfiles, files,
-                                                      false);
+  global_symbol_searcher spec (FUNCTIONS_DOMAIN, regexp);
+  if (file_name != nullptr)
+    spec.filenames.push_back (file_name);
+  std::vector<symbol_search> symbols = spec.search ();
 
   scoped_rbreak_breakpoints finalize;
   for (const symbol_search &p : symbols)
@@ -5095,18 +5180,18 @@ rbreak_command (const char *regexp, int from_tty)
          const char *fullname = symtab_to_fullname (symtab);
 
          string = string_printf ("%s:'%s'", fullname,
-                                 SYMBOL_LINKAGE_NAME (p.symbol));
+                                 p.symbol->linkage_name ());
          break_command (&string[0], from_tty);
          print_symbol_info (FUNCTIONS_DOMAIN, p.symbol, p.block, NULL);
        }
       else
        {
          string = string_printf ("'%s'",
-                                 MSYMBOL_LINKAGE_NAME (p.msymbol.minsym));
+                                 p.msymbol.minsym->linkage_name ());
 
          break_command (&string[0], from_tty);
          printf_filtered ("<function, no debug info> %s;\n",
-                          MSYMBOL_PRINT_NAME (p.msymbol.minsym));
+                          p.msymbol.minsym->print_name ());
        }
     }
 }
@@ -5175,8 +5260,8 @@ completion_list_add_symbol (completion_tracker &tracker,
                            const lookup_name_info &lookup_name,
                            const char *text, const char *word)
 {
-  completion_list_add_name (tracker, SYMBOL_LANGUAGE (sym),
-                           SYMBOL_NATURAL_NAME (sym),
+  completion_list_add_name (tracker, sym->language (),
+                           sym->natural_name (),
                            lookup_name, text, word);
 }
 
@@ -5188,8 +5273,8 @@ completion_list_add_msymbol (completion_tracker &tracker,
                             const lookup_name_info &lookup_name,
                             const char *text, const char *word)
 {
-  completion_list_add_name (tracker, MSYMBOL_LANGUAGE (sym),
-                           MSYMBOL_NATURAL_NAME (sym),
+  completion_list_add_name (tracker, sym->language (),
+                           sym->natural_name (),
                            lookup_name, text, word);
 }
 
@@ -5209,7 +5294,7 @@ completion_list_objc_symbol (completion_tracker &tracker,
   const char *method, *category, *selector;
   char *tmp2 = NULL;
 
-  method = MSYMBOL_NATURAL_NAME (msymbol);
+  method = msymbol->natural_name ();
 
   /* Is it a method?  */
   if ((method[0] != '-') && (method[0] != '+'))
@@ -5322,7 +5407,7 @@ completion_list_add_fields (completion_tracker &tracker,
       if (c == TYPE_CODE_UNION || c == TYPE_CODE_STRUCT)
        for (j = TYPE_N_BASECLASSES (t); j < TYPE_NFIELDS (t); j++)
          if (TYPE_FIELD_NAME (t, j))
-           completion_list_add_name (tracker, SYMBOL_LANGUAGE (sym),
+           completion_list_add_name (tracker, sym->language (),
                                      TYPE_FIELD_NAME (t, j),
                                      lookup_name, text, word);
     }
@@ -5368,7 +5453,7 @@ find_gnu_ifunc (const symbol *sym)
   if (SYMBOL_CLASS (sym) != LOC_BLOCK)
     return {};
 
-  lookup_name_info lookup_name (SYMBOL_SEARCH_NAME (sym),
+  lookup_name_info lookup_name (sym->search_name (),
                                symbol_name_match_type::SEARCH_NAME);
   struct objfile *objfile = symbol_objfile (sym);
 
@@ -6177,23 +6262,13 @@ initialize_ordinary_address_classes (void)
 
 \f
 
-/* Helper function to initialize the fields of an objfile-owned symbol.
-   It assumed that *SYM is already all zeroes.  */
-
-static void
-initialize_objfile_symbol_1 (struct symbol *sym)
-{
-  SYMBOL_OBJFILE_OWNED (sym) = 1;
-  SYMBOL_SECTION (sym) = -1;
-}
-
 /* Initialize the symbol SYM, and mark it as being owned by an objfile.  */
 
 void
 initialize_objfile_symbol (struct symbol *sym)
 {
-  memset (sym, 0, sizeof (*sym));
-  initialize_objfile_symbol_1 (sym);
+  SYMBOL_OBJFILE_OWNED (sym) = 1;
+  SYMBOL_SECTION (sym) = -1;
 }
 
 /* Allocate and initialize a new 'struct symbol' on OBJFILE's
@@ -6202,10 +6277,9 @@ initialize_objfile_symbol (struct symbol *sym)
 struct symbol *
 allocate_symbol (struct objfile *objfile)
 {
-  struct symbol *result;
+  struct symbol *result = new (&objfile->objfile_obstack) symbol ();
 
-  result = OBSTACK_ZALLOC (&objfile->objfile_obstack, struct symbol);
-  initialize_objfile_symbol_1 (result);
+  initialize_objfile_symbol (result);
 
   return result;
 }
@@ -6218,8 +6292,8 @@ allocate_template_symbol (struct objfile *objfile)
 {
   struct template_symbol *result;
 
-  result = OBSTACK_ZALLOC (&objfile->objfile_obstack, struct template_symbol);
-  initialize_objfile_symbol_1 (result);
+  result = new (&objfile->objfile_obstack) template_symbol ();
+  initialize_objfile_symbol (result);
 
   return result;
 }
@@ -6269,7 +6343,7 @@ get_symbol_address (const struct symbol *sym)
   gdb_assert (sym->maybe_copied);
   gdb_assert (SYMBOL_CLASS (sym) == LOC_STATIC);
 
-  const char *linkage_name = SYMBOL_LINKAGE_NAME (sym);
+  const char *linkage_name = sym->linkage_name ();
 
   for (objfile *objfile : current_program_space->objfiles ())
     {
@@ -6278,7 +6352,7 @@ get_symbol_address (const struct symbol *sym)
       if (minsym.minsym != nullptr)
        return BMSYMBOL_VALUE_ADDRESS (minsym);
     }
-  return sym->ginfo.value.address;
+  return sym->value.address;
 }
 
 /* See symtab.h.  */
@@ -6289,7 +6363,7 @@ get_msymbol_address (struct objfile *objf, const struct minimal_symbol *minsym)
   gdb_assert (minsym->maybe_copied);
   gdb_assert ((objf->flags & OBJF_MAINLINE) == 0);
 
-  const char *linkage_name = MSYMBOL_LINKAGE_NAME (minsym);
+  const char *linkage_name = minsym->linkage_name ();
 
   for (objfile *objfile : current_program_space->objfiles ())
     {
@@ -6307,6 +6381,314 @@ get_msymbol_address (struct objfile *objf, const struct minimal_symbol *minsym)
 
 \f
 
+/* Hold the sub-commands of 'info module'.  */
+
+static struct cmd_list_element *info_module_cmdlist = NULL;
+
+/* Implement the 'info module' command, just displays some help text for
+   the available sub-commands.  */
+
+static void
+info_module_command (const char *args, int from_tty)
+{
+  help_list (info_module_cmdlist, "info module ", class_info, gdb_stdout);
+}
+
+/* See symtab.h.  */
+
+std::vector<module_symbol_search>
+search_module_symbols (const char *module_regexp, const char *regexp,
+                      const char *type_regexp, search_domain kind)
+{
+  std::vector<module_symbol_search> results;
+
+  /* Search for all modules matching MODULE_REGEXP.  */
+  global_symbol_searcher spec1 (MODULES_DOMAIN, module_regexp);
+  spec1.set_exclude_minsyms (true);
+  std::vector<symbol_search> modules = spec1.search ();
+
+  /* Now search for all symbols of the required KIND matching the required
+     regular expressions.  We figure out which ones are in which modules
+     below.  */
+  global_symbol_searcher spec2 (kind, regexp);
+  spec2.set_symbol_type_regexp (type_regexp);
+  spec2.set_exclude_minsyms (true);
+  std::vector<symbol_search> symbols = spec2.search ();
+
+  /* Now iterate over all MODULES, checking to see which items from
+     SYMBOLS are in each module.  */
+  for (const symbol_search &p : modules)
+    {
+      QUIT;
+
+      /* This is a module.  */
+      gdb_assert (p.symbol != nullptr);
+
+      std::string prefix = p.symbol->print_name ();
+      prefix += "::";
+
+      for (const symbol_search &q : symbols)
+       {
+         if (q.symbol == nullptr)
+           continue;
+
+         if (strncmp (q.symbol->print_name (), prefix.c_str (),
+                      prefix.size ()) != 0)
+           continue;
+
+         results.push_back ({p, q});
+       }
+    }
+
+  return results;
+}
+
+/* Implement the core of both 'info module functions' and 'info module
+   variables'.  */
+
+static void
+info_module_subcommand (bool quiet, const char *module_regexp,
+                       const char *regexp, const char *type_regexp,
+                       search_domain kind)
+{
+  /* Print a header line.  Don't build the header line bit by bit as this
+     prevents internationalisation.  */
+  if (!quiet)
+    {
+      if (module_regexp == nullptr)
+       {
+         if (type_regexp == nullptr)
+           {
+             if (regexp == nullptr)
+               printf_filtered ((kind == VARIABLES_DOMAIN
+                                 ? _("All variables in all modules:")
+                                 : _("All functions in all modules:")));
+             else
+               printf_filtered
+                 ((kind == VARIABLES_DOMAIN
+                   ? _("All variables matching regular expression"
+                       " \"%s\" in all modules:")
+                   : _("All functions matching regular expression"
+                       " \"%s\" in all modules:")),
+                  regexp);
+           }
+         else
+           {
+             if (regexp == nullptr)
+               printf_filtered
+                 ((kind == VARIABLES_DOMAIN
+                   ? _("All variables with type matching regular "
+                       "expression \"%s\" in all modules:")
+                   : _("All functions with type matching regular "
+                       "expression \"%s\" in all modules:")),
+                  type_regexp);
+             else
+               printf_filtered
+                 ((kind == VARIABLES_DOMAIN
+                   ? _("All variables matching regular expression "
+                       "\"%s\",\n\twith type matching regular "
+                       "expression \"%s\" in all modules:")
+                   : _("All functions matching regular expression "
+                       "\"%s\",\n\twith type matching regular "
+                       "expression \"%s\" in all modules:")),
+                  regexp, type_regexp);
+           }
+       }
+      else
+       {
+         if (type_regexp == nullptr)
+           {
+             if (regexp == nullptr)
+               printf_filtered
+                 ((kind == VARIABLES_DOMAIN
+                   ? _("All variables in all modules matching regular "
+                       "expression \"%s\":")
+                   : _("All functions in all modules matching regular "
+                       "expression \"%s\":")),
+                  module_regexp);
+             else
+               printf_filtered
+                 ((kind == VARIABLES_DOMAIN
+                   ? _("All variables matching regular expression "
+                       "\"%s\",\n\tin all modules matching regular "
+                       "expression \"%s\":")
+                   : _("All functions matching regular expression "
+                       "\"%s\",\n\tin all modules matching regular "
+                       "expression \"%s\":")),
+                  regexp, module_regexp);
+           }
+         else
+           {
+             if (regexp == nullptr)
+               printf_filtered
+                 ((kind == VARIABLES_DOMAIN
+                   ? _("All variables with type matching regular "
+                       "expression \"%s\"\n\tin all modules matching "
+                       "regular expression \"%s\":")
+                   : _("All functions with type matching regular "
+                       "expression \"%s\"\n\tin all modules matching "
+                       "regular expression \"%s\":")),
+                  type_regexp, module_regexp);
+             else
+               printf_filtered
+                 ((kind == VARIABLES_DOMAIN
+                   ? _("All variables matching regular expression "
+                       "\"%s\",\n\twith type matching regular expression "
+                       "\"%s\",\n\tin all modules matching regular "
+                       "expression \"%s\":")
+                   : _("All functions matching regular expression "
+                       "\"%s\",\n\twith type matching regular expression "
+                       "\"%s\",\n\tin all modules matching regular "
+                       "expression \"%s\":")),
+                  regexp, type_regexp, module_regexp);
+           }
+       }
+      printf_filtered ("\n");
+    }
+
+  /* Find all symbols of type KIND matching the given regular expressions
+     along with the symbols for the modules in which those symbols
+     reside.  */
+  std::vector<module_symbol_search> module_symbols
+    = search_module_symbols (module_regexp, regexp, type_regexp, kind);
+
+  std::sort (module_symbols.begin (), module_symbols.end (),
+            [] (const module_symbol_search &a, const module_symbol_search &b)
+            {
+              if (a.first < b.first)
+                return true;
+              else if (a.first == b.first)
+                return a.second < b.second;
+              else
+                return false;
+            });
+
+  const char *last_filename = "";
+  const symbol *last_module_symbol = nullptr;
+  for (const module_symbol_search &ms : module_symbols)
+    {
+      const symbol_search &p = ms.first;
+      const symbol_search &q = ms.second;
+
+      gdb_assert (q.symbol != nullptr);
+
+      if (last_module_symbol != p.symbol)
+       {
+         printf_filtered ("\n");
+         printf_filtered (_("Module \"%s\":\n"), p.symbol->print_name ());
+         last_module_symbol = p.symbol;
+         last_filename = "";
+       }
+
+      print_symbol_info (FUNCTIONS_DOMAIN, q.symbol, q.block,
+                        last_filename);
+      last_filename
+       = symtab_to_filename_for_display (symbol_symtab (q.symbol));
+    }
+}
+
+/* Hold the option values for the 'info module .....' sub-commands.  */
+
+struct info_modules_var_func_options
+{
+  bool quiet = false;
+  char *type_regexp = nullptr;
+  char *module_regexp = nullptr;
+
+  ~info_modules_var_func_options ()
+  {
+    xfree (type_regexp);
+    xfree (module_regexp);
+  }
+};
+
+/* The options used by 'info module variables' and 'info module functions'
+   commands.  */
+
+static const gdb::option::option_def info_modules_var_func_options_defs [] = {
+  gdb::option::boolean_option_def<info_modules_var_func_options> {
+    "q",
+    [] (info_modules_var_func_options *opt) { return &opt->quiet; },
+    nullptr, /* show_cmd_cb */
+    nullptr /* set_doc */
+  },
+
+  gdb::option::string_option_def<info_modules_var_func_options> {
+    "t",
+    [] (info_modules_var_func_options *opt) { return &opt->type_regexp; },
+    nullptr, /* show_cmd_cb */
+    nullptr /* set_doc */
+  },
+
+  gdb::option::string_option_def<info_modules_var_func_options> {
+    "m",
+    [] (info_modules_var_func_options *opt) { return &opt->module_regexp; },
+    nullptr, /* show_cmd_cb */
+    nullptr /* set_doc */
+  }
+};
+
+/* Return the option group used by the 'info module ...' sub-commands.  */
+
+static inline gdb::option::option_def_group
+make_info_modules_var_func_options_def_group
+       (info_modules_var_func_options *opts)
+{
+  return {{info_modules_var_func_options_defs}, opts};
+}
+
+/* Implements the 'info module functions' command.  */
+
+static void
+info_module_functions_command (const char *args, int from_tty)
+{
+  info_modules_var_func_options opts;
+  auto grp = make_info_modules_var_func_options_def_group (&opts);
+  gdb::option::process_options
+    (&args, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, grp);
+  if (args != nullptr && *args == '\0')
+    args = nullptr;
+
+  info_module_subcommand (opts.quiet, opts.module_regexp, args,
+                         opts.type_regexp, FUNCTIONS_DOMAIN);
+}
+
+/* Implements the 'info module variables' command.  */
+
+static void
+info_module_variables_command (const char *args, int from_tty)
+{
+  info_modules_var_func_options opts;
+  auto grp = make_info_modules_var_func_options_def_group (&opts);
+  gdb::option::process_options
+    (&args, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, grp);
+  if (args != nullptr && *args == '\0')
+    args = nullptr;
+
+  info_module_subcommand (opts.quiet, opts.module_regexp, args,
+                         opts.type_regexp, VARIABLES_DOMAIN);
+}
+
+/* Command completer for 'info module ...' sub-commands.  */
+
+static void
+info_module_var_func_command_completer (struct cmd_list_element *ignore,
+                                       completion_tracker &tracker,
+                                       const char *text,
+                                       const char * /* word */)
+{
+
+  const auto group = make_info_modules_var_func_options_def_group (nullptr);
+  if (gdb::option::complete_options
+      (tracker, &text, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, group))
+    return;
+
+  const char *word = advance_to_expression_complete_word_point (tracker, text);
+  symbol_completer (ignore, tracker, text, word);
+}
+
+\f
+
 void
 _initialize_symtab (void)
 {
@@ -6365,6 +6747,49 @@ Options:\n\
   c = add_info ("sources", info_sources_command, info_sources_help.c_str ());
   set_cmd_completer_handle_brkchars (c, info_sources_command_completer);
 
+  c = add_info ("modules", info_modules_command,
+               _("All module names, or those matching REGEXP."));
+  set_cmd_completer_handle_brkchars (c, info_types_command_completer);
+
+  add_prefix_cmd ("module", class_info, info_module_command, _("\
+Print information about modules."),
+                 &info_module_cmdlist, "info module ",
+                 0, &infolist);
+
+  c = add_cmd ("functions", class_info, info_module_functions_command, _("\
+Display functions arranged by modules.\n\
+Usage: info module functions [-q] [-m MODREGEXP] [-t TYPEREGEXP] [REGEXP]\n\
+Print a summary of all functions within each Fortran module, grouped by\n\
+module and file.  For each function the line on which the function is\n\
+defined is given along with the type signature and name of the function.\n\
+\n\
+If REGEXP is provided then only functions whose name matches REGEXP are\n\
+listed.  If MODREGEXP is provided then only functions in modules matching\n\
+MODREGEXP are listed.  If TYPEREGEXP is given then only functions whose\n\
+type signature matches TYPEREGEXP are listed.\n\
+\n\
+The -q flag suppresses printing some header information."),
+              &info_module_cmdlist);
+  set_cmd_completer_handle_brkchars
+    (c, info_module_var_func_command_completer);
+
+  c = add_cmd ("variables", class_info, info_module_variables_command, _("\
+Display variables arranged by modules.\n\
+Usage: info module variables [-q] [-m MODREGEXP] [-t TYPEREGEXP] [REGEXP]\n\
+Print a summary of all variables within each Fortran module, grouped by\n\
+module and file.  For each variable the line on which the variable is\n\
+defined is given along with the type and name of the variable.\n\
+\n\
+If REGEXP is provided then only variables whose name matches REGEXP are\n\
+listed.  If MODREGEXP is provided then only variables in modules matching\n\
+MODREGEXP are listed.  If TYPEREGEXP is given then only variables whose\n\
+type matches TYPEREGEXP are listed.\n\
+\n\
+The -q flag suppresses printing some header information."),
+              &info_module_cmdlist);
+  set_cmd_completer_handle_brkchars
+    (c, info_module_var_func_command_completer);
+
   add_com ("rbreak", class_breakpoint, rbreak_command,
           _("Set a breakpoint for all functions matching REGEXP."));
 
This page took 0.049064 seconds and 4 git commands to generate.