]> code.delx.au - gnu-emacs/commitdiff
*** empty log message ***
authorGerd Moellmann <gerd@gnu.org>
Sun, 9 Apr 2000 11:15:57 +0000 (11:15 +0000)
committerGerd Moellmann <gerd@gnu.org>
Sun, 9 Apr 2000 11:15:57 +0000 (11:15 +0000)
lib-src/ChangeLog
lib-src/ebrowse.c [new file with mode: 0644]
lisp/ChangeLog
lisp/cus-load.el
lisp/loaddefs.el
lisp/progmodes/ebrowse.el [new file with mode: 0644]
src/ChangeLog

index dc45c7cf8e6d4cde7145cf22224450c5f267a607..bb0f635c598ed529335413b747b1a113f7d07746 100644 (file)
@@ -1,3 +1,10 @@
+2000-04-09  Gerd Moellmann  <gerd@gnu.org>
+
+       * Makefile.in (INSTALLABLES): Add ebrowse.
+       (ebrowse): New target.
+
+       * ebrowse.c: New file.
+
 2000-03-29  Andreas Schwab  <schwab@suse.de>
 
        * make-docfile.c (scan_lisp_file): Also look for `defsubst'.
diff --git a/lib-src/ebrowse.c b/lib-src/ebrowse.c
new file mode 100644 (file)
index 0000000..5e70bcd
--- /dev/null
@@ -0,0 +1,3702 @@
+/* ebrowse.c --- parsing files for the ebrowse C++ browser
+
+   Copyright (C) 1992-1999, 2000 Free Software Foundation Inc.
+
+   Author: Gerd Moellmann <gerd@gnu.org>
+   Maintainer: FSF
+   
+   This file is part of GNU Emacs.
+
+   GNU Emacs is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   GNU Emacs is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GNU Emacs; see the file COPYING.  If not, write to
+   the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+#include "getopt.h"
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* Conditionalize function prototypes.  */
+
+#ifdef PROTOTYPES              /* From config.h.  */
+#define P_(x) x
+#else
+#define P_(x) ()
+#endif
+
+/* Value is non-zero if strings X and Y compare equal.  */
+
+#define streq(X, Y) (*(X) == *(Y) && strcmp ((X) + 1, (Y) + 1) == 0)
+
+/* The ubiquitous `max' and `min' macros.  */
+
+#ifndef max
+#define max(X, Y)      ((X) > (Y) ? (X) : (Y))
+#define min(X, Y)      ((X) < (Y) ? (X) : (Y))
+#endif
+
+/* Files are read in chunks of this number of bytes.  */
+
+#define READ_CHUNK_SIZE (100 * 1024)
+
+/* The character used as a separator in path lists (like $PATH).  */
+
+#define PATH_LIST_SEPARATOR ':'
+
+/* The default output file name.  */
+
+#define DEFAULT_OUTFILE "EBROWSE"
+
+/* A version string written to the output file.  Change this whenever
+   the structure of the output file changes.  */
+
+#define EBROWSE_FILE_VERSION "ebrowse 5.0"
+
+/* The output file consists of a tree of Lisp objects, with major
+   nodes built out of Lisp structures.  These are the heads of the
+   Lisp structs with symbols identifying their type.  */
+
+#define TREE_HEADER_STRUCT     "[ebrowse-hs "
+#define TREE_STRUCT            "[ebrowse-ts "
+#define MEMBER_STRUCT          "[ebrowse-ms "
+#define BROWSE_STRUCT          "[ebrowse-bs "
+#define CLASS_STRUCT           "[ebrowse-cs "
+
+/* The name of the symbol table entry for global functions, variables,
+   defines etc.  This name also appears in the browser display.  */
+
+#define GLOBALS_NAME "*Globals*"
+
+/* Token definitions.  */
+
+enum token
+{
+  YYEOF = 0,                   /* end of file */
+  CSTRING = 256,               /* string constant */
+  CCHAR,                       /* character constant */
+  CINT,                                /* integral constant */
+  CFLOAT,                      /* real constant */
+
+  ELLIPSIS,                    /* ... */
+  LSHIFTASGN,                  /* <<= */
+  RSHIFTASGN,                  /* >>= */
+  ARROWSTAR,                   /* ->* */
+  IDENT,                       /* identifier */
+  DIVASGN,                     /* /= */
+  INC,                         /* ++ */
+  ADDASGN,                     /* += */
+  DEC,                         /* -- */
+  ARROW,                       /* -> */
+  SUBASGN,                     /* -= */
+  MULASGN,                     /* *= */
+  MODASGN,                     /* %= */
+  LOR,                         /* || */
+  ORASGN,                      /* |= */
+  LAND,                                /* && */
+  ANDASGN,                     /* &= */
+  XORASGN,                     /* ^= */
+  POINTSTAR,                   /* .* */
+  DCOLON,                      /* :: */
+  EQ,                          /* == */
+  NE,                          /* != */
+  LE,                          /* <= */
+  LSHIFT,                      /* << */
+  GE,                          /* >= */
+  RSHIFT,                      /* >> */
+
+/* Keywords.  The undef's are there because these
+   three symbols are very likely to be defined somewhere.  */
+#undef BOOL
+#undef TRUE
+#undef FALSE
+
+  ASM,                         /* asm */
+  AUTO,                                /* auto */
+  BREAK,                       /* break */
+  CASE,                                /* case  */
+  CATCH,                       /* catch */
+  CHAR,                                /* char */
+  CLASS,                       /* class */
+  CONST,                       /* const */
+  CONTINUE,                    /* continue */
+  DEFAULT,                     /* default */
+  DELETE,                      /* delete */
+  DO,                          /* do */
+  DOUBLE,                      /* double */
+  ELSE,                                /* else */
+  ENUM,                                /* enum */
+  EXTERN,                      /* extern */
+  FLOAT,                       /* float */
+  FOR,                         /* for */
+  FRIEND,                      /* friend */
+  GOTO,                                /* goto */
+  IF,                          /* if */
+  T_INLINE,                    /* inline */
+  INT,                         /* int */
+  LONG,                                /* long */
+  NEW,                         /* new */
+  OPERATOR,                    /* operator */
+  PRIVATE,                     /* private */
+  PROTECTED,                   /* protected */
+  PUBLIC,                      /* public */
+  REGISTER,                    /* register */
+  RETURN,                      /* return */
+  SHORT,                       /* short */
+  SIGNED,                      /* signed */
+  SIZEOF,                      /* sizeof */
+  STATIC,                      /* static */
+  STRUCT,                      /* struct */
+  SWITCH,                      /* switch */
+  TEMPLATE,                    /* template */
+  THIS,                                /* this */
+  THROW,                       /* throw */
+  TRY,                         /* try */
+  TYPEDEF,                     /* typedef */
+  UNION,                       /* union */
+  UNSIGNED,                    /* unsigned */
+  VIRTUAL,                     /* virtual */
+  VOID,                                /* void */
+  VOLATILE,                    /* volatile */
+  WHILE,                       /* while */
+  MUTABLE,                     /* mutable */
+  BOOL,                                /* bool */
+  TRUE,                                /* true */
+  FALSE,                       /* false */
+  SIGNATURE,                   /* signature (GNU extension) */
+  NAMESPACE,                   /* namespace */
+  EXPLICIT,                    /* explicit */
+  TYPENAME,                    /* typename */
+  CONST_CAST,                  /* const_cast */
+  DYNAMIC_CAST,                        /* dynamic_cast */
+  REINTERPRET_CAST,            /* reinterpret_cast */
+  STATIC_CAST,                 /* static_cast */
+  TYPEID,                      /* typeid */
+  USING,                       /* using */
+  WCHAR                                /* wchar_t */
+};
+
+/* Storage classes, in a wider sense.  */
+
+enum sc
+{
+  SC_UNKNOWN,
+  SC_MEMBER,                   /* Is an instance member.  */
+  SC_STATIC,                   /* Is static member.  */
+  SC_FRIEND,                   /* Is friend function.  */
+  SC_TYPE                      /* Is a type definition.  */
+};
+
+/* Member visibility.  */
+
+enum visibility
+{
+  V_PUBLIC,
+  V_PROTECTED,
+  V_PRIVATE
+};
+
+/* Member flags.  */
+
+#define F_VIRTUAL      1       /* Is virtual function.  */
+#define F_INLINE       2       /* Is inline function.  */
+#define F_CONST                4       /* Is const.  */
+#define F_PURE         8       /* Is pure virtual function.  */
+#define F_MUTABLE      16      /* Is mutable.  */
+#define F_TEMPLATE     32      /* Is a template.  */
+#define F_EXPLICIT     64      /* Is explicit constructor.  */
+#define F_THROW                128     /* Has a throw specification.  */
+#define F_EXTERNC      256     /* Is declared extern "C".  */
+#define F_DEFINE       512     /* Is a #define.  */
+
+/* Two macros to set and test a bit in an int.  */
+
+#define SET_FLAG(F, FLAG)      ((F) |= (FLAG))
+#define HAS_FLAG(F, FLAG)      (((F) & (FLAG)) != 0)
+
+/* Structure describing a class member.  */
+
+struct member
+{
+  struct member *next;         /* Next in list of members.  */
+  struct member *anext;                /* Collision chain in member_table.  */
+  struct member **list;                /* Pointer to list in class.  */
+  unsigned param_hash;         /* Hash value for parameter types.  */
+  int vis;                     /* Visibility (public, ...).  */
+  int flags;                   /* See F_* above.  */
+  char *regexp;                        /* Matching regular expression.  */
+  char *filename;              /* Don't free this shared string.  */
+  int pos;                     /* Buffer position of occurrence.  */
+  char *def_regexp;            /* Regular expression matching definition.  */
+  char *def_filename;          /* File name of definition.  */
+  int def_pos;                 /* Buffer position of definition.  */
+  char name[1];                        /* Member name.  */
+};
+
+/* Structures of this type are used to connect class structures with
+   their super and subclasses.  */
+
+struct link
+{
+  struct sym *sym;             /* The super or subclass.  */
+  struct link *next;           /* Next in list or NULL.  */
+};
+
+/* Structure used to record namespace aliases.  */
+
+struct alias
+{
+  struct alias *next;          /* Next in list.  */
+  char name[1];                        /* Alias name.  */
+};
+
+/* The structure used to describe a class in the symbol table,
+   or a namespace in all_namespaces.  */
+
+struct sym
+{
+  int flags;                   /* Is class a template class?.  */
+  unsigned char visited;       /* Used to find circles.  */
+  struct sym *next;             /* Hash collision list.  */
+  struct link *subs;           /* List of subclasses.  */
+  struct link *supers;         /* List of superclasses.  */
+  struct member *vars;         /* List of instance variables.  */
+  struct member *fns;          /* List of instance functions.  */
+  struct member *static_vars;  /* List of static variables.  */
+  struct member *static_fns;   /* List of static functions.  */
+  struct member *friends;      /* List of friend functions.  */
+  struct member *types;                /* List of local types.  */
+  char *regexp;                        /* Matching regular expression.  */
+  int pos;                     /* Buffer position.  */
+  char *filename;              /* File in which it can be found.  */
+  char *sfilename;             /* File in which members can be found.  */
+  struct sym *namesp;          /* Namespace in which defined. .  */
+  struct alias *namesp_aliases;        /* List of aliases for namespaces.  */
+  char name[1];                 /* Name of the class.  */
+};
+
+/* Experimental: Print info for `--position-info'.  We print
+   '(CLASS-NAME SCOPE MEMBER-NAME).  */
+
+#define P_DEFN 1
+#define P_DECL  2
+
+int info_where;
+struct sym *info_cls = NULL;
+struct member *info_member = NULL;
+
+/* Experimental.  For option `--position-info', the buffer position we
+   are interested in.  When this position is reached, print out
+   information about what we know about that point.  */
+
+int info_position = -1;
+
+/* Command line options structure for getopt_long.  */
+
+struct option options[] =
+{
+  {"append",                   no_argument,       NULL, 'a'},
+  {"files",                    required_argument, NULL, 'f'},
+  {"help",                     no_argument,       NULL, -2},
+  {"min-regexp-length",        required_argument, NULL, 'm'},
+  {"max-regexp-length",        required_argument, NULL, 'M'},
+  {"no-nested-classes",        no_argument,       NULL, 'n'},
+  {"no-regexps",               no_argument,       NULL, 'x'},
+  {"no-structs-or-unions",     no_argument,       NULL, 's'},
+  {"output-file",              required_argument, NULL, 'o'},
+  {"position-info",            required_argument, NULL, 'p'},
+  {"search-path",              required_argument, NULL, 'I'},
+  {"verbose",                  no_argument,       NULL, 'v'},
+  {"version",                  no_argument,       NULL, -3},
+  {"very-verbose",             no_argument,       NULL, 'V'},
+  {NULL,                       0,                 NULL, 0}
+};
+
+/* Semantic values of tokens.  Set by yylex..  */
+
+unsigned yyival;               /* Set for token CINT.  */
+char *yytext;                  /* Set for token IDENT.  */
+char *yytext_end;
+
+/* Output file.  */
+
+FILE *yyout;
+
+/* Current line number.  */
+
+int yyline;
+
+/* The name of the current input file.  */
+
+char *filename;
+
+/* Three character class vectors, and macros to test membership
+   of characters.  */
+
+char is_ident[255];
+char is_digit[255];
+char is_white[255];
+
+#define IDENTP(C)      is_ident[(unsigned char) (C)]
+#define DIGITP(C)      is_digit[(unsigned char) (C)]
+#define WHITEP(C)      is_white[(unsigned char) (C)]
+
+/* Command line flags.  */
+
+int f_append;
+int f_verbose;
+int f_very_verbose;
+int f_structs = 1;
+int f_regexps = 1;
+int f_nested_classes = 1;
+
+/* Maximum and minimum lengths of regular expressions matching a
+   member, class etc., for writing them to the output file.  These are
+   overridable from the command line.  */
+
+int min_regexp = 5;
+int max_regexp = 50;
+
+/* Input buffer.  */
+
+char *inbuffer;
+char *in;
+int inbuffer_size;
+
+/* Return the current buffer position in the input file.  */
+
+#define BUFFER_POS() (in - inbuffer)
+
+/* If current lookahead is CSTRING, the following points to the 
+   first character in the string constant.  Used for recognizing
+   extern "C".  */
+
+char *string_start;
+
+/* The size of the hash tables for classes.and members.  Should be
+   prime.  */
+
+#define TABLE_SIZE 1001
+
+/* The hash table for class symbols.  */
+
+struct sym *class_table[TABLE_SIZE];
+
+/* Hash table containing all member structures.  This is generally
+   faster for member lookup than traversing the member lists of a
+   `struct sym'.  */
+
+struct member *member_table[TABLE_SIZE];
+
+/* The special class symbol used to hold global functions,
+   variables etc.  */
+
+struct sym *global_symbols;
+
+/* The current namespace.  */
+
+struct sym *current_namespace;
+
+/* The list of all known namespaces.  */
+
+struct sym *all_namespaces;
+
+/* Stack of namespaces we're currently nested in, during the parse.  */
+
+struct sym **namespace_stack;
+int namespace_stack_size;
+int namespace_sp;
+
+/* The current lookahead token.  */
+
+int tk = -1;
+
+/* Structure describing a keyword.  */
+
+struct kw
+{
+  char *name;                  /* Spelling.  */
+  int tk;                      /* Token value.  */
+  struct kw *next;             /* Next in collision chain.  */
+};
+
+/* Keywords are lookup up in a hash table of their own.  */
+
+#define KEYWORD_TABLE_SIZE 1001
+struct kw *keyword_table[KEYWORD_TABLE_SIZE];
+
+/* Search path.  */
+
+struct search_path
+{
+  char *path;
+  struct search_path *next;
+};
+
+struct search_path *search_path;
+struct search_path *search_path_tail;
+
+/* Function prototypes.  */
+
+int yylex P_ ((void));
+void yyparse P_ ((void));
+void re_init_parser P_ ((void));
+char *token_string P_ ((int));
+char *matching_regexp P_ ((void));
+void init_sym P_ ((void));
+struct sym *add_sym P_ ((char *, struct sym *));
+void add_link P_ ((struct sym *, struct sym *));
+void add_member_defn P_ ((struct sym *, char *, char *,
+                         int, unsigned, int, int, int));
+void add_member_decl P_ ((struct sym *, char *, char *, int,
+                         unsigned, int, int, int, int));
+void dump_roots P_ ((FILE *));
+void *xmalloc P_ ((int));
+void add_global_defn P_ ((char *, char *, int, unsigned, int, int, int));
+void add_global_decl P_ ((char *, char *, int, unsigned, int, int, int));
+void add_define P_ ((char *, char *, int));
+void mark_inherited_virtual P_ ((void));
+void leave_namespace P_ ((void));
+void enter_namespace P_ ((char *));
+void register_namespace_alias P_ ((char *, char *));
+void insert_keyword P_ ((char *, int));
+void re_init_scanner P_ ((void));
+void init_scanner P_ ((void));
+void usage P_ ((int));
+void version P_ ((void));
+void process_file P_ ((char *));
+void add_search_path P_ ((char *));
+FILE *open_file P_ ((char *));
+int process_pp_line P_ ((void));
+int dump_members P_ ((FILE *, struct member *));
+void dump_sym P_ ((FILE *, struct sym *));
+int dump_tree P_ ((FILE *, struct sym *));
+struct member *find_member P_ ((struct sym *, char *, int, int, unsigned));
+struct member *add_member P_ ((struct sym *, char *, int, int, unsigned));
+void mark_virtual P_ ((struct sym *));
+void mark_virtual P_ ((struct sym *));
+struct sym *make_namespace P_ ((char *));
+char *sym_scope P_ ((struct sym *));
+char *sym_scope_1 P_ ((struct sym *));
+int skip_to P_ ((int));
+void skip_matching P_ ((void));
+void member P_ ((struct sym *, int));
+void class_body P_ ((struct sym *, int));
+void class_definition P_ ((struct sym *, int, int, int));
+void declaration P_ ((int, int));
+unsigned parm_list P_ ((int *));
+char *operator_name P_ ((int *));
+struct sym *parse_classname P_ ((void));
+struct sym *parse_qualified_ident_or_type P_ ((char **));
+void parse_qualified_param_ident_or_type P_ ((char **));
+int globals P_ ((int));
+
+
+\f
+/***********************************************************************
+                             Utilities
+ ***********************************************************************/
+
+/* Print an error in a printf-like style with the current input file
+   name and line number.  */
+
+void
+yyerror (format, a1, a2, a3, a4, a5)
+     char *format;
+     int a1, a2, a3, a4, a5;
+{
+  fprintf (stderr, "%s:%d: ", filename, yyline);
+  fprintf (stderr, format, a1, a2, a3, a4, a5);
+  putc ('\n', stderr);
+}
+
+
+/* Like malloc but print an error and exit if not enough memory is
+   available.  */
+
+void *
+xmalloc (nbytes)
+     int nbytes;
+{
+  void *p = malloc (nbytes);
+  if (p)
+    return p;
+  yyerror ("out of memory");
+  exit (1);
+}
+
+
+/* Like realloc but print an error and exit if out of memory.  */
+
+void *
+xrealloc (p, sz)
+     void *p;
+     int sz;
+{
+  p = realloc (p, sz);
+  if (p)
+    return p;
+  yyerror ("out of memory");
+  exit (1);
+}
+
+
+/* Like strdup, but print an error and exit if not enough memory is
+   available..  If S is null, return null.  */
+
+char *
+xstrdup (s)
+     char *s;
+{
+  if (s)
+    s = strcpy (xmalloc (strlen (s) + 1), s);
+  return s;
+}
+
+
+\f
+/***********************************************************************
+                              Symbols
+ ***********************************************************************/
+
+/* Initialize the symbol table.  This currently only sets up the
+   special symbol for globals (`*Globals*').  */
+
+void
+init_sym ()
+{
+  global_symbols = add_sym (GLOBALS_NAME, NULL);
+}
+
+
+/* Add a symbol for class NAME to the symbol table.  NESTED_IN_CLASS
+   is the class in which class NAME was found.  If it is null,
+   this means the scope of NAME is the current namespace.
+
+   If a symbol for NAME already exists, return that.  Otherwise
+   create a new symbol and set it to default values.  */
+
+struct sym *
+add_sym (name, nested_in_class)
+     char *name;
+     struct sym *nested_in_class;
+{
+  struct sym *sym;
+  unsigned h;
+  char *s;
+  struct sym *scope = nested_in_class ? nested_in_class : current_namespace;
+
+  for (s = name, h = 0; *s; ++s)
+    h = (h << 1) ^ *s;
+  h %= TABLE_SIZE;
+
+  for (sym = class_table[h]; sym; sym = sym->next)
+    if (streq (name, sym->name) && sym->namesp == scope)
+      break;
+
+  if (sym == NULL)
+    {
+      if (f_very_verbose)
+       {
+         putchar ('\t');
+         puts (name);
+       }
+
+      sym = (struct sym *) xmalloc (sizeof *sym + strlen (name));
+      bzero (sym, sizeof *sym);
+      strcpy (sym->name, name);
+      sym->namesp = scope;
+      sym->next = class_table[h];
+      class_table[h] = sym;
+    }
+
+  return sym;
+}
+
+
+/* Add links between superclass SUPER and subclass SUB.  */
+
+void
+add_link (super, sub)
+     struct sym *super, *sub;
+{
+  struct link *lnk, *lnk2, *p, *prev;
+
+  /* See if a link already exists.  */
+  for (p = super->subs, prev = NULL;
+       p && strcmp (sub->name, p->sym->name) > 0;
+       prev = p, p = p->next)
+    ;
+
+  /* Avoid duplicates.  */
+  if (p == NULL || p->sym != sub)
+    {
+      lnk = (struct link *) xmalloc (sizeof *lnk);
+      lnk2 = (struct link *) xmalloc (sizeof *lnk2);
+  
+      lnk->sym = sub;
+      lnk->next = p;
+
+      if (prev)
+       prev->next = lnk;
+      else
+       super->subs = lnk;
+
+      lnk2->sym = super;
+      lnk2->next = sub->supers;
+      sub->supers = lnk2;
+    }
+}
+
+
+/* Find in class CLS member NAME.
+
+   VAR non-zero means look for a member variable; otherwise a function
+   is searched.  SC specifies what kind of member is searched---a
+   static, or per-instance member etc.  HASH is a hash code for the
+   parameter types of functions.  Value is a pointer to the member
+   found or null if not found.  */
+
+struct member *
+find_member (cls, name, var, sc, hash)
+     struct sym *cls;
+     char *name;
+     int var, sc;
+     unsigned hash;
+{
+  struct member **list;
+  struct member *p;
+  unsigned name_hash = 0;
+  char *s;
+  int i;
+
+  switch (sc)
+    {
+    case SC_FRIEND:
+      list = &cls->friends;
+      break;
+      
+    case SC_TYPE:
+      list = &cls->types;
+      break;
+      
+    case SC_STATIC:
+      list = var ? &cls->static_vars : &cls->static_fns;
+      break;
+      
+    default:
+      list = var ? &cls->vars : &cls->fns;
+      break;
+    }
+
+  for (s = name; *s; ++s)
+    name_hash = (name_hash << 1) ^ *s;
+  i = name_hash % TABLE_SIZE;
+
+  for (p = member_table[i]; p; p = p->anext)
+    if (p->list == list && p->param_hash == hash && streq (name, p->name))
+      break;
+
+  return p;
+}
+
+
+/* Add to class CLS information for the declaration of member NAME.
+   REGEXP is a regexp matching the declaration, if non-null.  POS is
+   the position in the source where the declaration is found.  HASH is
+   a hash code for the parameter list of the member, if it's a
+   function.  VAR non-zero means member is a variable or type.  SC
+   specifies the type of member (instance member, static, ...).  VIS
+   is the member's visibility (public, protected, private).  FLAGS is
+   a bit set giving additional information about the member (see the
+   F_* defines).  */
+
+void
+add_member_decl (cls, name, regexp, pos, hash, var, sc, vis, flags)
+     struct sym *cls;
+     char *name;
+     char *regexp;
+     int pos;
+     unsigned hash;
+     int var;
+     int sc;
+     int vis;
+     int flags;
+{
+  struct member *m;
+
+  m = find_member (cls, name, var, sc, hash);
+  if (m == NULL)
+    m = add_member (cls, name, var, sc, hash);
+
+  /* Have we seen a new filename?  If so record that.  */
+  if (!cls->filename || !streq (cls->filename, filename))
+    m->filename = filename;
+
+  m->regexp = regexp;
+  m->pos = pos;
+  m->flags = flags;
+
+  switch (vis)
+    {
+    case PRIVATE:
+      m->vis = V_PRIVATE;
+      break;
+
+    case PROTECTED:
+      m->vis = V_PROTECTED;
+      break;
+
+    case PUBLIC:
+      m->vis = V_PUBLIC;
+      break;
+    }
+
+  info_where = P_DECL;
+  info_cls = cls;
+  info_member = m;
+}
+
+
+/* Add to class CLS information for the definition of member NAME.
+   REGEXP is a regexp matching the declaration, if non-null.  POS is
+   the position in the source where the declaration is found.  HASH is
+   a hash code for the parameter list of the member, if it's a
+   function.  VAR non-zero means member is a variable or type.  SC
+   specifies the type of member (instance member, static, ...).  VIS
+   is the member's visibility (public, protected, private).  FLAGS is
+   a bit set giving additional information about the member (see the
+   F_* defines).  */
+
+void
+add_member_defn (cls, name, regexp, pos, hash, var, sc, flags)
+     struct sym *cls;
+     char *name;
+     char *regexp;
+     int pos;
+     unsigned hash;
+     int var;
+     int sc;
+     int flags;
+{
+  struct member *m;
+
+  if (sc == SC_UNKNOWN)
+    {
+      m = find_member (cls, name, var, SC_MEMBER, hash);
+      if (m == NULL)
+       {
+         m = find_member (cls, name, var, SC_STATIC, hash);
+         if (m == NULL)
+           m = add_member (cls, name, var, sc, hash);
+       }
+    }
+  else
+    {
+      m = find_member (cls, name, var, sc, hash);
+      if (m == NULL)
+       m = add_member (cls, name, var, sc, hash);
+    }
+
+  if (!cls->sfilename)
+    cls->sfilename = filename;
+
+  if (!streq (cls->sfilename, filename))
+    m->def_filename = filename;
+
+  m->def_regexp = regexp;
+  m->def_pos = pos;
+  m->flags |= flags;
+
+  info_where = P_DEFN;
+  info_cls = cls;
+  info_member = m;
+}
+
+
+/* Add a symbol for a define named NAME to the symbol table.
+   REGEXP is a regular expression matching the define in the source,
+   if it is non-null.  POS is the position in the file.  */
+
+void
+add_define (name, regexp, pos)
+     char *name, *regexp;
+     int pos;
+{
+  add_global_defn (name, regexp, pos, 0, 1, SC_FRIEND, F_DEFINE);
+  add_global_decl (name, regexp, pos, 0, 1, SC_FRIEND, F_DEFINE);
+}
+
+
+/* Add information for the global definition of NAME.
+   REGEXP is a regexp matching the declaration, if non-null.  POS is
+   the position in the source where the declaration is found.  HASH is
+   a hash code for the parameter list of the member, if it's a
+   function.  VAR non-zero means member is a variable or type.  SC
+   specifies the type of member (instance member, static, ...).  VIS
+   is the member's visibility (public, protected, private).  FLAGS is
+   a bit set giving additional information about the member (see the
+   F_* defines).  */
+
+void
+add_global_defn (name, regexp, pos, hash, var, sc, flags)
+     char *name, *regexp;
+     int pos;
+     unsigned hash;
+     int var;
+     int sc;
+     int flags;
+{
+  int i;
+  struct sym *sym;
+
+  /* Try to find out for which classes a function is a friend, and add
+     what we know about it to them.  */
+  if (!var)
+    for (i = 0; i < TABLE_SIZE; ++i)
+      for (sym = class_table[i]; sym; sym = sym->next)
+       if (sym != global_symbols && sym->friends)
+         if (find_member (sym, name, 0, SC_FRIEND, hash))
+           add_member_defn (sym, name, regexp, pos, hash, 0,
+                            SC_FRIEND, flags);
+
+  /* Add to global symbols.  */
+  add_member_defn (global_symbols, name, regexp, pos, hash, var, sc, flags);
+}
+
+
+/* Add information for the global declaration of NAME.
+   REGEXP is a regexp matching the declaration, if non-null.  POS is
+   the position in the source where the declaration is found.  HASH is
+   a hash code for the parameter list of the member, if it's a
+   function.  VAR non-zero means member is a variable or type.  SC
+   specifies the type of member (instance member, static, ...).  VIS
+   is the member's visibility (public, protected, private).  FLAGS is
+   a bit set giving additional information about the member (see the
+   F_* defines).  */
+
+void 
+add_global_decl (name, regexp, pos, hash, var, sc, flags)
+     char *name, *regexp;
+     int pos;
+     unsigned hash;
+     int var;
+     int sc;
+     int flags;
+{
+  /* Add declaration only if not already declared.  Header files must
+     be processed before source files for this to have the right effect.
+     I do not want to handle implicit declarations at the moment.  */
+  struct member *m;
+  struct member *found;
+
+  m = found = find_member (global_symbols, name, var, sc, hash);
+  if (m == NULL)
+    m = add_member (global_symbols, name, var, sc, hash);
+
+  /* Definition already seen => probably last declaration implicit.
+     Override.  This means that declarations must always be added to
+     the symbol table before definitions.  */
+  if (!found)
+    {
+      if (!global_symbols->filename
+         || !streq (global_symbols->filename, filename))
+       m->filename = filename;
+
+      m->regexp = regexp;
+      m->pos = pos;
+      m->vis = V_PUBLIC;
+      m->flags = flags;
+
+      info_where = P_DECL;
+      info_cls = global_symbols;
+      info_member = m;
+    }
+}
+
+
+/* Add a symbol for member NAME to class CLS.
+   VAR non-zero means it's a variable.  SC specifies the kind of
+   member.  HASH is a hash code for the parameter types of a function.
+   Value is a pointer to the member's structure.  */
+
+struct member *
+add_member (cls, name, var, sc, hash)
+     struct sym *cls;
+     char *name;
+     int var;
+     int sc;
+     unsigned hash;
+{
+  struct member *m = (struct member *) xmalloc (sizeof *m + strlen (name));
+  struct member **list;
+  struct member *p;
+  struct member *prev;
+  unsigned name_hash = 0;
+  int i;
+  char *s;
+
+  strcpy (m->name, name);
+  m->param_hash = hash;
+
+  m->vis = 0;
+  m->flags = 0;
+  m->regexp = NULL;
+  m->filename = NULL;
+  m->pos = 0;
+  m->def_regexp = NULL;
+  m->def_filename = NULL;
+  m->def_pos = 0;
+
+  assert (cls != NULL);
+
+  switch (sc)
+    {
+    case SC_FRIEND:
+      list = &cls->friends;
+      break;
+      
+    case SC_TYPE:
+      list = &cls->types;
+      break;
+      
+    case SC_STATIC:
+      list = var ? &cls->static_vars : &cls->static_fns;
+      break;
+      
+    default:
+      list = var ? &cls->vars : &cls->fns;
+      break;
+    }
+
+  for (s = name; *s; ++s)
+    name_hash = (name_hash << 1) ^ *s;
+  i = name_hash % TABLE_SIZE;
+  m->anext = member_table[i];
+  member_table[i] = m;
+  m->list = list;
+
+  /* Keep the member list sorted.  It's cheaper to do it here than to
+     sort them in Lisp.  */
+  for (prev = NULL, p = *list;
+       p && strcmp (name, p->name) > 0;
+       prev = p, p = p->next)
+    ;
+
+  m->next = p;
+  if (prev)
+    prev->next = m;
+  else
+    *list = m;
+  return m;
+}
+
+
+/* Given the root R of a class tree, step through all subclasses
+   recursively, marking functions as virtual that are declared virtual
+   in base classes.  */
+
+void
+mark_virtual (r)
+     struct sym *r;
+{
+  struct link *p;
+  struct member *m, *m2;
+
+  for (p = r->subs; p; p = p->next)
+    {
+      for (m = r->fns; m; m = m->next)
+        if (HAS_FLAG (m->flags, F_VIRTUAL))
+          {
+            for (m2 = p->sym->fns; m2; m2 = m2->next)
+              if (m->param_hash == m2->param_hash && streq (m->name, m2->name))
+                SET_FLAG (m2->flags, F_VIRTUAL);
+          }
+
+      mark_virtual (p->sym);
+    }
+}
+
+
+/* For all roots of the class tree, mark functions as virtual that
+   are virtual because of a virtual declaration in a base class.  */
+
+void
+mark_inherited_virtual ()
+{
+  struct sym *r;
+  int i;
+
+  for (i = 0; i < TABLE_SIZE; ++i)
+    for (r = class_table[i]; r; r = r->next)
+      if (r->supers == NULL)
+        mark_virtual (r);
+}
+
+
+/* Create and return a symbol for a namespace with name NAME.  */
+
+struct sym *
+make_namespace (name)
+     char *name;
+{
+  struct sym *s = (struct sym *) xmalloc (sizeof *s + strlen (name));
+  bzero (s, sizeof *s);
+  strcpy (s->name, name);
+  s->next = all_namespaces;
+  s->namesp = current_namespace;
+  all_namespaces = s;
+  return s;
+}
+
+
+/* Find the symbol for namespace NAME.  If not found, add a new symbol
+   for NAME to all_namespaces.  */
+
+struct sym *
+find_namespace (name)
+     char *name;
+{
+  struct sym *p;
+  
+  for (p = all_namespaces; p; p = p->next)
+    {
+      if (streq (p->name, name))
+       break;
+      else
+       {
+         struct alias *p2;
+         for (p2 = p->namesp_aliases; p2; p2 = p2->next)
+           if (streq (p2->name, name))
+             break;
+         if (p2)
+           break;
+       }
+    }
+
+  if (p == NULL)
+    p = make_namespace (name);
+
+  return p;
+}
+  
+
+/* Register the name NEW_NAME as an alias for namespace OLD_NAME.  */
+
+void
+register_namespace_alias (new_name, old_name)
+     char *new_name, *old_name;
+{
+  struct sym *p = find_namespace (old_name);
+  struct alias *al;
+
+  /* Is it already in the list of aliases?  */
+  for (al = p->namesp_aliases; al; al = al->next)
+    if (streq (new_name, p->name))
+      return;
+
+  al = (struct alias *) xmalloc (sizeof *al + strlen (new_name));
+  strcpy (al->name, new_name);
+  al->next = p->namesp_aliases;
+  p->namesp_aliases = al;
+}
+
+
+/* Enter namespace with name NAME.  */
+
+void
+enter_namespace (name)
+     char *name;
+{
+  struct sym *p = find_namespace (name);
+
+  if (namespace_sp == namespace_stack_size)
+    {
+      int size = max (10, 2 * namespace_stack_size);
+      namespace_stack = (struct sym **) xrealloc (namespace_stack, size);
+      namespace_stack_size = size;
+    }
+  
+  namespace_stack[namespace_sp++] = current_namespace;
+  current_namespace = p;
+}
+
+
+/* Leave the current namespace.  */
+
+void
+leave_namespace ()
+{
+  assert (namespace_sp > 0);
+  current_namespace = namespace_stack[--namespace_sp];
+}
+
+
+\f
+/***********************************************************************
+                      Writing the Output File
+ ***********************************************************************/
+
+/* Write string S to the output file FP in a Lisp-readable form.
+   If S is null, write out `()'.  */
+
+#define PUTSTR(s, fp)                          \
+  do {                                         \
+    if (!s)                                    \
+      {                                                \
+        putc ('(', fp);                                \
+        putc (')', fp);                                \
+        putc (' ', fp);                                \
+      }                                                \
+    else                                       \
+      {                                                \
+        putc ('"', fp);                                \
+        fputs (s, fp);                         \
+        putc ('"', fp);                                \
+        putc (' ', fp);                                \
+      }                                                \
+   } while (0)
+
+/* A dynamically allocated buffer for constructing a scope name.  */
+
+char *scope_buffer;
+int scope_buffer_size;
+int scope_buffer_len;
+
+
+/* Make sure scope_buffer has enough room to add LEN chars to it.  */
+
+void
+ensure_scope_buffer_room (len)
+     int len;
+{
+  if (scope_buffer_len + len >= scope_buffer_size)
+    {
+      int new_size = max (2 * scope_buffer_size, scope_buffer_len + len);
+      scope_buffer = (char *) xrealloc (new_size);
+      scope_buffer_size = new_size;
+    }
+}
+
+
+/* Recursively add the scope names of symbol P and the scopes of its
+   namespaces to scope_buffer.  Value is a pointer to the complete
+   scope name constructed.  */
+
+char *
+sym_scope_1 (p)
+     struct sym *p;
+{
+  int len;
+  
+  if (p->namesp)
+    sym_scope_1 (p->namesp);
+
+  if (*scope_buffer)
+    {
+      ensure_scope_buffer_room (3);
+      strcat (scope_buffer, "::");
+      scope_buffer_len += 2;
+    }
+
+  len = strlen (p->name);
+  ensure_scope_buffer_room (len + 1);
+  strcat (scope_buffer, p->name);
+  scope_buffer_len += len;
+  
+  if (HAS_FLAG (p->flags, F_TEMPLATE))
+    {
+      ensure_scope_buffer_room (3);
+      strcat (scope_buffer, "<>");
+      scope_buffer_len += 2;
+    }
+  
+  return scope_buffer;
+}
+
+
+/* Return the scope of symbol P in printed representation, i.e.
+   as it would appear in a C*+ source file.  */
+
+char *
+sym_scope (p)
+     struct sym *p;
+{
+  if (!scope_buffer)
+    {
+      scope_buffer_size = 1024;
+      scope_buffer = (char *) xmalloc (scope_buffer_size);
+    }
+  
+  *scope_buffer = '\0';
+  scope_buffer_len = 0;
+  
+  if (p->namesp)
+    sym_scope_1 (p->namesp);
+
+  return scope_buffer;
+}
+
+
+/* Dump the list of members M to file FP.  Value is the length of the
+   list.  */
+
+int
+dump_members (fp, m)
+     FILE *fp;
+     struct member *m;
+{
+  int n;
+
+  putc ('(', fp);
+
+  for (n = 0; m; m = m->next, ++n)
+    {
+      fputs (MEMBER_STRUCT, fp);
+      PUTSTR (m->name, fp);
+      PUTSTR (NULL, fp);               /* FIXME? scope for globals */
+      fprintf (fp, "%u ", (unsigned) m->flags);
+      PUTSTR (m->filename, fp);
+      PUTSTR (m->regexp, fp);
+      fprintf (fp, "%u ", (unsigned) m->pos);
+      fprintf (fp, "%u ", (unsigned) m->vis);
+      putc (' ', fp);
+      PUTSTR (m->def_filename, fp);
+      PUTSTR (m->def_regexp, fp);
+      fprintf (fp, "%u", (unsigned) m->def_pos);
+      putc (']', fp);
+      putc ('\n', fp);
+    }
+
+  putc (')', fp);
+  putc ('\n', fp);
+  return n;
+}
+
+
+/* Dump class ROOT to stream FP.  */
+
+void
+dump_sym (fp, root)
+     FILE *fp;
+     struct sym *root;
+{
+  fputs (CLASS_STRUCT, fp);
+  PUTSTR (root->name, fp);
+  
+  /* Print scope, if any.  */
+  if (root->namesp)
+    PUTSTR (sym_scope (root), fp);
+  else
+    PUTSTR (NULL, fp);
+  
+  /* Print flags.  */
+  fprintf (fp, "%u", root->flags);
+  PUTSTR (root->filename, fp);
+  PUTSTR (root->regexp, fp);
+  fprintf (fp, "%u", (unsigned) root->pos);
+  PUTSTR (root->sfilename, fp);
+  putc (']', fp);
+  putc ('\n', fp);
+}
+
+
+/* Dump class ROOT and its subclasses to file FP.  Value is the
+   number of classes written.  */
+
+int
+dump_tree (fp, root)
+     FILE *fp;
+     struct sym *root;
+{
+  struct link *lk;
+  unsigned n = 0;
+
+  dump_sym (fp, root);
+
+  if (f_verbose)
+    {
+      putchar ('+');
+      fflush (stdout);
+    }
+
+  putc ('(', fp);
+
+  for (lk = root->subs; lk; lk = lk->next)
+    {
+      fputs (TREE_STRUCT, fp);
+      n += dump_tree (fp, lk->sym);
+      putc (']', fp);
+    }
+
+  putc (')', fp);
+
+  dump_members (fp, root->vars);
+  n += dump_members (fp, root->fns);
+  dump_members (fp, root->static_vars);
+  n += dump_members (fp, root->static_fns);
+  n += dump_members (fp, root->friends);
+  dump_members (fp, root->types);
+
+  /* Superclasses.  */
+  putc ('(', fp);
+  putc (')', fp);
+
+  /* Mark slot.  */
+  putc ('(', fp);
+  putc (')', fp);
+
+  putc ('\n', fp);
+  return n;
+}
+
+
+/* Dump the entire class tree to file FP.  */
+
+void
+dump_roots (fp)
+     FILE *fp;
+{
+  int i, n = 0;
+  struct sym *r;
+
+  /* Output file header containing version string, command line
+     options etc.  */
+  if (!f_append)
+    {
+      fputs (TREE_HEADER_STRUCT, fp);
+      PUTSTR (EBROWSE_FILE_VERSION, fp);
+
+      putc ('\"', fp);
+      if (!f_structs)
+       fputs (" -s", fp);
+      if (f_regexps)
+       fputs (" -x", fp);
+      putc ('\"', fp);
+      fputs (" ()", fp);
+      fputs (" ()", fp);
+      putc (']', fp);
+    }
+
+  /* Mark functions as virtual that are so because of functions
+     declared virtual in base classes.  */
+  mark_inherited_virtual ();
+
+  /* Dump the roots of the graph.  */
+  for (i = 0; i < TABLE_SIZE; ++i)
+    for (r = class_table[i]; r; r = r->next)
+      if (!r->supers)
+        {
+         fputs (TREE_STRUCT, fp);
+          n += dump_tree (fp, r);
+         putc (']', fp);
+        }
+
+  if (f_verbose)
+    putchar ('\n');
+}
+
+
+\f
+/***********************************************************************
+                               Scanner
+ ***********************************************************************/
+
+#ifdef DEBUG
+#define INCREMENT_LINENO                       \
+do {                                           \
+  if (f_very_verbose)                          \
+    {                                          \
+      ++yyline;                                        \
+      printf ("%d:\n", yyline);                        \
+    }                                          \
+  else                                         \
+    ++yyline;                                  \
+} while (0)
+#else
+#define INCREMENT_LINENO       ++yyline
+#endif
+
+/* Define two macros for accessing the input buffer (current input
+   file).  GET(C) sets C to the next input character and advances the
+   input pointer.  UNGET retracts the input pointer.  */
+
+#define GET(C) ((C) = *in++)
+#define UNGET() (--in)
+
+
+/* Process a preprocessor line.  Value is the next character from the
+   input buffer not consumed.  */
+
+int
+process_pp_line ()
+{
+  int in_comment = 0;
+  int c;
+  char *p = yytext;
+
+  /* Skip over white space.  The `#' has been consumed already.  */
+  while (WHITEP (GET (c)))
+    ;
+
+  /* Read the preprocessor command (if any).  */
+  while (IDENTP (c))
+    {
+      *p++ = c;
+      GET (c);
+    }
+
+  /* Is it a `define'?  */
+  *p = '\0';
+
+  if (*yytext && streq (yytext, "define"))
+    {
+      p = yytext;
+      while (WHITEP (c))
+       GET (c);
+      while (IDENTP (c))
+       {
+         *p++ = c;
+         GET (c);
+       }
+
+      *p = '\0';
+
+      if (*yytext)
+       {
+         char *regexp = matching_regexp ();
+         int pos = BUFFER_POS ();
+         add_define (yytext, regexp, pos);
+       }
+    }
+  
+  while (c && (c != '\n' || in_comment))
+    {
+      if (c == '\\')
+       GET (c);
+      else if (c == '/' && !in_comment)
+       {
+         if (GET (c) == '*')
+           in_comment = 1;
+       }
+      else if (c == '*' && in_comment)
+       {
+         if (GET (c) == '/')
+           in_comment = 0;
+       }
+      
+      if (c == '\n')
+       INCREMENT_LINENO;
+
+      GET (c);
+    }
+  
+  return c;
+}
+
+
+/* Value is the next token from the input buffer.  */
+
+int
+yylex ()
+{
+  int c;
+  char end_char;
+  char *p;
+
+  for (;;)
+    {
+      while (WHITEP (GET (c)))
+        ;
+
+      switch (c)
+        {
+        case '\n':
+          INCREMENT_LINENO;
+          break;
+
+        case '\r':
+          break;
+
+        case 0:
+          /* End of file.  */
+          return YYEOF;
+
+        case '\\':
+          GET (c);
+          break;
+
+        case '"':
+        case '\'':
+          /* String and character constants.  */
+          end_char = c;
+          string_start = in;
+          while (GET (c) && c != end_char)
+            {
+              switch (c)
+                {
+                case '\\':
+                  /* Escape sequences.  */
+                  if (!GET (c))
+                    {
+                      if (end_char == '\'')
+                        yyerror ("EOF in character constant");
+                      else
+                        yyerror ("EOF in string constant");
+                      goto end_string;
+                    }
+                  else switch (c)
+                    {
+                    case '\n':
+                    case 'a':
+                    case 'b':
+                    case 'f':
+                    case 'n':
+                    case 'r':
+                    case 't':
+                    case 'v':
+                      break;
+
+                    case 'x':
+                      {
+                        /* Hexadecimal escape sequence.  */
+                        int i;
+                        for (i = 0; i < 2; ++i)
+                          {
+                            GET (c);
+
+                            if (c >= '0' && c <= '7')
+                              ;
+                            else if (c >= 'a' && c <= 'f')
+                              ;
+                            else if (c >= 'A' && c <= 'F')
+                              ;
+                            else
+                              {
+                                UNGET ();
+                                break;
+                              }
+                          }
+                      }
+                      break;
+
+                    case '0':
+                      {
+                        /* Octal escape sequence.  */
+                        int i;
+                        for (i = 0; i < 3; ++i)
+                          {
+                            GET (c);
+
+                            if (c >= '0' && c <= '7')
+                              ;
+                            else
+                              {
+                                UNGET ();
+                                break;
+                              }
+                          }
+                      }
+                      break;
+
+                    default:
+                      break;
+                    }
+                  break;
+
+                case '\n':
+                  if (end_char == '\'')
+                    yyerror ("newline in character constant");
+                  else
+                    yyerror ("newline in string constant");
+                  INCREMENT_LINENO;
+                  goto end_string;
+
+                default:
+                  break;
+                }
+            }
+
+        end_string:
+          return end_char == '\'' ? CCHAR : CSTRING;
+
+        case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
+        case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
+        case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
+        case 'v': case 'w': case 'x': case 'y': case 'z':
+        case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
+        case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
+        case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
+        case 'V': case 'W': case 'X': case 'Y': case 'Z': case '_':
+          {
+            /* Identifier and keywords.  */
+            unsigned hash;
+            struct kw *k;
+
+            p = yytext;
+            *p++ = hash = c;
+
+            while (IDENTP (GET (*p)))
+             {
+               hash = (hash << 1) ^ *p++;
+               if (p == yytext_end - 1)
+                 {
+                   int size = yytext_end - yytext;
+                   yytext = (char *) xrealloc (yytext, 2 * size);
+                   yytext_end = yytext + 2 * size;
+                   p = yytext + size - 1;
+                 }
+             }
+
+            UNGET ();
+            *p = 0;
+
+            for (k = keyword_table[hash % KEYWORD_TABLE_SIZE]; k; k = k->next)
+              if (streq (k->name, yytext))
+                return k->tk;
+
+            return IDENT;
+          }
+
+        case '/':
+          /* C and C++ comments, '/' and '/='.  */
+          switch (GET (c))
+            {
+            case '*':
+              while (GET (c))
+                {
+                  switch (c)
+                    {
+                    case '*':
+                      if (GET (c) == '/')
+                        goto comment_end;
+                      UNGET ();
+                      break;
+                    case '\\':
+                      GET (c);
+                      break;
+                    case '\n':
+                      INCREMENT_LINENO;
+                      break;
+                    }
+                }
+            comment_end:;
+              break;
+
+            case '=':
+              return DIVASGN;
+
+            case '/':
+             while (GET (c) && c != '\n')
+               ;
+             INCREMENT_LINENO;
+             break;
+
+            default:
+              UNGET ();
+              return '/';
+            }
+          break;
+
+        case '+':
+          if (GET (c) == '+')
+            return INC;
+          else if (c == '=')
+            return ADDASGN;
+          UNGET ();
+          return '+';
+
+        case '-':
+          switch (GET (c))
+            {
+            case '-':
+              return DEC;
+            case '>':
+              if (GET (c) == '*')
+                return ARROWSTAR;
+              UNGET ();
+              return ARROW;
+            case '=':
+              return SUBASGN;
+            }
+          UNGET ();
+          return '-';
+
+        case '*':
+          if (GET (c) == '=')
+            return MULASGN;
+          UNGET ();
+          return '*';
+
+        case '%':
+          if (GET (c) == '=')
+            return MODASGN;
+          UNGET ();
+          return '%';
+
+        case '|':
+          if (GET (c) == '|')
+            return LOR;
+          else if (c == '=')
+            return ORASGN;
+          UNGET ();
+          return '|';
+
+        case '&':
+          if (GET (c) == '&')
+            return LAND;
+          else if (c == '=')
+            return ANDASGN;
+          UNGET ();
+          return '&';
+
+        case '^':
+          if (GET (c) == '=')
+            return XORASGN;
+          UNGET ();
+          return '^';
+
+        case '.':
+          if (GET (c) == '*')
+            return POINTSTAR;
+          else if (c == '.')
+            {
+              if (GET (c) != '.')
+                yyerror ("invalid token '..' ('...' assumed)");
+              UNGET ();
+              return ELLIPSIS;
+            }
+          else if (!DIGITP (c))
+            {
+              UNGET ();
+              return '.';
+            }
+          goto mantissa;
+
+        case ':':
+          if (GET (c) == ':')
+            return DCOLON;
+          UNGET ();
+          return ':';
+
+        case '=':
+          if (GET (c) == '=')
+            return EQ;
+          UNGET ();
+          return '=';
+
+        case '!':
+          if (GET (c) == '=')
+            return NE;
+          UNGET ();
+          return '!';
+
+        case '<':
+          switch (GET (c))
+            {
+            case '=':
+              return LE;
+            case '<':
+              if (GET (c) == '=')
+                return LSHIFTASGN;
+              UNGET ();
+              return LSHIFT;
+            }
+          UNGET ();
+          return '<';
+
+        case '>':
+          switch (GET (c))
+            {
+            case '=':
+              return GE;
+            case '>':
+              if (GET (c) == '=')
+                return RSHIFTASGN;
+              UNGET ();
+              return RSHIFT;
+            }
+          UNGET ();
+          return '>';
+
+        case '#':
+          c = process_pp_line ();
+          if (c == 0)
+            return YYEOF;
+          break;
+
+        case '(': case ')': case '[': case ']': case '{': case '}':
+        case ';': case ',': case '?': case '~':
+          return c;
+
+        case '0':
+          yyival = 0;
+
+          if (GET (c) == 'x' || c == 'X')
+            {
+              while (GET (c))
+                {
+                  if (DIGITP (c))
+                    yyival = yyival * 16 + c - '0';
+                  else if (c >= 'a' && c <= 'f')
+                    yyival = yyival * 16 + c - 'a' + 10;
+                  else if (c >= 'A' && c <= 'F')
+                    yyival = yyival * 16 + c - 'A' + 10;
+                  else
+                    break;
+                }
+
+              goto int_suffixes;
+            }
+          else if (c == '.')
+            goto mantissa;
+
+          while (c >= '0' && c <= '7')
+            {
+              yyival = (yyival << 3) + c - '0';
+              GET (c);
+            }
+
+        int_suffixes:
+          /* Integer suffixes.  */
+          while (isalpha (c))
+            GET (c);
+          UNGET ();
+          return CINT;
+
+        case '1': case '2': case '3': case '4': case '5': case '6':
+        case '7': case '8': case '9':
+          /* Integer or floating constant, part before '.'.  */
+          yyival = c - '0';
+
+          while (GET (c) && DIGITP (c))
+            yyival = 10 * yyival + c - '0';
+
+          if (c != '.')
+            goto int_suffixes;
+
+        mantissa:
+          /* Digits following '.'.  */
+          while (DIGITP (c))
+            GET (c);
+
+          /* Optional exponent.  */
+          if (c == 'E' || c == 'e')
+            {
+              if (GET (c) == '-' || c == '+')
+                GET (c);
+
+              while (DIGITP (c))
+                GET (c);
+            }
+
+          /* Optional type suffixes.  */
+          while (isalpha (c))
+            GET (c);
+         UNGET ();
+          return CFLOAT;
+
+        default:
+          break;
+        }
+    }
+}
+
+
+/* Value is the string from the start of the line to the current
+   position in the input buffer, or maybe a bit more if that string is
+   shorter than min_regexp.  */
+
+char *
+matching_regexp ()
+{
+  char *p;
+  char *s;
+  char *t;
+  static char *buffer, *end_buf;
+
+  if (!f_regexps)
+    return NULL;
+
+  if (buffer == NULL)
+    {
+      buffer = (char *) xmalloc (max_regexp);
+      end_buf = &buffer[max_regexp] - 1;
+    }
+
+  /* Scan back to previous newline of buffer start.  */
+  for (p = in - 1; p > inbuffer && *p != '\n'; --p)
+    ;
+
+  if (*p == '\n')
+    {
+      while (in - p < min_regexp && p > inbuffer)
+        {
+          /* Line probably not significant enough */
+          for (--p; p >= inbuffer && *p != '\n'; --p)
+            ;
+        }
+      if (*p == '\n')
+        ++p;
+    }
+
+  /* Copy from end to make sure significant portions are included.
+     This implies that in the browser a regular expressing of the form
+     `^.*{regexp}' has to be used.  */
+  for (s = end_buf - 1, t = in; s > buffer && t > p;)
+    {
+      *--s = *--t;
+
+      if (*s == '"')
+        *--s = '\\';
+    }
+
+  *(end_buf - 1) = '\0';
+  return xstrdup (s);
+}
+
+
+/* Return a printable representation of token T.  */
+
+char *
+token_string (t)
+     int t;
+{
+  static char b[3];
+
+  switch (t)
+    {
+    case CSTRING:               return "string constant";
+    case CCHAR:                 return "char constant";
+    case CINT:                  return "int constant";
+    case CFLOAT:                return "floating constant";
+    case ELLIPSIS:              return "...";
+    case LSHIFTASGN:            return "<<=";
+    case RSHIFTASGN:            return ">>=";
+    case ARROWSTAR:             return "->*";
+    case IDENT:                 return "identifier";
+    case DIVASGN:               return "/=";
+    case INC:                   return "++";
+    case ADDASGN:               return "+=";
+    case DEC:                   return "--";
+    case ARROW:                 return "->";
+    case SUBASGN:               return "-=";
+    case MULASGN:               return "*=";
+    case MODASGN:               return "%=";
+    case LOR:                   return "||";
+    case ORASGN:                return "|=";
+    case LAND:                  return "&&";
+    case ANDASGN:               return "&=";
+    case XORASGN:               return "^=";
+    case POINTSTAR:             return ".*";
+    case DCOLON:                return "::";
+    case EQ:                    return "==";
+    case NE:                    return "!=";
+    case LE:                    return "<=";
+    case LSHIFT:                return "<<";
+    case GE:                    return ">=";
+    case RSHIFT:                return ">>";
+    case ASM:                   return "asm";
+    case AUTO:                  return "auto";
+    case BREAK:                 return "break";
+    case CASE:                  return "case";
+    case CATCH:                 return "catch";
+    case CHAR:                  return "char";
+    case CLASS:                 return "class";
+    case CONST:                 return "const";
+    case CONTINUE:              return "continue";
+    case DEFAULT:               return "default";
+    case DELETE:                return "delete";
+    case DO:                    return "do";
+    case DOUBLE:                return "double";
+    case ELSE:                  return "else";
+    case ENUM:                  return "enum";
+    case EXTERN:                return "extern";
+    case FLOAT:                 return "float";
+    case FOR:                   return "for";
+    case FRIEND:                return "friend";
+    case GOTO:                  return "goto";
+    case IF:                    return "if";
+    case T_INLINE:              return "inline";
+    case INT:                   return "int";
+    case LONG:                  return "long";
+    case NEW:                   return "new";
+    case OPERATOR:              return "operator";
+    case PRIVATE:               return "private";
+    case PROTECTED:             return "protected";
+    case PUBLIC:                return "public";
+    case REGISTER:              return "register";
+    case RETURN:                return "return";
+    case SHORT:                 return "short";
+    case SIGNED:                return "signed";
+    case SIZEOF:                return "sizeof";
+    case STATIC:                return "static";
+    case STRUCT:                return "struct";
+    case SWITCH:                return "switch";
+    case TEMPLATE:              return "template";
+    case THIS:                  return "this";
+    case THROW:                 return "throw";
+    case TRY:                   return "try";
+    case TYPEDEF:               return "typedef";
+    case UNION:                 return "union";
+    case UNSIGNED:              return "unsigned";
+    case VIRTUAL:               return "virtual";
+    case VOID:                  return "void";
+    case VOLATILE:              return "volatile";
+    case WHILE:                 return "while";
+    case YYEOF:                 return "EOF";
+    }
+
+  assert (t < 255);
+  b[0] = t;
+  b[1] = '\0';
+  return b;
+}
+
+
+/* Reinitialize the scanner for a new input file.  */
+
+void
+re_init_scanner ()
+{
+  in = inbuffer;
+  yyline = 1;
+  
+  if (yytext == NULL)
+    {
+      int size = 256;
+      yytext = (char *) xmalloc (size * sizeof *yytext);
+      yytext_end = yytext + size;
+    }
+}
+
+
+/* Insert a keyword NAME with token value TK into the keyword hash
+   table.  */
+
+void
+insert_keyword (name, tk)
+     char *name;
+     int tk;
+{
+  char *s;
+  unsigned h = 0;
+  struct kw *k = (struct kw *) xmalloc (sizeof *k);
+
+  for (s = name; *s; ++s)
+    h = (h << 1) ^ *s;
+
+  h %= KEYWORD_TABLE_SIZE;
+  k->name = name;
+  k->tk = tk;
+  k->next = keyword_table[h];
+  keyword_table[h] = k;
+}
+
+
+/* Initialize the scanner for the first file.  This sets up the
+   character class vectors and fills the keyword hash table.  */
+
+void
+init_scanner ()
+{
+  int i;
+
+  /* Allocate the input buffer */
+  inbuffer_size = READ_CHUNK_SIZE + 1;
+  inbuffer = in = (char *) xmalloc (inbuffer_size);
+  yyline = 1;
+
+  /* Set up character class vectors.  */
+  for (i = 0; i < sizeof is_ident; ++i)
+    {
+      if (i == '_' || isalnum (i))
+        is_ident[i] = 1;
+
+      if (i >= '0' && i <= '9')
+        is_digit[i] = 1;
+
+      if (i == ' ' || i == '\t' || i == '\f' || i == '\v')
+        is_white[i] = 1;
+    }
+
+  /* Fill keyword hash table.  */
+  insert_keyword ("and", LAND);
+  insert_keyword ("and_eq", ANDASGN);
+  insert_keyword ("asm", ASM);
+  insert_keyword ("auto", AUTO);
+  insert_keyword ("bitand", '&');
+  insert_keyword ("bitor", '|');
+  insert_keyword ("bool", BOOL);
+  insert_keyword ("break", BREAK);
+  insert_keyword ("case", CASE);
+  insert_keyword ("catch", CATCH);
+  insert_keyword ("char", CHAR);
+  insert_keyword ("class", CLASS);
+  insert_keyword ("compl", '~');
+  insert_keyword ("const", CONST);
+  insert_keyword ("const_cast", CONST_CAST);
+  insert_keyword ("continue", CONTINUE);
+  insert_keyword ("default", DEFAULT);
+  insert_keyword ("delete", DELETE);
+  insert_keyword ("do", DO);
+  insert_keyword ("double", DOUBLE);
+  insert_keyword ("dynamic_cast", DYNAMIC_CAST);
+  insert_keyword ("else", ELSE);
+  insert_keyword ("enum", ENUM);
+  insert_keyword ("explicit", EXPLICIT);
+  insert_keyword ("extern", EXTERN);
+  insert_keyword ("false", FALSE);
+  insert_keyword ("float", FLOAT);
+  insert_keyword ("for", FOR);
+  insert_keyword ("friend", FRIEND);
+  insert_keyword ("goto", GOTO);
+  insert_keyword ("if", IF);
+  insert_keyword ("inline", T_INLINE);
+  insert_keyword ("int", INT);
+  insert_keyword ("long", LONG);
+  insert_keyword ("mutable", MUTABLE);
+  insert_keyword ("namespace", NAMESPACE);
+  insert_keyword ("new", NEW);
+  insert_keyword ("not", '!');
+  insert_keyword ("not_eq", NE);
+  insert_keyword ("operator", OPERATOR);
+  insert_keyword ("or", LOR);
+  insert_keyword ("or_eq", ORASGN);
+  insert_keyword ("private", PRIVATE);
+  insert_keyword ("protected", PROTECTED);
+  insert_keyword ("public", PUBLIC);
+  insert_keyword ("register", REGISTER);
+  insert_keyword ("reinterpret_cast", REINTERPRET_CAST);
+  insert_keyword ("return", RETURN);
+  insert_keyword ("short", SHORT);
+  insert_keyword ("signed", SIGNED);
+  insert_keyword ("sizeof", SIZEOF);
+  insert_keyword ("static", STATIC);
+  insert_keyword ("static_cast", STATIC_CAST);
+  insert_keyword ("struct", STRUCT);
+  insert_keyword ("switch", SWITCH);
+  insert_keyword ("template", TEMPLATE);
+  insert_keyword ("this", THIS);
+  insert_keyword ("throw", THROW);
+  insert_keyword ("true", TRUE);
+  insert_keyword ("try", TRY);
+  insert_keyword ("typedef", TYPEDEF);
+  insert_keyword ("typeid", TYPEID);
+  insert_keyword ("typename", TYPENAME);
+  insert_keyword ("union", UNION);
+  insert_keyword ("unsigned", UNSIGNED);
+  insert_keyword ("using", USING);
+  insert_keyword ("virtual", VIRTUAL);
+  insert_keyword ("void", VOID);
+  insert_keyword ("volatile", VOLATILE);
+  insert_keyword ("wchar_t", WCHAR);
+  insert_keyword ("while", WHILE);
+  insert_keyword ("xor", '^');
+  insert_keyword ("xor_eq", XORASGN);
+}
+
+
+\f
+/***********************************************************************
+                               Parser
+ ***********************************************************************/
+
+/* Match the current lookahead token and set it to the next token.  */
+
+#define MATCH() (tk = yylex ())
+
+/* Return the lookahead token.  If current lookahead token is cleared,
+   read a new token.  */
+
+#define LA1 (tk == -1 ? (tk = yylex ()) : tk)
+
+/* Is the current lookahead equal to the token T? */
+
+#define LOOKING_AT(T) (tk == (T))
+
+/* Is the current lookahead one of T1 or T2?  */
+
+#define LOOKING_AT2(T1, T2)    (tk == (T1) || tk == (T2))
+
+/* Is the current lookahead one of T1, T2 or T3?  */
+
+#define LOOKING_AT3(T1, T2, T3)        (tk == (T1) || tk == (T2) || tk == (T3))
+
+/* Is the current lookahead one of T1...T4?  */
+
+#define LOOKING_AT4(T1, T2, T3, T4) \
+     (tk == (T1) || tk == (T2) || tk == (T3) || tk == (T4))
+
+/* Match token T if current lookahead is T.  */
+
+#define MATCH_IF(T) if (LOOKING_AT (T)) MATCH (); else ((void) 0)
+
+/* Skip to matching token if current token is T.  */
+
+#define SKIP_MATCHING_IF(T) \
+  if (LOOKING_AT (T)) skip_matching (); else ((void) 0)
+
+
+/* Skip forward until a given token TOKEN or YYEOF is seen and return
+   the current lookahead token after skipping.  */
+
+int
+skip_to (token)
+     int token;
+{
+  while (!LOOKING_AT2 (YYEOF, token))
+    MATCH ();
+  return tk;
+}
+
+
+/* Skip over pairs of tokens (parentheses, square brackets,
+   angle brackets, curly brackets) matching the current lookahead.  */
+
+void
+skip_matching ()
+{
+  int open, close, n;
+
+  switch (open = LA1)
+    {
+    case '{':
+      close = '}';
+      break;
+      
+    case '(':
+      close = ')';
+      break;
+      
+    case '<':
+      close = '>';
+      break;
+      
+    case '[':
+      close = ']';
+      break;
+      
+    default:
+      abort ();
+    }
+
+  for (n = 0;;)
+    {
+      if (LOOKING_AT (open))
+        ++n;
+      else if (LOOKING_AT (close))
+        --n;
+      else if (LOOKING_AT (YYEOF))
+        break;
+
+      MATCH ();
+
+      if (n == 0)
+        break;
+    }
+}
+
+
+/* Re-initialize the parser by resetting the lookahead token.  */
+
+void
+re_init_parser ()
+{
+  tk = -1;
+}
+
+
+/* Parse a parameter list, including the const-specifier,
+   pure-specifier, and throw-list that may follow a parameter list.
+   Return in FLAGS what was seen following the parameter list.
+   Returns a hash code for the parameter types.  This value is used to
+   distinguish between overloaded functions.  */
+
+unsigned
+parm_list (flags)
+     int *flags;
+{
+  unsigned hash = 0;
+  int type_seen = 0;
+
+  while (!LOOKING_AT2 (YYEOF, ')'))
+    {
+      switch (LA1)
+        {
+         /* Skip over grouping parens or parameter lists in parameter 
+            declarations.  */
+        case '(':
+          skip_matching ();
+          break;
+
+         /* Next parameter.  */
+        case ',':
+          MATCH ();
+          type_seen = 0;
+          break;
+
+          /* Ignore the scope part of types, if any.  This is because
+             some types need scopes when defined outside of a class body,
+             and don't need them inside the class body.  This means that
+             we have to look for the last IDENT in a sequence of
+             IDENT::IDENT::...  */
+        case IDENT:
+          if (!type_seen)
+            {
+             char *s;
+             unsigned ident_type_hash = 0;
+             
+             parse_qualified_param_ident_or_type (&s);
+              for (; *s; ++s)
+                ident_type_hash = (ident_type_hash << 1) ^ *s;
+             hash = (hash << 1) ^ ident_type_hash;
+             type_seen = 1;
+            }
+         else
+           MATCH ();
+          break;
+
+        case VOID:
+          /* This distinction is made to make `func (void)' equivalent
+             to `func ()'.  */
+          type_seen = 1;
+          MATCH ();
+          if (!LOOKING_AT (')'))
+            hash = (hash << 1) ^ VOID;
+          break;
+
+        case BOOL:      case CHAR:      case CLASS:     case CONST:
+        case DOUBLE:    case ENUM:      case FLOAT:     case INT:
+        case LONG:      case SHORT:     case SIGNED:    case STRUCT:
+        case UNION:     case UNSIGNED:  case VOLATILE:  case WCHAR:
+        case ELLIPSIS:  
+          type_seen = 1;
+          hash = (hash << 1) ^ LA1;
+          MATCH ();
+          break;
+
+        case '*':       case '&':       case '[':       case ']':
+          hash = (hash << 1) ^ LA1;
+          MATCH ();
+          break;
+
+        default:
+          MATCH ();
+          break;
+        }
+    }
+
+  if (LOOKING_AT (')'))
+    {
+      MATCH ();
+      
+      if (LOOKING_AT (CONST))
+        {
+          /* We can overload the same function on `const' */
+          hash = (hash << 1) ^ CONST;
+          SET_FLAG (*flags, F_CONST);
+          MATCH ();
+        }
+
+      if (LOOKING_AT (THROW))
+        {
+          MATCH ();
+          SKIP_MATCHING_IF ('(');
+          SET_FLAG (*flags, F_THROW);
+        }
+
+      if (LOOKING_AT ('='))
+        {
+          MATCH ();
+          if (LOOKING_AT (CINT) && yyival == 0)
+            {
+              MATCH ();
+              SET_FLAG (*flags, F_PURE);
+            }
+        }
+    }
+
+  return hash;
+}
+
+
+/* Print position info to stdout.  */
+
+void
+print_info ()
+{
+  if (info_position >= 0 && BUFFER_POS () <= info_position)
+    if (info_cls)
+      printf ("(\"%s\" \"%s\" \"%s\" %d)\n",
+             info_cls->name, sym_scope (info_cls),
+             info_member->name, info_where);
+}
+
+
+/* Parse a member declaration within the class body of CLS.  VIS is
+   the access specifier for the member (private, protected,
+   public).  */
+
+void
+member (cls, vis)
+     struct sym *cls;
+     int vis;
+{
+  char *id = NULL;
+  int sc = SC_MEMBER;
+  char *regexp = NULL;
+  int pos;
+  int is_constructor;
+  int anonymous = 0;
+  int flags = 0;
+  int class_tag;
+  int type_seen = 0;
+  int paren_seen = 0;
+  unsigned hash = 0;
+  int tilde = 0;
+
+  while (!LOOKING_AT4 (';', '{', '}', YYEOF))
+    {
+      switch (LA1)
+        {
+        default:
+          MATCH ();
+          break;
+
+          /* A function or class may follow.  */
+        case TEMPLATE:
+          MATCH();
+          SET_FLAG (flags, F_TEMPLATE);
+          /* Skip over template argument list */
+          SKIP_MATCHING_IF ('<');
+          break;
+
+        case EXPLICIT:
+          SET_FLAG (flags, F_EXPLICIT);
+          goto typeseen;
+          
+        case MUTABLE:
+          SET_FLAG (flags, F_MUTABLE);
+          goto typeseen;
+
+        case T_INLINE:
+          SET_FLAG (flags, F_INLINE);
+          goto typeseen;
+
+        case VIRTUAL:
+          SET_FLAG (flags, F_VIRTUAL);
+          goto typeseen;
+
+        case '[':
+          skip_matching ();
+          break;
+
+        case ENUM:
+          sc = SC_TYPE;
+          goto typeseen;
+
+        case TYPEDEF:
+          sc = SC_TYPE;
+          goto typeseen;
+
+        case FRIEND:
+          sc = SC_FRIEND;
+          goto typeseen;
+
+        case STATIC:
+          sc = SC_STATIC;
+          goto typeseen;
+
+        case '~':
+         tilde = 1;
+          MATCH ();
+          break;
+
+        case IDENT:
+          /* Remember IDENTS seen so far.  Among these will be the member
+             name.  */
+         id = (char *) alloca (strlen (yytext) + 2);
+         if (tilde)
+           {
+             *id = '~';
+             strcpy (id + 1, yytext);
+           }
+         else
+           strcpy (id, yytext);
+         MATCH ();
+         break;
+
+        case OPERATOR:
+          id = operator_name (&sc);
+          break;
+
+        case '(':
+          /* Most probably the beginning of a parameter list.  */
+          MATCH ();
+          paren_seen = 1;
+
+          if (id && cls)
+            {
+              if (!(is_constructor = streq (id, cls->name)))
+                regexp = matching_regexp ();
+            }
+          else
+            is_constructor = 0;
+
+          pos = BUFFER_POS ();
+          hash = parm_list (&flags);
+
+          if (is_constructor)
+            regexp = matching_regexp ();
+
+          if (id && cls != NULL)
+           add_member_decl (cls, id, regexp, pos, hash, 0, sc, vis, flags);
+
+          while (!LOOKING_AT3 (';', '{', YYEOF))
+            MATCH ();
+
+          if (LOOKING_AT ('{') && id && cls)
+           add_member_defn (cls, id, regexp, pos, hash, 0, sc, flags);
+         
+          id = NULL;
+          sc = SC_MEMBER;
+          break;
+
+        case STRUCT: case UNION: case CLASS:
+          /* Nested class */
+          class_tag = LA1;
+          type_seen = 1;
+          MATCH ();
+          anonymous = 1;
+
+          /* More than one ident here to allow for MS-DOS specialties
+             like `_export class' etc.  The last IDENT seen counts
+             as the class name.  */
+         while (!LOOKING_AT4 (YYEOF, ';', ':', '{'))
+           {
+             if (LOOKING_AT (IDENT))
+               anonymous = 0;
+             MATCH ();
+           }
+
+          if (LOOKING_AT2 (':', '{'))
+           class_definition (anonymous ? NULL : cls, class_tag, flags, 1);
+          else
+            skip_to (';');
+          break;
+
+        case INT:       case CHAR:      case LONG:      case UNSIGNED:
+        case SIGNED:    case CONST:     case DOUBLE:    case VOID:
+        case SHORT:     case VOLATILE:  case BOOL:      case WCHAR:
+        case TYPENAME:
+        typeseen:
+          type_seen = 1;
+          MATCH ();
+          break;
+        }
+    }
+
+  if (LOOKING_AT (';'))
+    {
+      /* The end of a member variable, a friend declaration or an access
+         declaration.  We don't want to add friend classes as members.  */
+      if (id && sc != SC_FRIEND && cls)
+        {
+          regexp = matching_regexp ();
+          pos = BUFFER_POS ();
+          
+          if (cls != NULL)
+            {
+              if (type_seen || !paren_seen)
+               add_member_decl (cls, id, regexp, pos, 0, 1, sc, vis, 0);
+              else
+               add_member_decl (cls, id, regexp, pos, hash, 0, sc, vis, 0);
+            }
+        }
+      
+      MATCH ();
+      print_info ();
+    }
+  else if (LOOKING_AT ('{'))
+    {
+      /* A named enum.  */
+      if (sc == SC_TYPE && id && cls)
+        {
+          regexp = matching_regexp ();
+          pos = BUFFER_POS ();
+
+          if (cls != NULL)
+            {
+              add_member_decl (cls, id, regexp, pos, 0, 1, sc, vis, 0);
+              add_member_defn (cls, id, regexp, pos, 0, 1, sc, 0);
+            }
+        }
+
+      skip_matching ();
+      print_info ();
+    }
+}
+
+
+/* Parse the body of class CLS.  TAG is the tag of the class (struct,
+   union, class).  */
+
+void
+class_body (cls, tag)
+     struct sym *cls;
+     int tag;
+{
+  int vis = tag == CLASS ? PRIVATE : PUBLIC;
+  int temp;
+
+  while (!LOOKING_AT2 (YYEOF, '}'))
+    {
+      switch (LA1)
+        {
+        case PRIVATE: case PROTECTED: case PUBLIC:
+          temp = LA1;
+          MATCH ();
+
+          if (LOOKING_AT (':'))
+            {
+              vis = temp;
+              MATCH ();
+            }
+          else
+            {
+              /* Probably conditional compilation for inheritance list.
+                 We don't known whether there comes more of this.
+                 This is only a crude fix that works most of the time.  */
+              do
+                {
+                  MATCH ();
+                }
+              while (LOOKING_AT2 (IDENT, ',')
+                     || LOOKING_AT3 (PUBLIC, PROTECTED, PRIVATE));
+            }
+          break;
+
+        case TYPENAME:
+        case USING:
+          skip_to (';');
+          break;
+
+          /* Try to synchronize */
+        case CHAR:      case CLASS:     case CONST:
+        case DOUBLE:    case ENUM:      case FLOAT:     case INT:
+        case LONG:      case SHORT:     case SIGNED:    case STRUCT:
+        case UNION:     case UNSIGNED:  case VOID:      case VOLATILE:
+        case TYPEDEF:   case STATIC:    case T_INLINE:  case FRIEND:
+        case VIRTUAL:   case TEMPLATE:  case IDENT:     case '~':
+        case BOOL:      case WCHAR:     case EXPLICIT:  case MUTABLE:
+          member (cls, vis);
+          break;
+
+        default:
+          MATCH ();
+          break;
+        }
+    }
+}
+
+
+/* Parse a qualified identifier.  Current lookahead is IDENT.  A
+   qualified ident has the form `X<..>::Y<...>::T<...>.  Returns a
+   symbol for that class.  */
+
+struct sym *
+parse_classname ()
+{
+  struct sym *last_class = NULL;
+  
+  while (LOOKING_AT (IDENT))
+    {
+      last_class = add_sym (yytext, last_class);
+      MATCH ();
+
+      if (LOOKING_AT ('<'))
+        {
+          skip_matching ();
+          SET_FLAG (last_class->flags, F_TEMPLATE);
+        }
+      
+      if (!LOOKING_AT (DCOLON))
+        break;
+      
+      MATCH ();
+    }
+
+  return last_class;
+}
+
+
+/* Parse an operator name.  Add the `static' flag to *SC if an
+   implicitly static operator has been parsed.  Value is a pointer to
+   a static buffer holding the constructed operator name string.  */
+
+char *
+operator_name (sc)
+     int *sc;
+{
+  static int id_size = 0;
+  static char *id = NULL;
+  char *s;
+  int len;
+  
+  MATCH ();
+
+  if (LOOKING_AT2 (NEW, DELETE))
+    {
+      /* `new' and `delete' are implicitly static.  */
+      if (*sc != SC_FRIEND)
+        *sc = SC_STATIC;
+
+      s = token_string (LA1);
+      MATCH ();
+      
+      len = strlen (s) + 10;
+      if (len > id_size)
+       {
+         int new_size = max (len, 2 * id_size);
+         id = (char *) xrealloc (id, new_size);
+         id_size = new_size;
+       }
+      strcpy (id, s);
+
+      /* Vector new or delete? */
+      if (LOOKING_AT ('['))
+       {
+         strcat (id, "[");
+         MATCH ();
+         
+         if (LOOKING_AT (']'))
+           {
+             strcat (id, "]");
+             MATCH ();
+           }
+       }
+    }
+  else
+    {
+      int tokens_matched = 0;
+
+      len = 20;
+      if (len > id_size)
+       {
+         int new_size = max (len, 2 * id_size);
+         id = (char *) xrealloc (id, new_size);
+         id_size = new_size;
+       }
+      strcpy (id, "operator");
+
+      /* Beware access declarations of the form "X::f;" Beware of
+        `operator () ()'.  Yet another difficulty is found in
+        GCC 2.95's STL: `operator == __STL_NULL_TMPL_ARGS (...'.  */
+      while (!(LOOKING_AT ('(') && tokens_matched)
+            && !LOOKING_AT2 (';', YYEOF))
+        {
+         s = token_string (LA1);
+         len += strlen (s) + 2;
+         if (len > id_size)
+           {
+             int new_size = max (len, 2 * id_size);
+             id = (char *) xrealloc (id, new_size);
+             id_size = new_size;
+           }
+
+         if (*s != ')' && *s != ']')
+           strcat (id, " ");
+          strcat (id, s);
+          MATCH ();
+
+         /* If this is a simple operator like `+', stop now.  */
+         if (!isalpha (*s) && *s != '(' && *s != '[')
+           break;
+
+         ++tokens_matched;
+        }
+    }
+
+  return id;
+}
+
+
+/* This one consumes the last IDENT of a qualified member name like
+   `X::Y::z'.  This IDENT is returned in LAST_ID.  Value if the
+   symbol structure for the ident.  */
+
+struct sym *
+parse_qualified_ident_or_type (last_id)
+     char **last_id;
+{
+  struct sym *cls = NULL;
+  static char *id = NULL;
+  static int id_size = 0;
+  
+  while (LOOKING_AT (IDENT))
+    {
+      int len = strlen (yytext) + 1;
+      if (len > id_size)
+       {
+         id = (char *) xrealloc (id, len);
+         id_size = len;
+       }
+      strcpy (id, yytext);
+      *last_id = id;
+      MATCH ();
+
+      SKIP_MATCHING_IF ('<');
+
+      if (LOOKING_AT (DCOLON))
+       {
+         cls = add_sym (id, cls);
+         *last_id = NULL;
+         MATCH ();
+       }
+      else
+       break;
+    }
+
+  return cls;
+}
+
+
+/* This one consumes the last IDENT of a qualified member name like
+   `X::Y::z'.  This IDENT is returned in LAST_ID.  Value if the
+   symbol structure for the ident.  */
+
+void
+parse_qualified_param_ident_or_type (last_id)
+     char **last_id;
+{
+  struct sym *cls = NULL;
+  static char *id = NULL;
+  static int id_size = 0;
+  
+  while (LOOKING_AT (IDENT))
+    {
+      int len = strlen (yytext) + 1;
+      if (len > id_size)
+       {
+         id = (char *) xrealloc (id, len);
+         id_size = len;
+       }
+      strcpy (id, yytext);
+      *last_id = id;
+      MATCH ();
+
+      SKIP_MATCHING_IF ('<');
+
+      if (LOOKING_AT (DCOLON))
+       {
+         cls = add_sym (id, cls);
+         *last_id = NULL;
+         MATCH ();
+       }
+      else
+       break;
+    }
+}
+
+
+/* Parse a class definition.
+
+   CONTAINING is the class containing the class being parsed or null.
+   This may also be null if NESTED != 0 if the containing class is
+   anonymous.  TAG is the tag of the class (struct, union, class).
+   NESTED is non-zero if we are parsing a nested class.
+
+   Current lookahead is the class name.  */
+
+void
+class_definition (containing, tag, flags, nested)
+     struct sym *containing;
+     int tag;
+     int flags;
+     int nested;
+{
+  register int token;
+  struct sym *current;
+  struct sym *base_class;
+
+  /* Set CURRENT to null if no entry has to be made for the class
+     parsed.  This is the case for certain command line flag
+     settings.  */
+  if ((tag != CLASS && !f_structs) || (nested && !f_nested_classes))
+    current = NULL;
+  else 
+    {
+      current = add_sym (yytext, containing);
+      current->pos = BUFFER_POS ();
+      current->regexp = matching_regexp ();
+      current->filename = filename;
+      current->flags = flags;
+    }
+
+  /* If at ':', base class list follows.  */
+  if (LOOKING_AT (':'))
+    {
+      int done = 0;
+      MATCH ();
+
+      while (!done)
+        {
+          switch (token = LA1)
+            {
+            case VIRTUAL: case PUBLIC: case PROTECTED: case PRIVATE: 
+              MATCH ();
+              break;
+
+            case IDENT:
+              base_class = parse_classname ();
+              if (base_class && current && base_class != current)
+                add_link (base_class, current);
+              break;
+
+              /* The `,' between base classes or the end of the base
+                 class list.  Add the previously found base class.
+                 It's done this way to skip over sequences of
+                 `A::B::C' until we reach the end.
+
+                 FIXME: it is now possible to handle `class X : public B::X'
+                 because we have enough information.  */
+            case ',':
+              MATCH ();
+              break;
+
+            default:
+              /* A syntax error, possibly due to preprocessor constructs
+                 like
+
+                 #ifdef SOMETHING
+                 class A : public B
+                 #else
+                 class A : private B.
+
+                 MATCH until we see something like `;' or `{'.  */
+              while (!LOOKING_AT3 (';', YYEOF, '{'))
+                MATCH ();
+             done = 1;
+
+            case '{':
+              done = 1;
+             break;
+            }
+        }
+    }
+
+  /* Parse the class body if there is one.  */
+  if (LOOKING_AT ('{'))
+    {
+      if (tag != CLASS && !f_structs)
+        skip_matching ();
+      else
+        {
+          MATCH ();
+          class_body (current, tag);
+
+          if (LOOKING_AT ('}'))
+            {
+              MATCH ();
+              if (LOOKING_AT (';') && !nested)
+                MATCH ();
+            }
+        }
+    }
+}
+
+
+/* Parse a declaration.  */
+
+void
+declaration (is_extern, flags)
+     int is_extern;
+     int flags;
+{
+  char *id = NULL;
+  struct sym *cls = NULL;
+  char *regexp = NULL;
+  int pos = 0;
+  unsigned hash = 0;
+  int is_constructor;
+  int sc = 0;
+
+  while (!LOOKING_AT3 (';', '{', YYEOF))
+    {
+      switch (LA1)
+        {
+        default:
+          MATCH ();
+          break;
+
+        case '[':
+          skip_matching ();
+          break;
+
+        case ENUM:
+        case TYPEDEF:
+          sc = SC_TYPE;
+          MATCH ();
+          break;
+          
+        case STATIC:
+          sc = SC_STATIC;
+          MATCH ();
+          break;
+
+        case INT:       case CHAR:      case LONG:      case UNSIGNED:
+        case SIGNED:    case CONST:     case DOUBLE:    case VOID:
+        case SHORT:     case VOLATILE:  case BOOL:      case WCHAR:
+          MATCH ();
+          break;
+
+        case CLASS: case STRUCT: case UNION:
+          /* This is for the case `STARTWRAP class X : ...' or
+             `declare (X, Y)\n class A : ...'.  */
+          if (id)
+            return;
+
+        case '=':
+          /* Assumed to be the start of an initialization in this context.
+             Skip over everything up to ';'.  */
+          skip_to (';');
+          break;
+
+        case OPERATOR:
+          id = operator_name (&sc);
+          break;
+
+        case T_INLINE:
+          SET_FLAG (flags, F_INLINE);
+          MATCH ();
+          break;
+
+        case '~':
+         MATCH ();
+         if (LOOKING_AT (IDENT))
+           {
+             id = (char *) alloca (strlen (yytext) + 2);
+             *id = '~';
+             strcpy (id + 1, yytext);
+             MATCH ();
+           }
+          break;
+
+        case IDENT:
+         cls = parse_qualified_ident_or_type (&id);
+          break;
+
+        case '(':
+          /* Most probably the beginning of a parameter list.  */
+          if (cls)
+            {
+              MATCH ();
+
+              if (id && cls)
+                {
+                  if (!(is_constructor = streq (id, cls->name)))
+                    regexp = matching_regexp ();
+                }
+              else
+                is_constructor = 0;
+
+              pos = BUFFER_POS ();
+              hash = parm_list (&flags);
+
+              if (is_constructor)
+                regexp = matching_regexp ();
+
+              if (id && cls)
+               add_member_defn (cls, id, regexp, pos, hash, 0,
+                                SC_UNKNOWN, flags);
+            }
+          else
+            {
+              /* This may be a C functions, but also a macro
+                 call of the form `declare (A, B)' --- such macros
+                 can be found in some class libraries.  */
+              MATCH ();
+
+              if (id)
+                {
+                  regexp = matching_regexp ();
+                  pos = BUFFER_POS ();
+                  hash = parm_list (&flags);
+                  add_global_decl (id, regexp, pos, hash, 0, sc, flags);
+                }
+
+              /* This is for the case that the function really is
+                 a macro with no `;' following it.  If a CLASS directly
+                 follows, we would miss it otherwise.  */
+              if (LOOKING_AT3 (CLASS, STRUCT, UNION))
+                return;
+            }
+
+          while (!LOOKING_AT3 (';', '{', YYEOF))
+            MATCH ();
+
+          if (!cls && id && LOOKING_AT ('{'))
+           add_global_defn (id, regexp, pos, hash, 0, sc, flags);
+          id = NULL;
+          break;
+        }
+    }
+
+  if (LOOKING_AT (';'))
+    {
+      /* The end of a member variable or of an access declaration
+         `X::f'.  To distinguish between them we have to know whether
+         type information has been seen.  */
+      if (id)
+        {
+          char *regexp = matching_regexp ();
+          int pos = BUFFER_POS ();
+
+          if (cls)
+           add_member_defn (cls, id, regexp, pos, 0, 1, SC_UNKNOWN, flags);
+          else
+            add_global_defn (id, regexp, pos, 0, 1, sc, flags);
+        }
+        
+      MATCH ();
+      print_info ();
+    }
+  else if (LOOKING_AT ('{'))
+    {
+      if (sc == SC_TYPE && id)
+        {
+          /* A named enumeration.  */
+          regexp = matching_regexp ();
+          pos = BUFFER_POS ();
+          add_global_defn (id, regexp, pos, 0, 1, sc, flags);
+        }
+
+      skip_matching ();
+      print_info ();
+    }
+}
+
+
+/* Parse a list of top-level declarations/definitions.  START_FLAGS
+   says in which context we are parsing.  If it is F_EXTERNC, we are
+   parsing in an `extern "C"' block.  Value is 1 if EOF is reached, 0
+   otherwise.  */
+
+int
+globals (start_flags)
+     int start_flags;
+{
+  int anonymous;
+  int class_tk;
+  int flags = start_flags;
+
+  for (;;)
+    {
+      char *prev_in = in;
+      
+      switch (LA1)
+        {
+        case NAMESPACE:
+          {
+            MATCH ();
+
+            if (LOOKING_AT (IDENT))
+              {
+                char *namespace_name
+                 = (char *) alloca (strlen (yytext) + 1);
+                strcpy (namespace_name, yytext);
+                MATCH ();
+                
+                if (LOOKING_AT ('='))
+                  {
+                    if (skip_to (';') == ';')
+                      MATCH ();
+                    register_namespace_alias (namespace_name, yytext);
+                  }
+                else if (LOOKING_AT ('{'))
+                  {
+                    MATCH ();
+                    enter_namespace (namespace_name);
+                    globals (0);
+                    leave_namespace ();
+                    MATCH_IF ('}');
+                  }
+              }
+          }
+          break;
+
+        case EXTERN:
+          MATCH ();
+          if (LOOKING_AT (CSTRING) && *string_start == 'C'
+              && *(string_start + 1) == '"')
+            {
+              /* This is `extern "C"'.  */
+              MATCH ();
+              
+              if (LOOKING_AT ('{'))
+                {
+                  MATCH ();
+                  globals (F_EXTERNC);
+                  MATCH_IF ('}');
+                }
+              else
+                SET_FLAG (flags, F_EXTERNC);
+            }
+          break;
+          
+        case TEMPLATE:
+          MATCH ();
+          SKIP_MATCHING_IF ('<');
+          SET_FLAG (flags, F_TEMPLATE);
+          break;
+
+        case CLASS: case STRUCT: case UNION:
+          class_tk = LA1;
+          MATCH ();
+          anonymous = 1;
+
+          /* More than one ident here to allow for MS-DOS and OS/2
+             specialties like `far', `_Export' etc.  Some C++ libs
+             have constructs like `_OS_DLLIMPORT(_OS_CLIENT)' in front
+             of the class name.  */
+         while (!LOOKING_AT4 (YYEOF, ';', ':', '{'))
+           {
+             if (LOOKING_AT (IDENT))
+               anonymous = 0;
+             MATCH ();
+           }
+
+          /* Don't add anonymous unions.  */
+          if (LOOKING_AT2 (':', '{') && !anonymous)
+              class_definition (NULL, class_tk, flags, 0);
+          else
+            {
+              if (skip_to (';') == ';')
+                MATCH ();
+            }
+
+          flags = start_flags;
+          break;
+
+        case YYEOF:
+          return 1;
+
+        case '}':
+          return 0;
+          
+        default:
+          declaration (0, flags);
+          flags = start_flags;
+          break;
+        }
+
+      if (prev_in == in)
+        yyerror ("parse error");
+    }
+}
+
+
+/* Parse the current input file.  */
+
+void
+yyparse ()
+{
+  while (globals (0) == 0)
+    MATCH_IF ('}');
+}
+
+
+\f
+/***********************************************************************
+                            Main Program
+ ***********************************************************************/
+
+/* Add the list of paths PATH_LIST to the current search path for
+   input files.  */
+
+void
+add_search_path (path_list)
+     char *path_list;
+{
+  while (*path_list)
+    {
+      char *start = path_list;
+      struct search_path *p;
+      
+      while (*path_list && *path_list != PATH_LIST_SEPARATOR)
+        ++path_list;
+      
+      p = (struct search_path *) xmalloc (sizeof *p);
+      p->path = (char *) xmalloc (path_list - start + 1);
+      memcpy (p->path, start, path_list - start);
+      p->path[path_list - start] = '\0';
+      p->next = NULL;
+
+      if (search_path_tail)
+        {
+          search_path_tail->next = p;
+          search_path_tail = p;
+        }
+      else
+        search_path = search_path_tail = p;
+
+      while (*path_list == PATH_LIST_SEPARATOR)
+        ++path_list;
+    }
+}
+
+
+/* Open FILE and return a file handle for it, or -1 if FILE cannot be
+   opened.  Try to find FILE in search_path first, then try the
+   unchanged file name.  */
+
+FILE *
+open_file (file)
+     char *file;
+{
+  FILE *fp = NULL;
+  static char *buffer;
+  static int buffer_size;
+  struct search_path *path;
+  
+  filename = xstrdup (file);
+
+  for (path = search_path; path && fp == NULL; path = path->next)
+    {
+      int len = strlen (path->path);
+
+      if (len + 1 >= buffer_size)
+       {
+         buffer_size = max (len + 1, 2 * buffer_size);
+         buffer = (char *) xrealloc (buffer, buffer_size);
+       }
+       
+      strcpy (buffer, path->path);
+      strcat (buffer, "/");
+      strcat (buffer, file);
+      fp = fopen (buffer, "r");
+    }
+  
+  /* Try the original file name.  */
+  if (fp == NULL)
+     fp = fopen (file, "r");
+
+  if (fp == NULL)
+    yyerror ("cannot open");
+  
+  return fp;
+}
+
+
+/* Display usage information and exit program.  */
+
+#define USAGE "\
+Usage: ebrowse [options] {files}\n\
+\n\
+  -a, --append                  append output\n\
+  -f, --files=FILES             read input file names from FILE\n\
+  -I, --search-path=LIST        set search path for input files\n\
+  -m, --min-regexp-length=N     set minimum regexp length to N\n\
+  -M, --max-regexp-length=N     set maximum regexp length to N\n\
+  -n, --no-nested-classes       exclude nested classes\n\
+  -o, --output-file=FILE        set output file name to FILE\n\
+  -p, --position-info           print info about position in file\n\
+  -s, --no-structs-or-unions    don't record structs or unions\n\
+  -v, --verbose                 be verbose\n\
+  -V, --very-verbose            be very verbose\n\
+  -x, --no-regexps             don't record regular expressions\n\
+      --help                    display this help\n\
+      --version                        display version info\n\
+"
+
+void
+usage (error)
+     int error;
+{
+  puts (USAGE);
+  exit (error ? 1 : 0);
+}
+
+
+/* Display version and copyright info.  The VERSION macro is set
+   from the Makefile and contains the Emacs version.  */
+
+void
+version ()
+{
+  printf ("ebrowse %s\n", VERSION);
+  puts ("Copyright (C) 1992-1999, 2000 Free Software Foundation, Inc.");
+  puts ("This program is distributed under the same terms as Emacs.");
+  exit (0);
+}
+
+
+/* Parse one input file FILE, adding classes and members to the symbol
+   table.  */
+
+void
+process_file (file)
+     char *file;
+{
+  FILE *fp;
+  
+  fp = open_file (file);
+  if (fp)
+    {      
+      int nread, nbytes;
+
+      /* Give a progress indication if needed.  */
+      if (f_very_verbose)
+        {
+          puts (filename);
+          fflush (stdout);
+        }
+      else if (f_verbose)
+        {
+          putchar ('.');
+          fflush (stdout);
+        }
+
+      /* Read file to inbuffer.  */
+      for (nread = 0;;)
+       {
+         if (nread + READ_CHUNK_SIZE >= inbuffer_size)
+           {
+             inbuffer_size = nread + READ_CHUNK_SIZE + 1;
+             inbuffer = (char *) xrealloc (inbuffer, inbuffer_size);
+           }
+         
+         nbytes = fread (inbuffer + nread, 1, READ_CHUNK_SIZE, fp);
+         nread += nbytes;
+         if (nbytes < READ_CHUNK_SIZE)
+           break;
+       }
+      inbuffer[nread] = '\0';
+
+      /* Reinitialize scanner and parser for the new input file.  */
+      re_init_scanner ();
+      re_init_parser ();
+
+      /* Parse it and close the file.  */
+      yyparse ();
+      fclose (fp);
+    }
+}
+
+
+/* Read a line from stream FP and return a pointer to a static buffer
+   containing its contents without the terminating newline.  Value
+   is null when EOF is reached.  */
+
+char *
+read_line (fp)
+     FILE *fp;
+{
+  static char *buffer;
+  static int buffer_size;
+  int i = 0, c;
+
+  while ((c = getc (fp)) != EOF && c != '\n')
+    {
+      if (i >= buffer_size)
+       {
+         buffer_size = max (100, buffer_size * 2);
+         buffer = (char *) xrealloc (buffer, buffer_size);
+       }
+
+      buffer[i++] = c;
+    }
+  
+  if (c == EOF && i == 0)
+    return NULL;
+  
+  if (i == buffer_size)
+    {
+      buffer_size = max (100, buffer_size * 2);
+      buffer = (char *) xrealloc (buffer, buffer_size);
+    }
+
+  buffer[i] = '\0';
+  return buffer;
+}
+
+
+/* Main entry point.  */
+
+int
+main (argc, argv)
+     int argc;
+     char **argv;
+{
+  int i;
+  int any_inputfiles = 0;
+  static char *out_filename = DEFAULT_OUTFILE;
+  static char **input_filenames = NULL;
+  static int input_filenames_size = 0;
+  static int n_input_files;
+
+  filename = "command line";
+  yyout = stdout;
+
+  while ((i = getopt_long (argc, argv, "af:I:m:M:no:p:svVx",
+                           options, NULL)) != EOF)
+    {
+      switch (i)
+        {
+         /* Experimental.  */
+       case 'p':
+         info_position = atoi (optarg);
+         break;
+         
+        case 'n':
+          f_nested_classes = 0;
+          break;
+
+        case 'x':
+          f_regexps = 0;
+          break;
+        
+          /* Add the name of a file containing more input files.  */
+        case 'f':
+         if (n_input_files == input_filenames_size)
+           {
+             input_filenames_size = max (10, 2 * input_filenames_size);
+             input_filenames = (char **) xrealloc (input_filenames,
+                                                   input_filenames_size);
+           }
+          input_filenames[n_input_files++] = xstrdup (optarg);
+          break;
+
+          /* Append new output to output file instead of truncating it.  */
+        case 'a':
+          f_append = 1;
+          break;
+
+          /* Include structs in the output */
+        case 's':
+          f_structs = 0;
+          break;
+
+          /* Be verbose (give a progress indication).  */
+        case 'v':
+          f_verbose = 1;
+          break;
+
+          /* Be very verbose (print file names as they are processed).  */
+        case 'V':
+          f_verbose = 1;
+          f_very_verbose = 1;
+          break;
+
+          /* Change the name of the output file.  */
+        case 'o':
+         out_filename = optarg;
+          break;
+
+          /* Set minimum length for regular expression strings
+             when recorded in the output file.  */
+        case 'm':
+          min_regexp = atoi (optarg);
+          break;
+
+          /* Set maximum length for regular expression strings
+             when recorded in the output file.  */
+        case 'M':
+          max_regexp = atoi (optarg);
+          break;
+
+          /* Add to search path.  */
+        case 'I':
+          add_search_path (optarg);
+          break;
+
+          /* Display help */
+        case -2:
+          usage (0);
+          break;
+
+       case -3:
+         version ();
+         break;
+        }
+    }
+
+  /* Call init_scanner after command line flags have been processed to be
+     able to add keywords depending on command line (not yet
+     implemented).  */
+  init_scanner ();
+  init_sym ();
+
+  /* Open output file */
+  if (*out_filename)
+    {
+      yyout = fopen (out_filename, f_append ? "a" : "w");
+      if (yyout == NULL)
+       {
+         yyerror ("cannot open output file `%s'", out_filename);
+         exit (1);
+       }
+    }
+
+  /* Process input files specified on the command line.  */
+  while (optind < argc)
+    {
+      process_file (argv[optind++]);
+      any_inputfiles = 1;
+    }
+
+  /* Process files given on stdin if no files specified.  */
+  if (!any_inputfiles && n_input_files == 0)
+    {
+      char *file;
+      while ((file = read_line (stdin)) != NULL)
+       process_file (file);
+    }
+  else
+    {
+      /* Process files from `--files=FILE'.  Every line in FILE names
+        one input file to process.  */
+      for (i = 0; i < n_input_files; ++i)
+        {
+          FILE *fp = fopen (input_filenames[i], "r");
+          
+          if (fp == NULL)
+            yyerror ("cannot open input file `%s'", input_filenames[i]);
+          else
+            {
+             char *file;
+             while ((file = read_line (fp)) != NULL)
+               process_file (file);
+              fclose (fp);
+            }
+        }
+    }
+
+  /* Write output file.  */
+  dump_roots (yyout);
+
+  /* Close output file.  */
+  if (yyout != stdout)
+    fclose (yyout);
+
+  return 0;
+}
+
+
+/* ebrowse.c ends here.  */
index a2fd5c2ead2b8fcf002be1af16cac6e8b6232b5d..36aa9eefa6f2e357fa1305a2a30ebb1de05d7a32 100644 (file)
@@ -1,3 +1,20 @@
+2000-04-09  Gerd Moellmann  <gerd@gnu.org>
+
+       * mail/rfc2368.el: Correct author's email address.
+
+       * progmodes/ebrowse.el: New file.
+
+       * emacs-lisp/easymenu.el (easy-menu-create-menu): Process menu
+       item help string.
+       (easy-menu-do-add-item): Ditto.
+       (easy-menu-define): Extend doc string.
+
+       * jit-lock.el (with-buffer-unmodified): Use
+       restore-buffer-modified-p.
+       (with-buffer-prepared-for-font-lock): Use with-buffer-unmodified.
+       (jit-lock-function, jit-lock-stealth-fontify): Don't use
+       with-buffer-unmodified.
+
 2000-04-08  Dave Love  <fx@gnu.org>
 
        * emacs-lisp/edebug.el: Fix specs for dolist, dotimes, push, pop,
index cf0c724f6e6ef242b634760ea43f97c8d2985324..df9b657c8a22337c7ecf3b5e7e282538c8a3d1c3 100644 (file)
@@ -38,6 +38,7 @@
 (put 'nnmail-procmail 'custom-loads '("nnmail"))
 (put 'desktop 'custom-loads '("desktop"))
 (put 'cperl-help-system 'custom-loads '("cperl-mode"))
+(put 'ps-print-miscellany 'custom-loads '("ps-print"))
 (put 'comint-completion 'custom-loads '("comint"))
 (put 'gnus-score-kill 'custom-loads '("gnus-kill" "gnus"))
 (put 'ldap 'custom-loads '("ldap"))
@@ -84,6 +85,7 @@
 (put 'gnus-various 'custom-loads '("gnus-sum" "gnus"))
 (put 'elide-head 'custom-loads '("elide-head"))
 (put 'vhdl-compile 'custom-loads '("vhdl-mode"))
+(put 'ebrowse-tree 'custom-loads '("ebrowse"))
 (put 'font-lock-highlighting-faces 'custom-loads '("font-lock" "vhdl-mode"))
 (put 'flyspell 'custom-loads '("flyspell"))
 (put 'ange-ftp 'custom-loads '("ange-ftp"))
 (put 'ebnf-optimization 'custom-loads '("ebnf2ps"))
 (put 'apropos 'custom-loads '("apropos"))
 (put 'gomoku 'custom-loads '("gomoku"))
-(put 'tools 'custom-loads '("add-log" "calculator" "compare-w" "diff-mode" "diff" "ediff" "elide-head" "emerge" "gud" "pcvs-defs" "smerge-mode" "speedbar" "tempo" "tooltip" "vc" "which-func" "copyright" "compile" "etags" "glasses" "make-mode" "rcompile"))
+(put 'tools 'custom-loads '("add-log" "calculator" "compare-w" "diff-mode" "diff" "ediff" "elide-head" "emerge" "gud" "pcvs-defs" "smerge-mode" "speedbar" "tempo" "tooltip" "vc" "which-func" "copyright" "compile" "ebrowse" "etags" "glasses" "make-mode" "rcompile"))
 (put 'gnus-topic 'custom-loads '("gnus-topic"))
 (put 'sgml 'custom-loads '("sgml-mode"))
 (put 'keyboard 'custom-loads '("simple" "chistory" "type-break"))
 (put 'rmail-summary 'custom-loads '("rmail" "rmailsum"))
 (put 'metamail 'custom-loads '("metamail"))
 (put 'winner 'custom-loads '("winner"))
+(put 'ebrowse-faces 'custom-loads '("ebrowse"))
 (put 'wp 'custom-loads '("cus-edit" "enriched" "lpr" "ps-print" "view" "ebnf2ps" "bib-mode" "nroff-mode" "refbib" "refer" "scribe" "tildify"))
 (put 'reftex-citation-support 'custom-loads '("reftex-vars"))
 (put 'gnus-summary-choose 'custom-loads '("gnus-sum"))
 (put 'ebnf-repeat 'custom-loads '("ebnf2ps"))
 (put 'supercite 'custom-loads '("supercite"))
 (put 'font-selection 'custom-loads '("faces"))
+(put 'ps-print-headers 'custom-loads '("ps-print"))
 (put 'gnus-summary-marks 'custom-loads '("gnus-sum" "gnus"))
 (put 'bibtex-autokey 'custom-loads '("bibtex"))
 (put 'eudc 'custom-loads '("eudc-vars"))
 (put 'fill-comments 'custom-loads '("simple"))
 (put 'gnus-summary-various 'custom-loads '("gnus-sum"))
 (put 'applications 'custom-loads '("calendar" "cus-edit" "uniquify" "spell"))
+(put 'ebrowse-member 'custom-loads '("ebrowse"))
 (put 'terminal 'custom-loads '("terminal"))
 (put 'shadow 'custom-loads '("shadowfile" "shadow"))
 (put 'hl-line 'custom-loads '("hl-line"))
 (put 'hanoi 'custom-loads '("hanoi"))
 (put 'reftex-index-support 'custom-loads '("reftex-vars"))
 (put 'pascal 'custom-loads '("pascal"))
-(put 'rmail-retrieve 'custom-loads '("rmail"))
+(put 'rmail-retrieve 'custom-loads '("rmail" "rmailsum"))
 (put 'data 'custom-loads '("text-mode" "arc-mode" "forms" "hexl" "jka-compr" "saveplace" "sort" "tar-mode" "time-stamp" "snmp-mode"))
 (put 'mail 'custom-loads '("simple" "startup" "time" "gnus" "message" "emacsbug" "feedmail" "mail-extr" "mail-hist" "mail-utils" "mailalias" "metamail" "mh-e" "mspools" "rmail" "sendmail" "smtpmail" "supercite" "uce" "fortune" "eudc-vars"))
 (put 'paren-blinking 'custom-loads '("simple"))
 (put 'dired 'custom-loads '("files" "dired-aux" "dired-x" "dired" "find-dired"))
 (put 'recentf 'custom-loads '("recentf"))
 (put 'fill 'custom-loads '("simple" "fill" "align"))
-(put 'ps-print-header 'custom-loads '("ps-print"))
 (put 'outlines 'custom-loads '("allout" "outline"))
 (put 'paragraphs 'custom-loads '("paragraphs"))
+(put 'ebrowse 'custom-loads '("ebrowse"))
 (put 'nnmail-split 'custom-loads '("nnmail"))
 (put 'makefile 'custom-loads '("make-mode"))
 (put 'supercite-attr 'custom-loads '("supercite"))
 (put 'ps-print-zebra 'custom-loads '("ps-print"))
 (put 'hideshow 'custom-loads '("hideshow"))
 (put 'viper-search 'custom-loads '("viper-init"))
-(put 'C 'custom-loads '("cpp"))
 (put 'mule 'custom-loads '("mule-cmds"))
 (put 'glasses 'custom-loads '("glasses"))
 (put 'vhdl-style 'custom-loads '("vhdl-mode"))
 (put 'tempo 'custom-loads '("tempo"))
-(put 'c 'custom-loads '("tooltip" "cc-vars" "cmacexp" "hideif"))
+(put 'c 'custom-loads '("tooltip" "cc-vars" "cmacexp" "cpp" "hideif"))
 (put 'nnmail-prepare 'custom-loads '("nnmail"))
 (put 'processes 'custom-loads '("comint" "cus-edit" "shell" "term" "metamail" "compile" "executable" "sql" "flyspell" "rcompile" "rlogin"))
 (put 'ebnf2ps 'custom-loads '("ebnf2ps"))
 (custom-put-if-not 'border 'group-documentation nil)
 (custom-put-if-not 'hl-line 'custom-version "21.1")
 (custom-put-if-not 'hl-line 'group-documentation "Highliight the current line.")
+(custom-put-if-not 'find-file-wildcards 'custom-version "20.4")
+(custom-put-if-not 'find-file-wildcards 'standard-value t)
 (custom-put-if-not 'custom-comment-face 'custom-version "21.1")
 (custom-put-if-not 'custom-comment-face 'group-documentation nil)
 (custom-put-if-not 'custom-raised-buttons 'custom-version "21.1")
 (custom-put-if-not 'hscroll-global-mode 'standard-value t)
 (custom-put-if-not 'tags-apropos-verbose 'custom-version "21.1")
 (custom-put-if-not 'tags-apropos-verbose 'standard-value t)
+(custom-put-if-not 'dabbrev-ignored-regexps 'custom-version "21.1")
+(custom-put-if-not 'dabbrev-ignored-regexps 'standard-value t)
 (custom-put-if-not 'find-variable-regexp 'custom-version "20.3")
 (custom-put-if-not 'find-variable-regexp 'standard-value t)
 (custom-put-if-not 'header-line 'custom-version "21.1")
 (custom-put-if-not 'eval-expression-print-level 'custom-version "21.1")
 (custom-put-if-not 'eval-expression-print-level 'standard-value t)
 
-(defvar custom-versions-load-alist '(("20.3.3" "dos-vars") (21.1 "ange-ftp") ("20.4" "sh-script" "help" "compile") ("21.1" "debug" "paths" "sgml-mode" "fortran" "etags" "cus-edit" "add-log" "find-func" "simple") ("20.3" "desktop" "easymenu" "hscroll" "dabbrev" "ffap" "rmail" "paren" "mailabbrev" "frame" "uce" "mouse" "diary-lib" "sendmail" "debug" "hexl" "vcursor" "vc" "compile" "etags" "help" "browse-url" "add-log" "find-func" "vc-hooks" "cus-edit" "replace"))
+(defvar custom-versions-load-alist '(("20.3.3" "dos-vars") (21.1 "ange-ftp") ("20.4" "files" "sh-script" "help" "compile") ("21.1" "debug" "dabbrev" "paths" "sgml-mode" "fortran" "etags" "cus-edit" "add-log" "find-func" "simple") ("20.3" "desktop" "easymenu" "hscroll" "dabbrev" "ffap" "rmail" "paren" "mailabbrev" "frame" "uce" "mouse" "diary-lib" "sendmail" "debug" "hexl" "vcursor" "vc" "compile" "etags" "help" "browse-url" "add-log" "find-func" "vc-hooks" "cus-edit" "replace"))
  "For internal use by custom.")
 
 (provide 'cus-load)
index 20127ec416c73accdc35933e161bc4d429370e16..a78d0906866c77dfa378bdfd06aa81b7c3f9cd7b 100644 (file)
@@ -119,7 +119,7 @@ Insert a descriptive header at the top of the file." t nil)
 ;;;### (autoloads (change-log-merge add-log-current-defun change-log-mode
 ;;;;;;  add-change-log-entry-other-window add-change-log-entry find-change-log
 ;;;;;;  prompt-for-change-log-name add-log-mailing-address add-log-full-name)
-;;;;;;  "add-log" "add-log.el" (14525 5303))
+;;;;;;  "add-log" "add-log.el" (14565 55609))
 ;;; Generated autoloads from add-log.el
 
 (defvar add-log-full-name nil "\
@@ -192,11 +192,11 @@ Runs `change-log-mode-hook'." t nil)
 Return name of function definition point is in, or nil.
 
 Understands C, Lisp, LaTeX (\"functions\" are chapters, sections, ...),
-Texinfo (@node titles), Perl, and Fortran.
+Texinfo (@node titles) and Perl.
 
 Other modes are handled by a heuristic that looks in the 10K before
 point for uppercase headings starting in the first column or
-identifiers followed by `:' or `=', see variables
+identifiers followed by `:' or `='.  See variables
 `add-log-current-defun-header-regexp' and
 `add-log-current-defun-function'
 
@@ -417,7 +417,7 @@ Used in `antlr-mode'.  Also a useful function in `java-mode-hook'." nil nil)
 ;;;### (autoloads (appt-make-list appt-delete appt-add appt-display-diary
 ;;;;;;  appt-display-duration appt-msg-window appt-display-mode-line
 ;;;;;;  appt-visible appt-audible appt-message-warning-time appt-issue-message)
-;;;;;;  "appt" "calendar/appt.el" (14517 9487))
+;;;;;;  "appt" "calendar/appt.el" (14563 8413))
 ;;; Generated autoloads from calendar/appt.el
 
 (defvar appt-issue-message t "\
@@ -448,13 +448,23 @@ as the first thing on a line.")
 This will occur at midnight when the appointment list is updated.")
 
 (autoload (quote appt-add) "appt" "\
-Add an appointment for the day at TIME and issue MESSAGE.
+Add an appointment for the day at NEW-APPT-TIME and issue message NEW-APPT-MSG.
 The time should be in either 24 hour format or am/pm format." t nil)
 
 (autoload (quote appt-delete) "appt" "\
 Delete an appointment from the list of appointments." t nil)
 
-(autoload (quote appt-make-list) "appt" nil nil nil)
+(autoload (quote appt-make-list) "appt" "\
+Create the appointments list from todays diary buffer.
+The time must be at the beginning of a line for it to be
+put in the appointments list.
+  02/23/89
+    12:00pm lunch
+   Wednesday
+     10:00am group meeting
+We assume that the variables DATE and NUMBER
+hold the arguments that `list-diary-entries' received.
+They specify the range of dates that the diary is being processed for." nil nil)
 
 ;;;***
 \f
@@ -665,7 +675,7 @@ insert a template for the file depending on the mode of the buffer." t nil)
 \f
 ;;;### (autoloads (batch-update-autoloads update-autoloads-from-directories
 ;;;;;;  update-file-autoloads) "autoload" "emacs-lisp/autoload.el"
-;;;;;;  (14398 37513))
+;;;;;;  (14563 8438))
 ;;; Generated autoloads from emacs-lisp/autoload.el
 
 (autoload (quote update-file-autoloads) "autoload" "\
@@ -1310,7 +1320,7 @@ corresponding bookmark function from Lisp (the one without the
 ;;;;;;  browse-url-of-buffer browse-url-of-file browse-url-generic-program
 ;;;;;;  browse-url-save-file browse-url-netscape-display browse-url-new-window-p
 ;;;;;;  browse-url-browser-function) "browse-url" "net/browse-url.el"
-;;;;;;  (14554 2050))
+;;;;;;  (14558 23455))
 ;;; Generated autoloads from net/browse-url.el
 
 (defvar browse-url-browser-function (if (eq system-type (quote windows-nt)) (quote browse-url-default-windows-browser) (quote browse-url-netscape)) "\
@@ -1546,7 +1556,7 @@ name of buffer configuration." t nil)
 ;;;### (autoloads (batch-byte-recompile-directory batch-byte-compile
 ;;;;;;  display-call-tree byte-compile compile-defun byte-compile-file
 ;;;;;;  byte-recompile-directory byte-force-recompile) "bytecomp"
-;;;;;;  "emacs-lisp/bytecomp.el" (14547 29523))
+;;;;;;  "emacs-lisp/bytecomp.el" (14564 35790))
 ;;; Generated autoloads from emacs-lisp/bytecomp.el
 
 (autoload (quote byte-force-recompile) "bytecomp" "\
@@ -2718,7 +2728,7 @@ If `compare-ignore-case' is non-nil, changes in case are also ignored." t nil)
 ;;;### (autoloads (next-error compilation-minor-mode compilation-shell-minor-mode
 ;;;;;;  compilation-mode grep-find grep compile compilation-search-path
 ;;;;;;  compilation-ask-about-save compilation-window-height compilation-mode-hook)
-;;;;;;  "compile" "progmodes/compile.el" (14440 46010))
+;;;;;;  "compile" "progmodes/compile.el" (14569 2479))
 ;;; Generated autoloads from progmodes/compile.el
 
 (defvar compilation-mode-hook nil "\
@@ -3289,7 +3299,7 @@ or as help on variables `cperl-tips', `cperl-problems',
 ;;;***
 \f
 ;;;### (autoloads (cpp-parse-edit cpp-highlight-buffer) "cpp" "progmodes/cpp.el"
-;;;;;;  (13826 9529))
+;;;;;;  (14568 36509))
 ;;; Generated autoloads from progmodes/cpp.el
 
 (autoload (quote cpp-highlight-buffer) "cpp" "\
@@ -3333,7 +3343,7 @@ With ARG, turn CRiSP mode on if ARG is positive, off otherwise." t nil)
 ;;;;;;  customize-option-other-window customize-changed-options customize-option
 ;;;;;;  customize-group-other-window customize-group customize customize-save-variable
 ;;;;;;  customize-set-variable customize-set-value) "cus-edit" "cus-edit.el"
-;;;;;;  (14552 48684))
+;;;;;;  (14558 7062))
 ;;; Generated autoloads from cus-edit.el
  (add-hook 'same-window-regexps "\\`\\*Customiz.*\\*\\'")
 
@@ -3582,7 +3592,7 @@ If the argument is nil, we return the display table to its standard state." t ni
 ;;;***
 \f
 ;;;### (autoloads (dabbrev-expand dabbrev-completion) "dabbrev" "dabbrev.el"
-;;;;;;  (14385 24830))
+;;;;;;  (14568 46430))
 ;;; Generated autoloads from dabbrev.el
 
 (define-key esc-map "/" (quote dabbrev-expand))
@@ -4066,7 +4076,7 @@ Minor mode for viewing/editing context diffs.
 ;;;;;;  dired dired-copy-preserve-time dired-dwim-target dired-keep-marker-symlink
 ;;;;;;  dired-keep-marker-hardlink dired-keep-marker-copy dired-keep-marker-rename
 ;;;;;;  dired-trivial-filenames dired-ls-F-marks-symlinks dired-listing-switches)
-;;;;;;  "dired" "dired.el" (14522 27392))
+;;;;;;  "dired" "dired.el" (14563 8348))
 ;;; Generated autoloads from dired.el
 
 (defvar dired-listing-switches "-al" "\
@@ -4625,8 +4635,8 @@ been generated automatically, with a reference to the keymap." nil (quote macro)
 ;;;***
 \f
 ;;;### (autoloads (easy-menu-change easy-menu-create-menu easy-menu-do-define
-;;;;;;  easy-menu-define) "easymenu" "emacs-lisp/easymenu.el" (14385
-;;;;;;  24854))
+;;;;;;  easy-menu-define) "easymenu" "emacs-lisp/easymenu.el" (14574
+;;;;;;  18612))
 ;;; Generated autoloads from emacs-lisp/easymenu.el
 
 (autoload (quote easy-menu-define) "easymenu" "\
@@ -4717,6 +4727,10 @@ anything else means an ordinary menu item.
 SELECTED is an expression; the checkbox or radio button is selected
 whenever this expression's value is non-nil.
 
+   :help HELP
+
+HELP is a string, the help to display for the menu item.
+
 A menu item can be a string.  Then that string appears in the menu as
 unselectable text.  A string consisting solely of hyphens is displayed
 as a solid horizontal line.
@@ -4858,6 +4872,43 @@ Pop a style and set it to current style.
 
 It returns the old style symbol." t nil)
 
+;;;***
+\f
+;;;### (autoloads (ebrowse-save-tree-as ebrowse-tags-query-replace
+;;;;;;  ebrowse-tags-loop-continue ebrowse-tags-complete-symbol ebrowse-electric-choose-tree
+;;;;;;  ebrowse-tree-mode) "ebrowse" "progmodes/ebrowse.el" (14575
+;;;;;;  54558))
+;;; Generated autoloads from progmodes/ebrowse.el
+
+(autoload (quote ebrowse-tree-mode) "ebrowse" "\
+Major mode for Ebrowse class tree buffers.
+Each line corresponds to a class in a class tree.
+Letters do not insert themselves, they are commands.
+File operations in the tree buffer work on class tree data structures.
+E.g.\\[save-buffer] writes the tree to the file it was loaded from.
+
+Tree mode key bindings:
+\\{ebrowse-tree-mode-map}" nil nil)
+
+(autoload (quote ebrowse-electric-choose-tree) "ebrowse" "\
+Return a buffer containing a tree or nil if no tree found or canceled." t nil)
+
+(autoload (quote ebrowse-tags-complete-symbol) "ebrowse" "Perform completion on the C++ symbol preceding point.\nA second call of this function without changing point inserts the next match. \nA call with prefix PREFIX reads the symbol to insert from the minibuffer with\ncompletion." t nil)
+
+(autoload (quote ebrowse-tags-loop-continue) "ebrowse" "\
+Repeat last operation on files in tree.
+FIRST-TIME non-nil means this is not a repetition, but the first time.
+TREE-BUFFER if indirectly specifies which files to loop over." t nil)
+
+(autoload (quote ebrowse-tags-query-replace) "ebrowse" "\
+Query replace FROM with TO in all files of a class tree.
+With prefix arg, process files of marked classes only." t nil)
+
+(autoload (quote ebrowse-save-tree-as) "ebrowse" "\
+Write the current tree data structure to a file.
+Read the file name from the minibuffer if interactive.
+Otherwise, FILE-NAME specifies the file to save the tree in." t nil)
+
 ;;;***
 \f
 ;;;### (autoloads (electric-buffer-list) "ebuff-menu" "ebuff-menu.el"
@@ -4894,7 +4945,7 @@ With prefix arg NOCONFIRM, execute current line as-is without editing." t nil)
 ;;;***
 \f
 ;;;### (autoloads (edebug-eval-top-level-form def-edebug-spec edebug-all-forms
-;;;;;;  edebug-all-defs) "edebug" "emacs-lisp/edebug.el" (14482 54435))
+;;;;;;  edebug-all-defs) "edebug" "emacs-lisp/edebug.el" (14576 25687))
 ;;; Generated autoloads from emacs-lisp/edebug.el
 
 (defvar edebug-all-defs nil "\
@@ -6957,7 +7008,7 @@ Some generic modes are defined in `generic-x.el'." t nil)
 ;;;***
 \f
 ;;;### (autoloads (glasses-mode) "glasses" "progmodes/glasses.el"
-;;;;;;  (14480 59906))
+;;;;;;  (14568 44804))
 ;;; Generated autoloads from progmodes/glasses.el
 
 (autoload (quote glasses-mode) "glasses" "\
@@ -8350,9 +8401,9 @@ and a negative argument disables it." t nil)
 ;;;***
 \f
 ;;;### (autoloads (iso-cvt-define-menu iso-cvt-write-only iso-cvt-read-only
-;;;;;;  iso-iso2duden iso-iso2gtex iso-gtex2iso iso-tex2iso iso-iso2tex
-;;;;;;  iso-german iso-spanish) "iso-cvt" "international/iso-cvt.el"
-;;;;;;  (13768 42838))
+;;;;;;  iso-sgml2iso iso-iso2sgml iso-iso2duden iso-iso2gtex iso-gtex2iso
+;;;;;;  iso-tex2iso iso-iso2tex iso-german iso-spanish) "iso-cvt"
+;;;;;;  "international/iso-cvt.el" (14564 29908))
 ;;; Generated autoloads from international/iso-cvt.el
 
 (autoload (quote iso-spanish) "iso-cvt" "\
@@ -8397,6 +8448,18 @@ The region between FROM and TO is translated using the table TRANS-TAB.
 Optional arg BUFFER is ignored (so that the function can can be used in
 `format-alist')." t nil)
 
+(autoload (quote iso-iso2sgml) "iso-cvt" "\
+Translate ISO 8859-1 characters in the region to SGML entities.
+The entities used are from \"ISO 8879:1986//ENTITIES Added Latin 1//EN\".
+Optional arg BUFFER is ignored (so that the function can can be used in
+`format-alist')." t nil)
+
+(autoload (quote iso-sgml2iso) "iso-cvt" "\
+Translate SGML entities in the region to ISO 8859-1 characters.
+The entities used are from \"ISO 8879:1986//ENTITIES Added Latin 1//EN\".
+Optional arg BUFFER is ignored (so that the function can can be used in
+`format-alist')." t nil)
+
 (autoload (quote iso-cvt-read-only) "iso-cvt" "\
 Warn that format is read-only." t nil)
 
@@ -8765,7 +8828,7 @@ If non-nil, second arg INITIAL-INPUT is a string to insert before reading." nil
 ;;;***
 \f
 ;;;### (autoloads (turn-on-jit-lock jit-lock-mode) "jit-lock" "jit-lock.el"
-;;;;;;  (14550 5866))
+;;;;;;  (14571 7073))
 ;;; Generated autoloads from jit-lock.el
 
 (autoload (quote jit-lock-mode) "jit-lock" "\
@@ -8806,7 +8869,7 @@ Unconditionally turn on Just-in-time Lock mode." nil nil)
 ;;;***
 \f
 ;;;### (autoloads (auto-compression-mode) "jka-compr" "jka-compr.el"
-;;;;;;  (14495 17985))
+;;;;;;  (14568 39747))
 ;;; Generated autoloads from jka-compr.el
 
 (defvar auto-compression-mode nil "\
@@ -9057,7 +9120,7 @@ is nil, raise an error." t nil)
 ;;;***
 \f
 ;;;### (autoloads (locate-with-filter locate) "locate" "locate.el"
-;;;;;;  (14396 4034))
+;;;;;;  (14563 8348))
 ;;; Generated autoloads from locate.el
 
 (autoload (quote locate) "locate" "\
@@ -9072,7 +9135,7 @@ shown; this is often useful to constrain a big search." t nil)
 
 ;;;***
 \f
-;;;### (autoloads (log-edit) "log-edit" "log-edit.el" (14537 49316))
+;;;### (autoloads (log-edit) "log-edit" "log-edit.el" (14559 17354))
 ;;; Generated autoloads from log-edit.el
 
 (autoload (quote log-edit) "log-edit" "\
@@ -9096,8 +9159,8 @@ Major mode for browsing CVS log output." t nil)
 ;;;***
 \f
 ;;;### (autoloads (print-region lpr-region print-buffer lpr-buffer
-;;;;;;  lpr-command lpr-switches printer-name) "lpr" "lpr.el" (14440
-;;;;;;  46009))
+;;;;;;  lpr-command lpr-switches printer-name) "lpr" "lpr.el" (14563
+;;;;;;  22518))
 ;;; Generated autoloads from lpr.el
 
 (defvar printer-name (if (memq system-type (quote (ms-dos windows-nt))) "PRN") "\
@@ -9428,7 +9491,7 @@ current header, calls `mail-complete-function' and passes prefix arg if any." t
 ;;;***
 \f
 ;;;### (autoloads (makefile-mode) "make-mode" "progmodes/make-mode.el"
-;;;;;;  (14554 2005))
+;;;;;;  (14570 19448))
 ;;; Generated autoloads from progmodes/make-mode.el
 
 (autoload (quote makefile-mode) "make-mode" "\
@@ -9517,7 +9580,7 @@ Previous contents of that buffer are killed first." t nil)
 
 ;;;***
 \f
-;;;### (autoloads (man-follow man) "man" "man.el" (14539 53667))
+;;;### (autoloads (man-follow man) "man" "man.el" (14570 21850))
 ;;; Generated autoloads from man.el
 
 (defalias (quote manual-entry) (quote man))
@@ -9955,7 +10018,7 @@ Multiplication puzzle with GNU Emacs." t nil)
 
 ;;;***
 \f
-;;;### (autoloads (msb-mode msb-mode) "msb" "msb.el" (14263 63030))
+;;;### (autoloads (msb-mode msb-mode) "msb" "msb.el" (14555 52300))
 ;;; Generated autoloads from msb.el
 
 (defvar msb-mode nil "\
@@ -10111,16 +10174,18 @@ The file is saved in the directory `data-directory'." nil nil)
 ;;;;;;  coding-system-post-read-conversion coding-system-eol-type-mnemonic
 ;;;;;;  lookup-nested-alist set-nested-alist truncate-string-to-width
 ;;;;;;  store-substring string-to-sequence) "mule-util" "international/mule-util.el"
-;;;;;;  (14423 50997))
+;;;;;;  (14568 36382))
 ;;; Generated autoloads from international/mule-util.el
 
 (autoload (quote string-to-sequence) "mule-util" "\
 Convert STRING to a sequence of TYPE which contains characters in STRING.
 TYPE should be `list' or `vector'." nil nil)
 
-(defsubst string-to-list (string) "Return a list of characters in STRING." (string-to-sequence string (quote list)))
+(defsubst string-to-list (string) "\
+Return a list of characters in STRING." (string-to-sequence string (quote list)))
 
-(defsubst string-to-vector (string) "Return a vector of characters in STRING." (string-to-sequence string (quote vector)))
+(defsubst string-to-vector (string) "\
+Return a vector of characters in STRING." (string-to-sequence string (quote vector)))
 
 (autoload (quote store-substring) "mule-util" "\
 Embed OBJ (string or character) at index IDX of STRING." nil nil)
@@ -10142,7 +10207,16 @@ the resulting string may be narrower than END-COLUMN." nil nil)
 
 (defalias (quote truncate-string) (quote truncate-string-to-width))
 
-(defsubst nested-alist-p (obj) "Return t if OBJ is a nested alist.\n\nNested alist is a list of the form (ENTRY . BRANCHES), where ENTRY is\nany Lisp object, and BRANCHES is a list of cons cells of the form\n(KEY-ELEMENT . NESTED-ALIST).\n\nYou can use a nested alist to store any Lisp object (ENTRY) for a key\nsequence KEYSEQ, where KEYSEQ is a sequence of KEY-ELEMENT.  KEYSEQ\ncan be a string, a vector, or a list." (and obj (listp obj) (listp (cdr obj))))
+(defsubst nested-alist-p (obj) "\
+Return t if OBJ is a nested alist.
+
+Nested alist is a list of the form (ENTRY . BRANCHES), where ENTRY is
+any Lisp object, and BRANCHES is a list of cons cells of the form
+\(KEY-ELEMENT . NESTED-ALIST).
+
+You can use a nested alist to store any Lisp object (ENTRY) for a key
+sequence KEYSEQ, where KEYSEQ is a sequence of KEY-ELEMENT.  KEYSEQ
+can be a string, a vector, or a list." (and obj (listp obj) (listp (cdr obj))))
 
 (autoload (quote set-nested-alist) "mule-util" "\
 Set ENTRY for KEYSEQ in a nested alist ALIST.
@@ -10207,7 +10281,7 @@ Enable mouse wheel support." nil nil)
 ;;;### (autoloads (network-connection network-connection-to-service
 ;;;;;;  whois-reverse-lookup whois finger ftp dig nslookup nslookup-host
 ;;;;;;  route arp netstat ipconfig ping traceroute) "net-utils" "net/net-utils.el"
-;;;;;;  (14385 24830))
+;;;;;;  (14564 29931))
 ;;; Generated autoloads from net/net-utils.el
 
 (autoload (quote traceroute) "net-utils" "\
@@ -11097,7 +11171,7 @@ This checks if all multi-byte characters in the region are printable or not." ni
 ;;;;;;  ps-spool-region ps-spool-buffer-with-faces ps-spool-buffer
 ;;;;;;  ps-print-region-with-faces ps-print-region ps-print-buffer-with-faces
 ;;;;;;  ps-print-buffer ps-print-customize ps-paper-type) "ps-print"
-;;;;;;  "ps-print.el" (14554 7425))
+;;;;;;  "ps-print.el" (14563 18761))
 ;;; Generated autoloads from ps-print.el
 
 (defvar ps-paper-type (quote letter) "\
@@ -11679,7 +11753,7 @@ Here are all local bindings.
 ;;;***
 \f
 ;;;### (autoloads (regexp-opt-depth regexp-opt) "regexp-opt" "emacs-lisp/regexp-opt.el"
-;;;;;;  (14535 45202))
+;;;;;;  (14564 29908))
 ;;; Generated autoloads from emacs-lisp/regexp-opt.el
 
 (autoload (quote regexp-opt) "regexp-opt" "\
@@ -12157,11 +12231,11 @@ KEYWORDS is a comma-separated list of labels." t nil)
 
 ;;;***
 \f
-;;;### (autoloads (rmail-summary-line-decoder rmail-summary-by-senders
-;;;;;;  rmail-summary-by-topic rmail-summary-by-regexp rmail-summary-by-recipients
-;;;;;;  rmail-summary-by-labels rmail-summary rmail-summary-line-count-flag
-;;;;;;  rmail-summary-scroll-between-messages) "rmailsum" "mail/rmailsum.el"
-;;;;;;  (14547 28270))
+;;;### (autoloads (rmail-user-mail-address-regexp rmail-summary-line-decoder
+;;;;;;  rmail-summary-by-senders rmail-summary-by-topic rmail-summary-by-regexp
+;;;;;;  rmail-summary-by-recipients rmail-summary-by-labels rmail-summary
+;;;;;;  rmail-summary-line-count-flag rmail-summary-scroll-between-messages)
+;;;;;;  "rmailsum" "mail/rmailsum.el" (14568 47126))
 ;;; Generated autoloads from mail/rmailsum.el
 
 (defvar rmail-summary-scroll-between-messages t "\
@@ -12206,6 +12280,20 @@ SENDERS is a string of names separated by commas." t nil)
 
 By default, `identity' is set.")
 
+(defvar rmail-user-mail-address-regexp nil "\
+*Regexp matching user mail addresses.
+If non-nil, this variable is used to identify the correspondent
+when receiving new mail.  If it matches the address of the sender,
+the recipient is taken as correspondent of a mail.
+If nil (default value), your `user-login-name' and `user-mail-address'
+are used to exclude yourself as correspondent.
+
+Usually you don't have to set this variable, except if you collect mails
+sent by you under different user names.
+Then it should be a regexp matching your mail adresses.
+
+Setting this variable has an effect only before reading a mail.")
+
 ;;;***
 \f
 ;;;### (autoloads (news-post-news) "rnewspost" "mail/rnewspost.el"
@@ -13450,7 +13538,7 @@ strokes with
 ;;;***
 \f
 ;;;### (autoloads (sc-cite-original) "supercite" "mail/supercite.el"
-;;;;;;  (14385 23097))
+;;;;;;  (14565 55801))
 ;;; Generated autoloads from mail/supercite.el
 
 (autoload (quote sc-cite-original) "supercite" "\
@@ -14160,7 +14248,7 @@ a symbol as a valid THING." nil nil)
 ;;;;;;  tibetan-compose-buffer tibetan-decompose-buffer tibetan-composition-function
 ;;;;;;  tibetan-compose-region tibetan-compose-string tibetan-transcription-to-tibetan
 ;;;;;;  tibetan-tibetan-to-transcription tibetan-char-p setup-tibetan-environment)
-;;;;;;  "tibet-util" "language/tibet-util.el" (14423 51008))
+;;;;;;  "tibet-util" "language/tibet-util.el" (14568 36412))
 ;;; Generated autoloads from language/tibet-util.el
 
 (autoload (quote setup-tibetan-environment) "tibet-util" nil t nil)
@@ -14747,8 +14835,8 @@ The buffer in question is current when this function is called." nil nil)
 ;;;;;;  vc-create-snapshot vc-directory vc-resolve-conflicts vc-merge
 ;;;;;;  vc-insert-headers vc-version-other-window vc-diff vc-register
 ;;;;;;  vc-next-action edit-vc-file with-vc-file vc-annotate-mode-hook
-;;;;;;  vc-before-checkin-hook vc-checkin-hook) "vc" "vc.el" (14478
-;;;;;;  52465))
+;;;;;;  vc-before-checkin-hook vc-checkin-hook) "vc" "vc.el" (14565
+;;;;;;  59735))
 ;;; Generated autoloads from vc.el
 
 (defvar vc-checkin-hook nil "\
diff --git a/lisp/progmodes/ebrowse.el b/lisp/progmodes/ebrowse.el
new file mode 100644 (file)
index 0000000..0bacb36
--- /dev/null
@@ -0,0 +1,4573 @@
+;;; ebrowse.el --- Emacs C++ class browser & tags facility
+
+;; Copyright (C) 1992-1999, 2000 Free Software Foundation Inc.
+
+;; Author: Gerd Moellmann <gerd@gnu.org>
+;; Maintainer: FSF
+;; Keywords: C++ tags tools
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 2, or (at your option)
+;; any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs; see the file COPYING.  If not, write to
+;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+;;; Commentary:
+
+;; This package implements
+
+;; - A class browser for C++
+;; - A complete set of tags-like functions working on class trees
+;; - An electric buffer list showing class browser buffers only
+
+;; Documentation is found in a separate Info file. 
+
+;;; Code:
+
+(require 'easymenu)
+(require 'view)
+(require 'ebuff-menu)
+
+(eval-when-compile
+  (require 'cl)
+  (require 'helper))
+
+\f
+;;; User-options
+
+(defgroup ebrowse nil
+  "Settings for the C++ class browser."
+  :group 'tools)
+
+
+(defcustom ebrowse-search-path nil
+  "*List of directories to search for source files in a class tree.
+Elements should be directory names; nil as an element means to try
+to find source files relative to the location of the EBROWSE file loaded."
+  :group 'ebrowse
+  :type '(repeat (choice (const :tag "Default" nil)
+                         (string :tag "Directory"))))
+
+
+(defcustom ebrowse-view/find-hook nil
+  "*Hooks run after finding or viewing a member or class."
+  :group 'ebrowse
+  :type 'hook)
+
+
+(defcustom ebrowse-not-found-hook nil
+  "*Hooks run when finding or viewing a member or class was not successful."
+  :group 'ebrowse
+  :type 'hook)
+
+
+(defcustom ebrowse-electric-list-mode-hook nil
+  "*Hook called by `ebrowse-electric-position-mode'."
+  :group 'ebrowse
+  :type 'hook)
+
+
+(defcustom ebrowse-max-positions 50
+  "*Number of markers saved on electric position stack."
+  :group 'ebrowse
+  :type 'integer)
+
+
+\f
+(defgroup ebrowse-tree nil
+  "Settings for class tree buffers."
+  :group 'ebrowse)
+
+
+(defcustom ebrowse-tree-mode-hook nil
+  "*Hook run in each new tree buffer."
+  :group 'ebrowse-tree
+  :type 'hook)
+
+
+(defcustom ebrowse-tree-buffer-name "*Tree*"
+  "*The default name of class tree buffers."
+  :group 'ebrowse-tree
+  :type 'string)
+
+
+(defcustom ebrowse--indentation 4
+  "*The amount by which subclasses are indented in the tree."
+  :group 'ebrowse-tree
+  :type 'integer)
+
+
+(defcustom ebrowse-source-file-column 40
+  "*The column in which source file names are displayed in the tree."
+  :group 'ebrowse-tree
+  :type 'integer)
+
+
+(defcustom ebrowse-tree-left-margin 2
+  "*Amount of space left at the left side of the tree display.
+This space is used to display markers."
+  :group 'ebrowse-tree
+  :type 'integer)
+
+
+\f
+(defgroup ebrowse-member nil
+  "Settings for member buffers."
+  :group 'ebrowse)
+
+
+(defcustom ebrowse-default-declaration-column 25
+  "*The column in which member declarations are displayed in member buffers."
+  :group 'ebrowse-member
+  :type 'integer)
+
+
+(defcustom ebrowse-default-column-width 25
+  "*The width of the columns in member buffers (short display form)."
+  :group 'ebrowse-member
+  :type 'integer)
+
+
+(defcustom ebrowse-member-buffer-name "*Members*"
+  "*The name of the buffer for member display."
+  :group 'ebrowse-member
+  :type 'string)
+
+
+(defcustom ebrowse-member-mode-hook nil
+  "*Run in each new member buffer."
+  :group 'ebrowse-member
+  :type 'hook)
+
+
+\f
+(defgroup ebrowse-faces nil
+  "Faces used by Ebrowse."
+  :group 'ebrowse)
+
+
+(defface ebrowse-tree-mark-face
+  '((t (:foreground "red")))
+  "*The face used for the mark character in the tree."
+  :group 'ebrowse-faces)
+
+
+(defface ebrowse-root-class-face
+  '((t (:weight bold :foreground "blue")))
+  "*The face used for root classes in the tree."
+  :group 'ebrowse-faces)
+
+
+(defface ebrowse-file-name-face
+  '((t (:italic t)))
+  "*The face for filenames displayed in the tree."
+  :group 'ebrowse-faces)
+
+
+(defface ebrowse-default-face
+  '((t nil))
+  "*Face for everything else in the tree not having other faces."
+  :group 'ebrowse-faces)
+
+
+(defface ebrowse-member-attribute-face
+  '((t (:foreground "red")))
+  "*Face used to display member attributes."
+  :group 'ebrowse-faces)
+
+
+(defface ebrowse-member-class-face
+  '((t (:foreground "purple")))
+  "*Face used to display the class title in member buffers."
+  :group 'ebrowse-faces)
+
+
+(defface ebrowse-progress-face
+  '((t (:background "blue")))
+  "*Face for progress indicator."
+  :group 'ebrowse-faces)
+
+
+\f
+;;; Utilities.
+
+(defun ebrowse-some (predicate vector)
+  "Return true if PREDICATE is true of some element of VECTOR.
+If so, return the value returned by PREDICATE."
+  (let ((length (length vector))
+       (i 0)
+       result)
+    (while (and (< i length) (not result))
+      (setq result (funcall predicate (aref vector i))
+           i (1+ i)))
+    result))
+
+
+(defun ebrowse-every (predicate vector)
+  "Return true if PREDICATE is true of every element of VECTOR."
+  (let ((length (length vector))
+       (i 0)
+       (result t))
+    (while (and (< i length) result)
+      (setq result (funcall predicate (aref vector i))
+           i (1+ i)))
+    result))
+
+
+(defun ebrowse-position (item list &optional test)
+  "Return the position of ITEM in LIST or nil if not found.
+Compare items with `eq' or TEST if specified."
+  (let ((i 0) found)
+    (cond (test
+          (while list
+            (when (funcall test item (car list))
+              (setq found i list nil))
+            (setq list (cdr list) i (1+ i))))
+         (t
+          (while list
+            (when (eq item (car list))
+              (setq found i list nil))
+            (setq list (cdr list) i (1+ i)))))
+    found))
+
+
+(defun ebrowse-delete-if-not (predicate list)
+  "Remove elements not satisfying PREDICATE from LIST and return the result.
+This is a destructive operation."
+  (let (result)
+    (while list
+      (let ((next (cdr list)))
+       (when (funcall predicate (car list))
+         (setq result (nconc result list))
+         (setf (cdr list) nil))
+       (setq list next)))
+    result))
+
+
+(defun ebrowse-copy-list (list)
+  "Return a shallow copy of LIST."
+  (apply #'list list))
+
+
+(defmacro ebrowse-output (&rest body)
+  "Eval BODY with a writable current buffer.
+Preserve buffer's modified state."
+  (let ((modified (gensym "--ebrowse-output--")))
+    `(let (buffer-read-only (,modified (buffer-modified-p)))
+       (unwind-protect
+          (progn ,@body)
+        (set-buffer-modified-p ,modified)))))
+
+
+(defmacro ebrowse-ignoring-completion-case (&rest body)
+  "Eval BODY with `completion-ignore-case' bound to t."
+  `(let ((completion-ignore-case t))
+     ,@body))
+
+
+(defmacro ebrowse-save-selective (&rest body)
+  "Eval BODY with `selective-display' restored at the end."
+  (let ((var (make-symbol "var")))
+    `(let ((,var selective-display))
+       (unwind-protect
+          (progn ,@body)
+        (setq selective-display ,var)))))
+
+
+(defmacro ebrowse-for-all-trees (spec &rest body)
+  "For all trees in SPEC, eval BODY."
+  (let ((var (make-symbol "var"))
+       (spec-var (car spec))
+       (array (cadr spec)))
+    `(loop for ,var being the symbols of ,array
+          as ,spec-var = (get ,var 'ebrowse-root) do
+          (when (vectorp ,spec-var)
+            ,@body))))
+
+;;; Set indentation for macros above.
+
+(put 'ebrowse-output 'lisp-indent-hook 0)
+(put 'ebrowse-ignoring-completion-case 'lisp-indent-hook 0)
+(put 'ebrowse-save-selective 'lisp-indent-hook 0)
+(put 'ebrowse-for-all-trees 'lisp-indent-hook 1)
+
+
+(defsubst ebrowse-set-face (start end face)
+  "Set face of a region START END to FACE."
+  (overlay-put (make-overlay start end) 'face face))
+
+
+(defun ebrowse-completing-read-value (prompt table initial-input)
+  "Read a string in the minibuffer, with completion.
+Case is ignored in completions.
+
+PROMPT is a string to prompt with; normally it ends in a colon and a space.
+TABLE is an alist whose elements' cars are strings, or an obarray.
+TABLE can also be a function to do the completion itself.
+If INITIAL-INPUT is non-nil, insert it in the minibuffer initially.
+If it is (STRING . POSITION), the initial input
+is STRING, but point is placed POSITION characters into the string."
+  (ebrowse-ignoring-completion-case
+    (completing-read prompt table nil t initial-input)))
+
+
+(defun ebrowse-value-in-buffer (sym buffer)
+  "Return the value of SYM in BUFFER."
+  (let ((old-buffer (current-buffer)))
+    (unwind-protect
+        (progn
+          (set-buffer buffer)
+          (symbol-value sym))
+      (set-buffer old-buffer))))
+
+
+(defun ebrowse-rename-buffer (new-name)
+  "Rename current buffer to NEW-NAME.
+If a buffer with name NEW-NAME already exists, delete it first."
+  (let ((old-buffer (get-buffer new-name)))
+    (unless (eq old-buffer (current-buffer))
+      (when old-buffer
+        (save-excursion (kill-buffer old-buffer)))
+      (rename-buffer new-name))))
+
+
+(defun ebrowse-trim-string (string)
+  "Return a copy of STRING with leading white space removed.
+Replace sequences of newlines with a single space."
+  (when (string-match "^[ \t\n\r]+" string)
+    (setq string (substring string (match-end 0))))
+  (loop while (string-match "[\n]+" string)
+        finally return string do
+       (setq string (replace-match " " nil t string))))
+
+
+(defun ebrowse-width-of-drawable-area ()
+  "Return the width of the display area for the current buffer.
+If buffer is displayed in a window, use that window's width,
+otherwise use the current frame's width."
+  (let ((window (get-buffer-window (current-buffer))))
+    (if window
+        (window-width window)
+      (frame-width))))
+
+\f
+;;; Structure definitions
+
+(defstruct (ebrowse-hs (:type vector) :named)
+  "Header structure found at the head of EBROWSE files."
+  ;; A version string that is compared against the version number of
+  ;; the Lisp package when the file is loaded.  This is done to
+  ;; detect file format changes.
+  version
+  ;; Command line options used for producing the EBROWSE file.
+  command-line-options
+  ;; The following slot is currently not used.  It's kept to keep
+  ;; the file format compatible.
+  unused
+  ;; A slot that is filled out after the tree is loaded.  This slot is
+  ;; set to a hash table mapping members to lists of classes in which
+  ;; they are defined.
+  member-table)
+
+    
+(defstruct (ebrowse-ts (:type vector) :named)
+  "Tree structure.
+Following the header structure, an EBROWSE file contains a number
+of `ebrowse-ts' structures, each one describing one root class of
+the class hierarchy with all its subclasses."
+  ;; A `ebrowse-cs' structure describing the root class.
+  class
+  ;; A list of `ebrowse-ts' structures for all subclasses.
+  subclasses
+  ;; Lists of `ebrowse-ms' structures for each member in a group of
+  ;; members.
+  member-variables member-functions static-variables static-functions
+  friends types
+  ;; List of `ebrowse-ts' structures for base classes.  This slot is
+  ;; filled at load time.
+  base-classes
+  ;; A marker slot used in the tree buffer (can be saved back to disk.
+  mark)
+
+
+(defstruct (ebrowse-bs (:type vector) :named)
+  "Common sub-structure.
+A common structure defining an occurrence of some name in the
+source files."
+  ;; The class or member name as a string constant
+  name
+  ;; An optional string for the scope of nested classes or for
+  ;; namespaces.
+  scope
+  ;; Various flags describing properties of classes/members, e.g. is
+  ;; template, is const etc.
+  flags
+  ;; File in which the entity is found.  If this is part of a
+  ;; `ebrowse-ms' member description structure, and FILE is nil, then
+  ;; search for the name in the SOURCE-FILE of the members class.
+  file
+  ;; Regular expression to search for.  This slot can be a number in
+  ;; which case the number is the file position at which the regular
+  ;; expression is found in a separate regexp file (see the header
+  ;; structure).  This slot can be nil in which case the regular
+  ;; expression will be generated from the class/member name.
+  pattern
+  ;; The buffer position at which the search for the class or member
+  ;; will start.
+  point)
+
+
+(defstruct (ebrowse-cs (:include ebrowse-bs) (:type vector) :named)
+  "Class structure.
+This is the structure stored in the CLASS slot of a `ebrowse-ts'
+structure.  It describes the location of the class declaration."
+  source-file)
+
+
+(defstruct (ebrowse-ms (:include ebrowse-bs) (:type vector) :named)
+  "Member structure.
+This is the structure describing a single member.  The `ebrowse-ts'
+structure contains various lists for the different types of
+members."
+  ;; Public, protected, private
+  visibility
+  ;; The file in which the member's definition can be found.
+  definition-file
+  ;; Same as PATTERN above, but for the member definition.
+  definition-pattern
+  ;; Same as POINT above but for member definition.
+  definition-point)
+
+
+\f
+;;; Some macros to access the FLAGS slot of a MEMBER.
+
+(defsubst ebrowse-member-bit-set-p (member bit)
+  "Value is non-nil if MEMBER's bit BIT is set."
+  (/= 0 (logand (ebrowse-bs-flags member) bit)))
+
+
+(defsubst ebrowse-virtual-p (member)
+  "Value is non-nil if MEMBER is virtual."
+  (ebrowse-member-bit-set-p member 1))
+
+
+(defsubst ebrowse-inline-p (member)
+  "Value is non-nil if MEMBER is inline."
+  (ebrowse-member-bit-set-p member 2))
+
+
+(defsubst ebrowse-const-p (member)
+  "Value is non-nil if MEMBER is const."
+  (ebrowse-member-bit-set-p member 4))
+
+
+(defsubst ebrowse-pure-virtual-p (member)
+  "Value is non-nil if MEMBER is a pure virtual function."
+  (ebrowse-member-bit-set-p member 8))
+
+
+(defsubst ebrowse-mutable-p (member)
+  "Value is non-nil if MEMBER is mutable."
+  (ebrowse-member-bit-set-p member 16))
+
+
+(defsubst ebrowse-template-p (member)
+  "Value is non-nil if MEMBER is a template."
+  (ebrowse-member-bit-set-p member 32))
+
+
+(defsubst ebrowse-explicit-p (member)
+  "Value is non-nil if MEMBER is explicit."
+  (ebrowse-member-bit-set-p member 64))
+
+
+(defsubst ebrowse-throw-list-p (member)
+  "Value is non-nil if MEMBER has a throw specification."
+  (ebrowse-member-bit-set-p member 128))
+
+
+(defsubst ebrowse-extern-c-p (member)
+  "Value is non-nil if MEMBER.is `extern \"C\"'."
+  (ebrowse-member-bit-set-p member 256))
+
+
+(defsubst ebrowse-define-p (member)
+  "Value is non-nil if MEMBER is a define."
+  (ebrowse-member-bit-set-p member 512))
+
+
+(defconst ebrowse-version-string "ebrowse 5.0"
+  "Version string expected in EBROWSE files.")
+
+
+(defconst ebrowse-globals-name "*Globals*"
+  "The name used for the surrogate class.containing global entities.
+This must be the same that `ebrowse' uses.")
+
+
+(defvar ebrowse--last-regexp nil
+  "Last regular expression searched for in tree and member buffers.
+Automatically buffer-local so that each tree and member buffer
+maintains its own search history.")
+(make-variable-buffer-local 'ebrowse--last-regexp)
+
+
+(defconst ebrowse-member-list-accessors
+  '(ebrowse-ts-member-variables
+    ebrowse-ts-member-functions
+    ebrowse-ts-static-variables
+    ebrowse-ts-static-functions
+    ebrowse-ts-friends
+    ebrowse-ts-types)
+  "List of accessors for member lists.
+Each element is the symbol of an accessor function.
+The nth element must be the accessor for the nth member list
+in an `ebrowse-ts' structure.")
+
+
+;;; FIXME: Add more doc strings for the buffer-local variables below.
+
+(defvar ebrowse--tree-obarray nil
+  "Obarray holding all `ebrowse-ts' structures of a class tree.
+Buffer-local in Ebrowse buffers.")
+
+
+(defvar ebrowse--tags-file-name nil
+  "File from which EBROWSE file was loaded.
+Buffer-local in Ebrowse buffers.")
+
+
+(defvar ebrowse--header nil
+  "Header structure of type `ebrowse-hs' of a class tree.
+Buffer-local in Ebrowse buffers.")
+
+
+(defvar ebrowse--frozen-flag nil
+  "Non-nil means an Ebrowse buffer won't be reused.
+Buffer-local in Ebrowse buffers.")
+  
+
+(defvar ebrowse--show-file-names-flag nil
+  "Non-nil means show file names in a tree buffer.
+Buffer-local in Ebrowse tree buffers.")
+
+
+(defvar ebrowse--long-display-flag nil
+  "Non-nil means show members in long display form.
+Buffer-local in Ebrowse member buffers.")
+
+
+(defvar ebrowse--n-columns nil
+  "Number of columns to display for short member display form.
+Buffer-local in Ebrowse member buffers.")
+
+
+(defvar ebrowse--column-width nil
+  "Width of a columns to display for short member display form.
+Buffer-local in Ebrowse member buffers.")
+
+  
+(defvar ebrowse--virtual-display-flag nil
+  "Non-nil means display virtual members in a member buffer.
+Buffer-local in Ebrowse member buffers.")
+
+
+(defvar ebrowse--inline-display-flag nil
+  "Non-nil means display inline members in a member buffer.
+Buffer-local in Ebrowse member buffers.")
+
+
+(defvar ebrowse--const-display-flag nil
+  "Non-nil means display const members in a member buffer.
+Buffer-local in Ebrowse member buffers.")
+
+
+(defvar ebrowse--pure-display-flag nil
+  "Non-nil means display pure virtual members in a member buffer.
+Buffer-local in Ebrowse member buffers.")
+
+
+(defvar ebrowse--filters nil
+  "Filter for display of public, protected, and private members.
+This is a vector of three elements.  An element nil means the
+corresponding members are not shown.
+Buffer-local in Ebrowse member buffers.")
+
+
+(defvar ebrowse--show-inherited-flag nil
+  "Non-nil means display inherited members in a member buffer.
+Buffer-local in Ebrowse member buffers.")
+
+
+(defvar ebrowse--attributes-flag nil
+  "Non-nil means display member attributes in a member buffer.
+Buffer-local in Ebrowse member buffers.")
+
+
+(defvar ebrowse--source-regexp-flag nil
+  "Non-nil means display member regexps in a member buffer.
+Buffer-local in Ebrowse member buffers.")
+
+
+(defvar ebrowse--displayed-class nil
+  "Class displayed in a member buffer, a `ebrowse-ts' structure.
+Buffer-local in Ebrowse member buffers.")
+
+
+(defvar ebrowse--accessor nil
+  "Member list displayed in a member buffer.
+This is a symbol whose function definition is an accessor for the
+member list in `ebrowse-cs' structures.
+Buffer-local in Ebrowse member buffers.")
+
+
+(defvar ebrowse--member-list nil
+  "The list of `ebrowse-ms' structures displayed in a member buffer.
+Buffer-local in Ebrowse member buffers.")
+
+
+(defvar ebrowse--decl-column nil
+  "Column in which declarations are displayed in member buffers.
+Buffer-local in Ebrowse member buffers.")
+
+
+(defvar ebrowse--mode-strings nil
+  "Strings displayed in the mode line.
+Buffer-local in Ebrowse tree buffers.")
+
+
+(defvar ebrowse--frame-configuration nil
+  "Frame configuration saved when viewing a class/member in another frame.
+Buffer-local in Ebrowse buffers.")
+
+
+(defvar ebrowse--view-exit-action nil
+  "Action to perform after viewing a class/member.
+Either `kill-buffer' or nil.
+Buffer-local in Ebrowse buffers.")
+
+
+(defvar ebrowse--tree nil
+  "Class tree.
+Buffer-local in Ebrowse buffers.")
+
+
+(defvar ebrowse--mode-line-props nil
+  "Text properties of mode line strings in member buffers.
+Buffer-local in Ebrowse member buffers.")
+
+
+;;; Temporaries used to communicate with `ebrowse-find-pattern'.
+
+(defvar ebrowse-temp-position-to-view nil)
+(defvar ebrowse-temp-info-to-view nil)
+
+
+(defvar ebrowse-tree-mode-map ()
+  "The keymap used in tree mode buffers.")
+
+
+(defvar ebrowse--member-mode-strings nil
+  "Strings displayed in the mode line of member buffers.")
+
+
+(defvar ebrowse-member-mode-map ()
+  "The keymap used in the member buffers.")
+
+
+;;; Define mode line titles for each member list.
+
+(put 'ebrowse-ts-member-variables 'ebrowse-title "Member Variables")
+(put 'ebrowse-ts-member-functions 'ebrowse-title "Member Functions")
+(put 'ebrowse-ts-static-variables 'ebrowse-title "Static Variables")
+(put 'ebrowse-ts-static-functions 'ebrowse-title "Static Functions")
+(put 'ebrowse-ts-friends 'ebrowse-title "Friends")
+(put 'ebrowse-ts-types 'ebrowse-title "Types")
+
+(put 'ebrowse-ts-member-variables 'ebrowse-global-title "Global Variables")
+(put 'ebrowse-ts-member-functions 'ebrowse-global-title "Global Functions")
+(put 'ebrowse-ts-static-variables 'ebrowse-global-title "Static Variables")
+(put 'ebrowse-ts-static-functions 'ebrowse-global-title "Static Functions")
+(put 'ebrowse-ts-friends 'ebrowse-global-title "Defines")
+(put 'ebrowse-ts-types 'ebrowse-global-title "Types")
+
+
+\f
+;;; Operations on `ebrowse-ts' structures
+
+(defun ebrowse-files-table (&optional marked-only)
+  "Return an obarray containing all files mentioned in the current tree.
+The tree is expected in the buffer-local variable `ebrowse--tree-obarray'.
+MARKED-ONLY non-nil means include marked classes only."
+  (let ((files (make-hash-table :test 'equal))
+       (i -1))
+    (ebrowse-for-all-trees (tree ebrowse--tree-obarray)
+      (when (or (not marked-only) (ebrowse-ts-mark tree))
+       (let ((class (ebrowse-ts-class tree)))
+         (when (zerop (% (incf i) 20))
+           (ebrowse-show-progress "Preparing file list" (zerop i)))
+         ;; Add files mentioned in class description
+         (let ((source-file (ebrowse-cs-source-file class))
+               (file        (ebrowse-cs-file class)))
+           (when source-file
+             (puthash source-file source-file files))
+           (when file
+             (puthash file file files))
+           ;; For all member lists in this class
+           (loop for accessor in ebrowse-member-list-accessors do
+                 (loop for m in (funcall accessor tree)
+                       for file = (ebrowse-ms-file m)
+                       for def-file = (ebrowse-ms-definition-file m) do
+                       (when file
+                         (puthash file file files))
+                       (when def-file
+                         (puthash def-file def-file files))))))))
+    files))
+
+
+(defun ebrowse-files-list (&optional marked-only)
+  "Return a list containing all files mentioned in a tree.
+MARKED-ONLY non-nil means include marked classes only."
+  (let (list)
+    (maphash #'(lambda (file dummy) (setq list (cons file list)))
+            (ebrowse-files-table marked-only))
+    list))
+
+
+(defun* ebrowse-marked-classes-p ()
+  "Value is non-nil if any class in the current class tree is marked."
+  (ebrowse-for-all-trees (tree ebrowse--tree-obarray)
+    (when (ebrowse-ts-mark tree)
+      (return-from ebrowse-marked-classes-p tree))))
+
+
+(defsubst ebrowse-globals-tree-p (tree)
+  "Return t if TREE is the one for global entities."
+  (string= (ebrowse-bs-name (ebrowse-ts-class tree))
+          ebrowse-globals-name))
+
+
+(defsubst ebrowse-qualified-class-name (class)
+  "Return the name of CLASS with scope prepended, if any."
+  (if (ebrowse-cs-scope class)
+      (concat (ebrowse-cs-scope class) "::" (ebrowse-cs-name class))
+    (ebrowse-cs-name class)))
+
+
+(defun ebrowse-tree-obarray-as-alist (&optional qualified-names-p)
+  "Return an alist describing all classes in a tree.
+Each elements in the list has the form (CLASS-NAME . TREE).
+CLASS-NAME is the name of the class.  TREE is the
+class tree whose root is QUALIFIED-CLASS-NAME.
+QUALIFIED-NAMES-P non-nil means return qualified names as CLASS-NAME.
+The class tree is found in the buffer-local variable `ebrowse--tree-obarray'."
+  (let (alist)
+    (if qualified-names-p
+       (ebrowse-for-all-trees (tree ebrowse--tree-obarray)
+         (setq alist
+               (acons (ebrowse-qualified-class-name (ebrowse-ts-class tree))
+                      tree alist)))
+      (ebrowse-for-all-trees (tree ebrowse--tree-obarray)
+       (setq alist
+             (acons (ebrowse-cs-name (ebrowse-ts-class tree))
+                    tree alist))))
+    alist))
+
+
+(defun ebrowse-sort-tree-list (list)
+  "Sort a LIST of `ebrowse-ts' structures by qualified class names."
+  (sort list
+       #'(lambda (a b)
+           (string< (ebrowse-qualified-class-name (ebrowse-ts-class a))
+                    (ebrowse-qualified-class-name (ebrowse-ts-class b))))))
+
+
+(defun ebrowse-class-in-tree (class tree)
+  "Search for a class with name CLASS in TREE.
+Return the class found, if any.  This function is used during the load
+phase where classes appended to a file replace older class
+information."
+  (let ((tclass (ebrowse-ts-class class))
+       found)
+    (while (and tree (not found))
+      (let ((root (car tree)))
+       (when (string= (ebrowse-qualified-class-name (ebrowse-ts-class root))
+                      (ebrowse-qualified-class-name tclass))
+         (setq found root))
+       (setq tree (cdr tree))))
+    found))
+
+
+(defun ebrowse-base-classes (tree)
+  "Return list of base-classes of TREE by searching subclass lists.
+This function must be used instead of the struct slot 
+`base-classes' to access the base-class list directly because it
+computes this information lazily."
+  (or (ebrowse-ts-base-classes tree)
+      (setf (ebrowse-ts-base-classes tree)
+           (loop with to-search = (list tree)
+                 with result = nil
+                 as search = (pop to-search)
+                 while search finally return result
+                 do (ebrowse-for-all-trees (ti ebrowse--tree-obarray)
+                      (when (memq search (ebrowse-ts-subclasses ti))
+                        (unless (memq ti result)
+                          (setq result (nconc result (list ti))))
+                        (push ti to-search)))))))
+
+
+(defun ebrowse-direct-base-classes (tree)
+  "Return the list of direct super classes of TREE."
+  (let (result)
+    (dolist (s (ebrowse-base-classes tree))
+      (when (memq tree (ebrowse-ts-subclasses s))
+       (setq result (cons s result))))
+    result))
+
+
+\f
+;;; Operations on MEMBER structures/lists
+
+(defun ebrowse-name/accessor-alist (tree accessor)
+  "Return an alist containing all members of TREE in group ACCESSOR.
+ACCESSOR is the accessor function for the member list.
+Elements of the result have the form (NAME . ACCESSOR), where NAME
+is the member name."
+  (loop for member in (funcall accessor tree)
+       collect (cons (ebrowse-ms-name member) accessor)))
+
+
+(defun ebrowse-name/accessor-alist-for-visible-members ()
+  "Return an alist describing all members visible in the current buffer.
+Each element of the list has the form (MEMBER-NAME . ACCESSOR),
+where MEMBER-NAME is the member's name, and ACCESSOR is the struct
+accessor with which the member's list can be accessed in an `ebrowse-ts'
+structure.  The list includes inherited members if these are visible."
+  (let* ((list (ebrowse-name/accessor-alist ebrowse--displayed-class
+                                           ebrowse--accessor)))
+    (if ebrowse--show-inherited-flag
+       (nconc list
+              (loop for tree in (ebrowse-base-classes
+                                 ebrowse--displayed-class)
+                    nconc (ebrowse-name/accessor-alist
+                           tree ebrowse--accessor)))
+      list)))
+
+
+(defun ebrowse-name/accessor-alist-for-class-members ()
+  "Like `ebrowse-name/accessor-alist-for-visible-members'.
+This function includes members of base classes if base class members
+are visible in the buffer."
+  (let (list)
+    (dolist (func ebrowse-member-list-accessors list)
+      (setq list (nconc list (ebrowse-name/accessor-alist
+                             ebrowse--displayed-class func)))
+      (when ebrowse--show-inherited-flag
+       (dolist (class (ebrowse-base-classes ebrowse--displayed-class))
+         (setq list
+               (nconc list (ebrowse-name/accessor-alist class func))))))))
+
+\f
+;;; Progress indication
+
+(defvar ebrowse-n-boxes 0)
+(defconst ebrowse-max-boxes 60)
+
+(defun ebrowse-show-progress (title &optional start)
+  "Display a progress indicator.
+TITLE is the title of the progress message.  START non-nil means
+this is the first progress message displayed."
+  (let (message-log-max)
+    (when start (setq ebrowse-n-boxes 0))
+    (setq ebrowse-n-boxes (mod (1+ ebrowse-n-boxes) ebrowse-max-boxes))
+    (message (concat title ": "
+                    (propertize (make-string ebrowse-n-boxes
+                                             (if (display-color-p) ?\  ?+))
+                                'face 'ebrowse-progress-face)))))
+
+\f
+;;; Reading a tree from disk
+
+(defun ebrowse-find-file ()
+  "Function installed as `find-file hook'.
+This loads a tree when it sees a special signature at the beginning of
+the file loaded."
+  (when (looking-at "\\[ebrowse-hs")
+    (ebrowse-load buffer-file-name 'switch)))
+
+
+(defun ebrowse-read ()
+  "Read `ebrowse-hs' and `ebrowse-ts' structures in the current buffer.  
+Return a list (HEADER TREE) where HEADER is the file header read
+and TREE is a list of `ebrowse-ts' structures forming the class tree."
+  (let ((header (condition-case nil
+                   (read (current-buffer))
+                 (error (error "No Ebrowse file header found"))))
+       tree)
+    ;; Check file format.
+    (unless (ebrowse-hs-p header)
+      (error "No Ebrowse file header found"))
+    (unless (string= (ebrowse-hs-version header) ebrowse-version-string)
+      (error "File has wrong version `%s' (`%s' expected)"
+            (ebrowse-hs-version header) ebrowse-version-string))
+    ;; Read Lisp objects.  Temporarily increase `gc-cons-threshold' to
+    ;; prevent a GC that would not free any memory.
+    (let ((gc-cons-threshold 2000000))
+      (while (not (eobp))
+       (let* ((root (read (current-buffer)))
+              (old-root (ebrowse-class-in-tree root tree)))
+         (ebrowse-show-progress "Reading data" (null tree))
+         (if old-root
+             (setf (car old-root) root)
+           (push root tree)))))
+    (garbage-collect)
+    (list header tree)))
+
+
+(defun ebrowse-load (file &optional switch)
+  "Load an Ebrowse file FILE into memory and make a tree buffer.
+Optional SWITCH non-nil means switch to the tree buffer afterwards.
+This function is normally called from a `find-file-hook'.
+Value is the tree buffer created."
+  (let (tree
+       header
+       (buffer (get-file-buffer file))
+       tree-buffer)
+    (if buffer
+       (multiple-value-setq (header tree)
+         (ebrowse-read))
+      (save-excursion
+       ;; Since find-file hooks may be involved, we must visit the
+       ;; file in a way that these hooks are not called.
+       (set-buffer (create-file-buffer file))
+       (erase-buffer)
+       (insert-file file)
+       (set-buffer-modified-p nil)
+       (unwind-protect
+           (multiple-value-setq (header tree)
+             (ebrowse-read))
+         (kill-buffer (current-buffer)))))
+    (when tree
+      (message "Sorting. Please be patient...")
+      (setf tree (ebrowse-sort-tree-list tree))
+      ;; Create tree buffer
+      (setf tree-buffer
+           (ebrowse-create-tree-buffer tree file header
+                                       (ebrowse-build-tree-obarray tree)
+                                       switch buffer))
+      (message nil)
+      tree-buffer)))
+
+
+(defun ebrowse-revert-tree-buffer-from-file (ignore-auto-save noconfirm)
+  "Function installed as `revert-buffer-function' in tree buffers.
+See that variable's documentation for the meaning of IGNORE-AUTO-SAVE and
+NOCONFIRM."
+  (interactive)
+  (when (or noconfirm
+           (yes-or-no-p "Revert tree from disk? "))
+    (let ((ebrowse-file (or buffer-file-name ebrowse--tags-file-name)))
+      (loop for member-buffer in (ebrowse-same-tree-member-buffer-list)
+           do (kill-buffer member-buffer))
+      (kill-buffer (current-buffer))
+      (switch-to-buffer (ebrowse-load ebrowse-file)))))
+
+
+(defun ebrowse-create-tree-buffer (tree tags-file header obarray pop
+                                       &optional find-file-buffer)
+  "Create a new tree buffer for tree TREE.
+The tree was loaded from file TAGS-FILE.
+HEADER is the header structure of the file.
+OBARRAY is an obarray with a symbol for each class in the tree.
+POP non-nil means popup the buffer up at the end.
+FIND-FILE-BUFFER, if non-nil, is the buffer from which the Lisp data
+was read.
+Return the buffer created."
+  (let (name)
+    (cond (find-file-buffer
+          (set-buffer find-file-buffer)
+          (erase-buffer)
+          (setq name (ebrowse-frozen-tree-buffer-name tags-file))
+          (ebrowse-rename-buffer name))
+         (t
+          (setq name ebrowse-tree-buffer-name)
+          (set-buffer (get-buffer-create name))))
+    ;; Switch to tree mode and initialize buffer local variables.
+    (ebrowse-tree-mode)
+    (setf ebrowse--tree          tree
+         ebrowse--tags-file-name tags-file
+         ebrowse--tree-obarray  obarray
+         ebrowse--header        header
+         ebrowse--frozen-flag        (not (null find-file-buffer)))
+    ;; Switch or pop to the tree buffer; display the tree and return the
+    ;; buffer.
+    (case pop 
+      (switch (switch-to-buffer name))
+      (pop    (pop-to-buffer name)))
+    (ebrowse-redraw-tree)
+    (set-buffer-modified-p nil)
+    (current-buffer)))
+
+
+\f
+;;; Operations for member obarrays
+
+(defun ebrowse-fill-member-table ()
+  "Return an obarray holding all members of all classes in the current tree.
+
+For each member, a symbol is added to the obarray.  Members are
+extracted from the buffer-local tree `ebrowse--tree-obarray'.
+
+Each symbol has its property `ebrowse-info' set to a list (TREE MEMBER-LIST
+MEMBER) where TREE is the tree in which the member is defined,
+MEMBER-LIST is a symbol describing the member list in which the member
+is found, and MEMBER is a MEMBER structure describing the member.
+
+The slot `member-table' of the buffer-local header structure of
+type `ebrowse-hs' is set to the resulting obarray."
+  (let ((members (make-hash-table :test 'equal))
+       (i -1))
+    (setf (ebrowse-hs-member-table ebrowse--header) nil)
+    (garbage-collect)
+    ;; For all classes...
+    (ebrowse-for-all-trees (c ebrowse--tree-obarray)
+      (when (zerop (% (incf i) 10))
+       (ebrowse-show-progress "Preparing member lookup" (zerop i)))
+      (loop for f in ebrowse-member-list-accessors do
+           (loop for m in (funcall f c) do
+                 (let* ((member-name (ebrowse-ms-name m))
+                        (value (gethash member-name members)))
+                   (push (list c f m) value)
+                   (puthash member-name value members)))))
+    (setf (ebrowse-hs-member-table ebrowse--header) members)))
+
+
+(defun ebrowse-member-table (header)
+  "Return the member obarray.  Build it it hasn't been set up yet.
+HEADER is the tree header structure of the class tree."
+  (when (null (ebrowse-hs-member-table header))
+    (loop for buffer in (ebrowse-browser-buffer-list)
+         until (eq header (ebrowse-value-in-buffer 'ebrowse--header buffer))
+         finally do
+         (save-excursion
+           (set-buffer buffer)
+           (ebrowse-fill-member-table))))
+  (ebrowse-hs-member-table header))
+
+
+\f
+;;; Operations on TREE obarrays
+
+(defun ebrowse-build-tree-obarray (tree)
+  "Make sure every class in TREE is represented by a unique object.
+Build obarray of all classes in TREE."
+  (let ((classes (make-vector 127 0)))
+    ;; Add root classes...
+    (loop for root in tree
+         as sym =
+         (intern (ebrowse-qualified-class-name (ebrowse-ts-class root)) classes)
+         do (unless (get sym 'ebrowse-root)
+              (setf (get sym 'ebrowse-root) root)))
+    ;; Process subclasses
+    (ebrowse-insert-supers tree classes)
+    classes))
+
+
+(defun ebrowse-insert-supers (tree classes)
+  "Build base class lists in class tree TREE.
+CLASSES is an obarray used to collect classes.
+
+Helper function for `ebrowse-build-tree-obarray'.  Base classes should
+be ordered so that immediate base classes come first, then the base
+class of the immediate base class and so on.  This means that we must
+construct the base-class list top down with adding each level at the
+beginning of the base-class list.
+
+We have to be cautious here not to end up in an infinite recursion
+if for some reason a circle is in the inheritance graph."
+  (loop for class in tree
+       as subclasses = (ebrowse-ts-subclasses class) do
+       ;; Make sure every class is represented by a unique object
+       (loop for subclass on subclasses
+             as sym = (intern
+                       (ebrowse-qualified-class-name (ebrowse-ts-class (car subclass)))
+                       classes)
+             as next = nil
+             do
+             ;; Replace the subclass tree with the one found in
+             ;; CLASSES if there is already an entry for that class
+             ;; in it. Otherwise make a new entry.
+             ;;
+             ;; CAVEAT: If by some means (e.g., use of the
+             ;; preprocessor in class declarations, a name is marked
+             ;; as a subclass of itself on some path, we would end up
+             ;; in an endless loop. We have to omit subclasses from
+             ;; the recursion that already have been processed.
+             (if (get sym 'ebrowse-root)
+                 (setf (car subclass) (get sym 'ebrowse-root))
+               (setf (get sym 'ebrowse-root) (car subclass))))
+       ;; Process subclasses
+       (ebrowse-insert-supers subclasses classes)))
+
+\f
+;;; Tree buffers
+
+(unless ebrowse-tree-mode-map
+  (let ((map (make-keymap)))
+    (setf ebrowse-tree-mode-map map)
+    (suppress-keymap map)
+
+    (when window-system
+      (define-key map [down-mouse-3] 'ebrowse-mouse-3-in-tree-buffer)
+      (define-key map [mouse-2] 'ebrowse-mouse-2-in-tree-buffer)
+      (define-key map [down-mouse-1] 'ebrowse-mouse-1-in-tree-buffer))
+  
+    (let ((map1 (make-sparse-keymap)))
+      (suppress-keymap map1 t)
+      (define-key map "L" map1)
+      (define-key map1 "d" 'ebrowse-tree-command:show-friends)
+      (define-key map1 "f" 'ebrowse-tree-command:show-member-functions)
+      (define-key map1 "F" 'ebrowse-tree-command:show-static-member-functions)
+      (define-key map1 "t" 'ebrowse-tree-command:show-types)
+      (define-key map1 "v" 'ebrowse-tree-command:show-member-variables)
+      (define-key map1 "V" 'ebrowse-tree-command:show-static-member-variables))
+
+    (let ((map1 (make-sparse-keymap)))
+      (suppress-keymap map1 t)
+      (define-key map "M" map1)
+      (define-key map1 "a" 'ebrowse-mark-all-classes)
+      (define-key map1 "t" 'ebrowse-toggle-mark-at-point))
+
+    (let ((map1 (make-sparse-keymap)))
+      (suppress-keymap map1 t)
+      (define-key map "T" map1)
+      (define-key map1 "f" 'ebrowse-toggle-file-name-display)
+      (define-key map1 "s" 'ebrowse-show-file-name-at-point)
+      (define-key map1 "w" 'ebrowse-set-tree-indentation)
+      (define-key map "x" 'ebrowse-statistics))
+
+    (define-key map "n" 'ebrowse-repeat-member-search)
+    (define-key map "q" 'bury-buffer)
+    (define-key map "*" 'ebrowse-expand-all)
+    (define-key map "+" 'ebrowse-expand-branch)
+    (define-key map "-" 'ebrowse-collapse-branch)
+    (define-key map "/" 'ebrowse-read-class-name-and-go)
+    (define-key map " " 'ebrowse-view-class-declaration)
+    (define-key map "?" 'describe-mode)
+    (define-key map "\C-i" 'ebrowse-pop/switch-to-member-buffer-for-same-tree)
+    (define-key map "\C-k" 'ebrowse-remove-class-at-point)
+    (define-key map "\C-l" 'ebrowse-redraw-tree)
+    (define-key map "\C-m" 'ebrowse-find-class-declaration)))
+
+
+\f
+;;; Tree-mode - mode for tree buffers
+
+;;;###autoload
+(defun ebrowse-tree-mode ()
+  "Major mode for Ebrowse class tree buffers.
+Each line corresponds to a class in a class tree.
+Letters do not insert themselves, they are commands.
+File operations in the tree buffer work on class tree data structures.
+E.g.\\[save-buffer] writes the tree to the file it was loaded from.
+
+Tree mode key bindings:
+\\{ebrowse-tree-mode-map}"
+  (kill-all-local-variables)
+  (mapcar 'make-local-variable
+         '(ebrowse--tags-file-name
+           ebrowse--indentation
+           ebrowse--tree
+           ebrowse--header
+           ebrowse--show-file-names-flag
+           ebrowse--frozen-flag
+           ebrowse--tree-obarray
+           ebrowse--mode-strings
+           revert-buffer-function))
+  (use-local-map ebrowse-tree-mode-map)
+  (let* ((props (text-properties-at
+                0
+                (car (default-value 'mode-line-buffer-identification))))
+        (ident (apply #'propertize "C++ Tree" props)))
+    (setf ebrowse--show-file-names-flag nil
+         ebrowse--tree-obarray (make-vector 127 0)
+         ebrowse--frozen-flag nil
+         major-mode 'ebrowse-tree-mode
+         mode-name "Ebrowse-Tree"
+         mode-line-buffer-identification (list ident)
+         buffer-read-only t
+         selective-display t
+         selective-display-ellipses t
+         revert-buffer-function 'ebrowse-revert-tree-buffer-from-file))
+  (add-hook 'local-write-file-hooks 'ebrowse-write-file-hook-fn)
+  (modify-syntax-entry ?_ (char-to-string (char-syntax ?a)))
+  (run-hooks 'ebrowse-tree-mode-hook))
+
+
+(defun ebrowse-update-tree-buffer-mode-line ()
+  "Update the tree buffer mode line."
+  (setf ebrowse--mode-strings
+       (concat (if ebrowse--frozen-flag (or buffer-file-name
+                                            ebrowse--tags-file-name))
+               (if (buffer-modified-p) "-**")))
+  (ebrowse-rename-buffer (if ebrowse--frozen-flag
+                            (ebrowse-frozen-tree-buffer-name
+                             ebrowse--tags-file-name)
+                          ebrowse-tree-buffer-name))
+  (force-mode-line-update))
+
+
+\f
+;;; Removing classes from trees
+
+(defun ebrowse-remove-class-and-kill-member-buffers (tree class)
+  "Remove from TREE class CLASS.
+Kill all member buffers still containing a reference to the class."
+  (let ((sym (intern-soft (ebrowse-cs-name (ebrowse-ts-class class))
+                         ebrowse--tree-obarray)))
+    (setf tree (delq class tree)
+         (get sym 'ebrowse-root) nil)
+    (dolist (root tree)
+      (setf (ebrowse-ts-subclasses root)
+           (delq class (ebrowse-ts-subclasses root))
+           (ebrowse-ts-base-classes root) nil)
+      (ebrowse-remove-class-and-kill-member-buffers
+       (ebrowse-ts-subclasses root) class))
+    (ebrowse-kill-member-buffers-displaying class)
+    tree))
+
+
+(defun ebrowse-remove-class-at-point (forced)
+  "Remove the class point is on from the class tree.
+Do not ask for confirmation if FORCED is non-nil."
+  (interactive "P")
+  (let* ((class (ebrowse-tree-at-point))
+        (class-name (ebrowse-cs-name (ebrowse-ts-class class)))
+        (subclasses (ebrowse-ts-subclasses class)))
+    (cond ((or forced
+              (y-or-n-p (concat "Delete class " class-name "? ")))
+          (setf ebrowse--tree (ebrowse-remove-class-and-kill-member-buffers
+                               ebrowse--tree class))
+          (set-buffer-modified-p t)
+          (message "%s %sdeleted." class-name
+                   (if subclasses "and derived classes " ""))
+          (ebrowse-redraw-tree))
+         (t (message "Aborted")))))
+
+
+\f
+;;; Marking classes in the tree buffer
+
+(defun ebrowse-toggle-mark-at-point (&optional n-times)
+  "Toggle mark for class cursor is on.
+If given a numeric N-TIMES argument, mark that many classes."
+  (interactive "p")
+  (let (to-change pnt)
+    ;; Get the classes whose mark must be toggled. Note that
+    ;; ebrowse-tree-at-point might issue an error.
+    (condition-case error
+       (loop repeat (or n-times 1)
+             as tree = (ebrowse-tree-at-point)
+             do (progn 
+                  (setf (ebrowse-ts-mark tree) (not (ebrowse-ts-mark tree)))
+                  (forward-line 1)
+                  (push tree to-change)))
+      (error nil))
+    (save-excursion
+      ;; For all these classes, reverse the mark char in the display
+      ;; by a regexp replace over the whole buffer. The reason for this
+      ;; is that classes might have multiple base classes. If this is
+      ;; the case, they are displayed more than once in the tree.
+      (ebrowse-output
+       (loop for tree in to-change
+             as regexp = (concat "^.*\\b"
+                                 (regexp-quote
+                                  (ebrowse-cs-name (ebrowse-ts-class tree)))
+                                 "\\b")
+             do
+             (goto-char (point-min))
+             (loop while (re-search-forward regexp nil t)
+                   do (progn
+                        (goto-char (match-beginning 0))
+                        (delete-char 1)
+                        (insert-char (if (ebrowse-ts-mark tree) ?> ? ) 1)
+                        (ebrowse-set-mark-props (1- (point)) (point) tree)
+                        (goto-char (match-end 0)))))))))
+
+
+(defun ebrowse-mark-all-classes (prefix)
+  "Unmark, with PREFIX mark, all classes in the tree."
+  (interactive "P")
+  (ebrowse-for-all-trees (tree ebrowse--tree-obarray)
+    (setf (ebrowse-ts-mark tree) prefix))
+  (ebrowse-redraw-marks (point-min) (point-max)))
+
+
+(defun ebrowse-redraw-marks (start end)
+  "Display class marker signs in the tree between START and END."
+  (interactive)
+  (save-excursion
+    (ebrowse-output
+      (catch 'end
+       (goto-char (point-min))
+       (dolist (root ebrowse--tree)
+         (ebrowse-draw-marks-fn root start end))))
+    (ebrowse-update-tree-buffer-mode-line)))
+
+
+(defun ebrowse-draw-marks-fn (tree start end)
+  "Display class marker signs in TREE between START and END."
+  (when (>= (point) start)
+    (delete-char 1)
+    (insert (if (ebrowse-ts-mark tree) ?> ? ))
+    (ebrowse-set-mark-props (1- (point)) (point) tree))
+  (forward-line 1)
+  (when (> (point) end)
+    (throw 'end nil))
+  (dolist (sub (ebrowse-ts-subclasses tree))
+    (ebrowse-draw-marks-fn sub start end)))
+
+
+\f
+;;; File name display in tree buffers
+
+(defun ebrowse-show-file-name-at-point (prefix)
+  "Show filename in the line point is in.
+With PREFIX, insert that many filenames."
+  (interactive "p")
+  (unless ebrowse--show-file-names-flag
+    (ebrowse-output
+      (dotimes (i prefix)
+       (let ((tree (ebrowse-tree-at-point))
+             start
+             file-name-existing)
+         (unless tree return)
+         (beginning-of-line)
+         (skip-chars-forward " \t*a-zA-Z0-9_")
+         (setq start (point)
+               file-name-existing (looking-at "("))
+         (delete-region start (save-excursion (end-of-line) (point)))
+         (unless file-name-existing
+           (indent-to ebrowse-source-file-column)
+           (insert "(" (or (ebrowse-cs-file
+                            (ebrowse-ts-class tree))
+                           "unknown")
+                   ")"))
+         (ebrowse-set-face start (point) 'ebrowse-file-name-face)
+         (beginning-of-line)
+         (forward-line 1))))))
+
+
+(defun ebrowse-toggle-file-name-display ()
+  "Toggle display of filenames in tree buffer."
+  (interactive)
+  (setf ebrowse--show-file-names-flag (not ebrowse--show-file-names-flag))
+  (let ((old-line (count-lines (point-min) (point))))
+    (ebrowse-redraw-tree)
+    (goto-line old-line)))
+
+
+\f
+;;; General member and tree buffer functions
+
+(defun ebrowse-member-buffer-p (buffer)
+  "Value is non-nil if BUFFER is a member buffer."
+  (eq (cdr (assoc 'major-mode (buffer-local-variables buffer)))
+      'ebrowse-member-mode))
+
+
+(defun ebrowse-tree-buffer-p (buffer)
+  "Value is non-nil if BUFFER is a class tree buffer."
+  (eq (cdr (assoc 'major-mode (buffer-local-variables buffer)))
+      'ebrowse-tree-mode))
+
+
+(defun ebrowse-buffer-p (buffer)
+  "Value is non-nil if BUFFER is a tree or member buffer."
+  (memq (cdr (assoc 'major-mode (buffer-local-variables buffer)))
+       '(ebrowse-tree-mode ebrowse-member-mode)))
+
+
+(defun ebrowse-browser-buffer-list ()
+  "Return a list of all tree or member buffers."
+  (ebrowse-delete-if-not 'ebrowse-buffer-p (buffer-list)))
+
+
+(defun ebrowse-member-buffer-list ()
+  "Return a list of all member buffers."
+  (ebrowse-delete-if-not 'ebrowse-member-buffer-p (buffer-list)))
+
+
+(defun ebrowse-tree-buffer-list ()
+  "Return a list of all tree buffers."
+  (ebrowse-delete-if-not 'ebrowse-tree-buffer-p (buffer-list)))
+
+
+(defun ebrowse-known-class-trees-buffer-list ()
+  "Return a list of buffers containing class trees.
+The list will contain, for each class tree loaded,
+one buffer.  Prefer tree buffers over member buffers."
+  (let ((buffers (nconc (ebrowse-tree-buffer-list)
+                       (ebrowse-member-buffer-list)))
+       (set (make-hash-table))
+       result)
+    (dolist (buffer buffers)
+      (let ((tree (ebrowse-value-in-buffer 'ebrowse--tree buffer)))
+       (unless (gethash tree set)
+         (push buffer result))
+       (puthash tree t set)))
+    result))
+
+
+(defun ebrowse-same-tree-member-buffer-list ()
+  "Return a list of members buffers with same tree as current buffer."
+  (ebrowse-delete-if-not
+   #'(lambda (buffer)
+       (eq (ebrowse-value-in-buffer 'ebrowse--tree buffer)
+          ebrowse--tree))
+   (ebrowse-member-buffer-list)))
+
+
+\f
+(defun ebrowse-pop/switch-to-member-buffer-for-same-tree (arg)
+  "Pop to the buffer displaying members.
+Switch to buffer if prefix ARG.
+If no member buffer exists, make one."
+  (interactive "P")
+  (let ((buf (or (first (ebrowse-same-tree-member-buffer-list))
+                (get-buffer ebrowse-member-buffer-name)
+                (ebrowse-tree-command:show-member-functions))))
+    (when buf
+      (if arg
+         (switch-to-buffer buf)
+       (pop-to-buffer buf)))
+    buf))
+
+
+(defun ebrowse-switch-to-next-member-buffer ()
+  "Switch to next member buffer."
+  (interactive)
+  (let* ((list (ebrowse-member-buffer-list))
+        (next-list (cdr (memq (current-buffer) list)))
+        (next-buffer (if next-list (car next-list) (car list))))
+    (if (eq next-buffer (current-buffer))
+       (error "No next buffer")
+      (bury-buffer)
+      (switch-to-buffer next-buffer))))
+
+
+(defun ebrowse-kill-member-buffers-displaying (tree)
+  "Kill all member buffers displaying TREE."
+  (loop for buffer in (ebrowse-member-buffer-list)
+       as class = (ebrowse-value-in-buffer 'ebrowse--displayed-class buffer)
+       when (eq class tree) do (kill-buffer buffer)))
+
+
+(defun ebrowse-frozen-tree-buffer-name (tags-file-name)
+  "Return the buffer name of a tree which is associated TAGS-FILE-NAME."
+  (concat ebrowse-tree-buffer-name " (" tags-file-name ")"))
+
+
+(defun ebrowse-pop-to-browser-buffer (arg)
+  "Pop to a browser buffer from any other buffer.
+Pop to member buffer if no prefix ARG, to tree buffer otherwise."
+  (interactive "P")
+  (let ((buffer (get-buffer (if arg 
+                               ebrowse-tree-buffer-name
+                             ebrowse-member-buffer-name))))
+    (unless buffer
+      (setq buffer 
+           (get-buffer (if arg
+                           ebrowse-member-buffer-name
+                         ebrowse-tree-buffer-name))))
+    (unless buffer
+      (error "No browser buffer found"))
+    (pop-to-buffer buffer)))
+
+
+\f
+;;; Misc tree buffer commands
+
+(defun ebrowse-set-tree-indentation ()
+  "Set the indentation width of the tree display."
+  (interactive)
+  (let ((width (string-to-int (read-from-minibuffer
+                              (concat "Indentation ("
+                                      (int-to-string ebrowse--indentation)
+                                      "): ")))))
+    (when (plusp width)
+      (setf ebrowse--indentation width)
+      (ebrowse-redraw-tree))))
+
+
+(defun ebrowse-read-class-name-and-go (&optional class)
+  "Position cursor on CLASS.
+Read a class name from the minibuffer if CLASS is nil."
+  (interactive)
+  (ebrowse-ignoring-completion-case
+    ;; If no class specified, read the class name from mini-buffer
+    (unless class
+      (setf class
+           (completing-read "Goto class: "
+                            (ebrowse-tree-obarray-as-alist) nil t)))
+    (ebrowse-save-selective
+      (goto-char (point-min))
+      (widen)
+      (setf selective-display nil)
+      (setq ebrowse--last-regexp (concat "\\b" class "\\b"))
+      (if (re-search-forward ebrowse--last-regexp nil t)
+         (progn
+           (goto-char (match-beginning 0))
+           (ebrowse-unhide-base-classes))
+       (error "Not found")))))
+
+
+\f
+;;; Showing various kinds of member buffers
+
+(defun ebrowse-tree-command:show-member-variables (arg)
+  "Display member variables; with prefix ARG in frozen member buffer."
+  (interactive "P")
+  (ebrowse-display-member-buffer 'ebrowse-ts-member-variables arg))
+
+
+(defun ebrowse-tree-command:show-member-functions (&optional arg)
+  "Display member functions; with prefix ARG in frozen member buffer."
+  (interactive "P")
+  (ebrowse-display-member-buffer 'ebrowse-ts-member-functions arg))
+
+
+(defun ebrowse-tree-command:show-static-member-variables (arg)
+  "Display static member variables; with prefix ARG in frozen member buffer."
+  (interactive "P")
+  (ebrowse-display-member-buffer 'ebrowse-ts-static-variables arg))
+
+
+(defun ebrowse-tree-command:show-static-member-functions (arg)
+  "Display static member functions; with prefix ARG in frozen member buffer."
+  (interactive "P")
+  (ebrowse-display-member-buffer 'ebrowse-ts-static-functions arg))
+
+
+(defun ebrowse-tree-command:show-friends (arg)
+  "Display friend functions; with prefix ARG in frozen member buffer."
+  (interactive "P")
+  (ebrowse-display-member-buffer 'ebrowse-ts-friends arg))
+
+
+(defun ebrowse-tree-command:show-types (arg)
+  "Display types defined in a class; with prefix ARG in frozen member buffer."
+  (interactive "P")
+  (ebrowse-display-member-buffer 'ebrowse-ts-types arg))
+
+
+\f
+;;; Viewing or finding a class declaration
+
+(defun ebrowse-tree-at-point ()
+  "Return the class structure for the class point is on."
+  (or (get-text-property (point) 'ebrowse-tree)
+      (error "Not on a class")))
+
+
+(defun* ebrowse-view/find-class-declaration (&key view where)
+  "View or find the declarator of the class point is on.
+VIEW non-nil means view it.  WHERE is additional position info."
+  (let* ((class (ebrowse-ts-class (ebrowse-tree-at-point)))
+        (file (ebrowse-cs-file class))
+        (browse-struct (make-ebrowse-bs
+                        :name (ebrowse-cs-name class)
+                        :pattern (ebrowse-cs-pattern class)
+                        :flags (ebrowse-cs-flags class)
+                        :file (ebrowse-cs-file class)
+                        :point (ebrowse-cs-point class))))
+    (ebrowse-view/find-file-and-search-pattern
+     browse-struct 
+     (list ebrowse--header class nil)
+     file
+     ebrowse--tags-file-name
+     view
+     where)))
+
+
+(defun ebrowse-find-class-declaration (prefix-arg)
+  "Find a class declaration and position cursor on it.
+PREFIX-ARG 4 means find it in another window.
+PREFIX-ARG 5 means find it in another frame."
+  (interactive "p")
+  (ebrowse-view/find-class-declaration
+   :view nil
+   :where (cond ((= prefix-arg 4) 'other-window)
+               ((= prefix-arg 5) 'other-frame)
+               (t                'this-window))))
+
+
+(defun ebrowse-view-class-declaration (prefix-arg)
+  "View class declaration and position cursor on it.
+PREFIX-ARG 4 means view it in another window.
+PREFIX-ARG 5 means view it in another frame."
+  (interactive "p")
+  (ebrowse-view/find-class-declaration
+   :view 'view
+   :where (cond ((= prefix-arg 4) 'other-window)
+               ((= prefix-arg 5) 'other-frame)
+               (t                'this-window))))
+
+
+\f
+;;; The FIND engine
+
+(defun ebrowse-find-source-file (file tags-file-name)
+  "Find source file FILE.
+Source files are searched for (a) relative to TAGS-FILE-NAME
+which is the path of the BROWSE file from which the class tree was loaded,
+and (b) in the directories named in `ebrowse-search-path'."
+  (let (file-name
+       (try-file (expand-file-name file
+                                   (file-name-directory tags-file-name))))
+    (if (file-readable-p try-file)
+       (setq file-name try-file)
+      (let ((search-in ebrowse-search-path))
+       (while (and search-in
+                   (null file-name))
+         (let ((try-file (expand-file-name file (car search-in))))
+           (if (file-readable-p try-file)
+               (setq file-name try-file))
+           (setq search-in (cdr search-in))))))
+    (unless file-name
+      (error "File `%s' not found" file))
+    file-name))
+
+
+(defun ebrowse-view-file-other-window (file)
+  "View a file FILE in another window.
+This is a replacement for `view-file-other-window' which does not
+seem to work. It should be removed when `view.el' is fixed."
+  (interactive)
+  (let ((old-arrangement (current-window-configuration))
+       (had-a-buf (get-file-buffer file))
+       (buf-to-view (find-file-noselect file)))
+    (switch-to-buffer-other-window buf-to-view)
+    (view-mode-enter old-arrangement
+                    (and (not had-a-buf)
+                         (not (buffer-modified-p buf-to-view))
+                         'kill-buffer))))
+
+
+(defun ebrowse-view-exit-fn (buffer)
+  "Function called when exiting View mode in BUFFER.
+Restore frame configuration active before viewing the file,
+and possibly kill the viewed buffer."
+  (let (exit-action original-frame-configuration)
+    (save-excursion
+      (set-buffer buffer)
+      (setq original-frame-configuration ebrowse--frame-configuration
+           exit-action ebrowse--view-exit-action))
+    ;; Delete the frame in which we viewed.
+    (mapcar 'delete-frame
+           (loop for frame in (frame-list)
+                 when (not (assq frame original-frame-configuration))
+                 collect frame))
+    (when exit-action
+      (funcall exit-action buffer))))
+
+
+(defun ebrowse-view-file-other-frame (file)
+  "View a file FILE in another frame.
+The new frame is deleted when it is no longer used."
+  (interactive)
+  (let ((old-frame-configuration (current-frame-configuration))
+       (old-arrangement (current-window-configuration))
+       (had-a-buf (get-file-buffer file))
+       (buf-to-view (find-file-noselect file)))
+    (switch-to-buffer-other-frame buf-to-view)
+    (make-local-variable 'ebrowse--frame-configuration)
+    (setq ebrowse--frame-configuration old-frame-configuration)
+    (make-local-variable 'ebrowse--view-exit-action)
+    (setq ebrowse--view-exit-action 
+         (and (not had-a-buf)
+              (not (buffer-modified-p buf-to-view))
+              'kill-buffer))
+    (view-mode-enter old-arrangement 'ebrowse-view-exit-fn)))
+
+
+(defun ebrowse-view/find-file-and-search-pattern
+  (struc info file tags-file-name &optional view where)
+  "Find or view a member or class.
+STRUC is an `ebrowse-bs' structure (or a structure including that)
+describing what to search.
+INFO is a list (HEADER MEMBER-OR-CLASS ACCESSOR).  HEADER is the
+header structure of a class tree.  MEMBER-OR-CLASS is either an
+`ebrowse-ms' or `ebrowse-cs' structure depending on what is searched.
+ACCESSOR is an accessor function for the member list of an member
+if MEMBER-OR-CLASS is an `ebrowse-ms'.
+FILE is the file to search the member in.
+FILE is not taken out of STRUC here because the filename in STRUC
+may be nil in which case the filename of the class description is used.
+TAGS-FILE-NAME is the name of the EBROWSE file from which the
+tree was loaded.
+If VIEW is non-nil, view file else find the file.
+WHERE is either `other-window', `other-frame' or `this-window' and
+specifies where to find/view the result."
+  (unless file
+    (error "Sorry, no file information available for %s"
+          (ebrowse-bs-name struc)))
+  ;; Get the source file to view or find.
+  (setf file (ebrowse-find-source-file file tags-file-name))
+  ;; If current window is dedicated, use another frame.
+  (when (window-dedicated-p (selected-window))
+    (setf where 'other-frame))
+  (cond (view
+        (setf ebrowse-temp-position-to-view struc
+              ebrowse-temp-info-to-view info)
+        (unless (boundp 'view-mode-hook)
+          (setq view-mode-hook nil))
+        (push 'ebrowse-find-pattern view-mode-hook)
+        (case where
+          (other-window (ebrowse-view-file-other-window file))
+          (other-frame  (ebrowse-view-file-other-frame file))
+          (t            (view-file file))))
+       (t
+        (case where
+          (other-window (find-file-other-window file))
+          (other-frame  (find-file-other-frame file))
+          (t            (find-file file)))
+        (ebrowse-find-pattern struc info))))
+
+
+(defun ebrowse-symbol-regexp (name)
+  "Generate a suitable regular expression for a member or class NAME.
+This is `regexp-quote' for most symbols, except for operator names
+which may contain whitespace.  For these symbols, replace white
+space in the symbol name (generated by EBROWSE) with a regular
+expression matching any number of whitespace characters."
+  (loop with regexp = (regexp-quote name)
+       with start = 0
+       finally return regexp
+       while (string-match "[ \t]+" regexp start)
+       do (setf (substring regexp (match-beginning 0) (match-end 0))
+                "[ \t]*"
+                start (+ (match-beginning 0) 5))))
+
+
+(defun ebrowse-class-declaration-regexp (name)
+  "Construct a regexp for a declaration of class NAME." 
+  (concat "^[ \t]*\\(template[ \t\n]*<.*>\\)?"
+         "[ \t\n]*\\(class\\|struct\\|union\\).*\\S_"
+         (ebrowse-symbol-regexp name)
+         "\\S_"))
+
+
+(defun ebrowse-variable-declaration-regexp (name)
+  "Construct a regexp for matching a variable NAME."
+  (concat "\\S_" (ebrowse-symbol-regexp name) "\\S_"))
+
+
+(defun ebrowse-function-declaration/definition-regexp (name)
+  "Construct a regexp for matching a function NAME."
+  (concat "^[a-zA-Z0-9_:*&<>, \t]*\\S_"
+         (ebrowse-symbol-regexp name)
+         "[ \t\n]*("))
+
+
+(defun ebrowse-pp-define-regexp (name)
+  "Construct a regexp matching a define of NAME."
+  (concat "^[ \t]*#[ \t]*define[ \t]+" (regexp-quote name)))
+
+
+(defun* ebrowse-find-pattern (&optional position info &aux viewing)
+  "Find a pattern.
+
+This is a kluge: Ebrowse allows you to find or view a file containing
+a pattern.  To be able to do a search in a viewed buffer,
+`view-mode-hook' is temporarily set to this function;
+`ebrowse-temp-position-to-view' holds what to search for.
+
+INFO is a list (TREE-HEADER TREE-OR-MEMBER MEMBER-LIST)."
+  (unless position
+    (pop view-mode-hook)
+    (setf viewing t
+         position ebrowse-temp-position-to-view
+         info ebrowse-temp-info-to-view))
+  (widen)
+  (let* ((pattern (ebrowse-bs-pattern position))
+        (start (ebrowse-bs-point position))
+        (offset 100)
+        found)
+    (destructuring-bind (header class-or-member member-list) info
+      ;; If no pattern is specified, construct one from the member name.
+      (when (stringp pattern)
+       (setq pattern (concat "^.*" (regexp-quote pattern))))
+      ;; Construct a regular expression if none given.
+      (unless pattern
+       (typecase class-or-member
+         (ebrowse-ms
+          (case member-list
+            ((ebrowse-ts-member-variables
+              ebrowse-ts-static-variables
+              ebrowse-ts-types)
+             (setf pattern (ebrowse-variable-declaration-regexp
+                            (ebrowse-bs-name position))))
+            (otherwise
+             (if (ebrowse-define-p class-or-member)
+                 (setf pattern (ebrowse-pp-define-regexp (ebrowse-bs-name position)))
+               (setf pattern (ebrowse-function-declaration/definition-regexp
+                              (ebrowse-bs-name position)))))))
+         (ebrowse-cs
+          (setf pattern (ebrowse-class-declaration-regexp
+                         (ebrowse-bs-name position))))))
+      ;; Begin searching some OFFSET from the original point where the
+      ;; regular expression was found by the parse, and step forward.
+      ;; When there is no regular expression in the database and a
+      ;; member definition/declaration was not seen by the parser,
+      ;; START will be 0.
+      (when (and (boundp 'ebrowse-debug)
+                (symbol-value 'ebrowse-debug))
+       (y-or-n-p (format "start = %d" start))
+       (y-or-n-p pattern))
+      (setf found
+           (loop do (goto-char (max (point-min) (- start offset)))
+                 when (re-search-forward pattern (+ start offset) t) return t
+                 never (bobp)
+                 do (incf offset offset)))
+      (cond (found
+            (beginning-of-line)
+            (run-hooks 'ebrowse-view/find-hook))
+           ((numberp (ebrowse-bs-pattern position))
+            (goto-char start)
+            (if ebrowse-not-found-hook
+                (run-hooks 'ebrowse-not-found-hook)
+              (message "Not found")
+              (sit-for 2)))
+           (t
+            (if ebrowse-not-found-hook
+                (run-hooks 'ebrowse-not-found-hook)
+              (unless viewing
+                (error "Not found"))
+              (message "Not found")
+              (sit-for 2)))))))
+
+\f
+;;; Drawing the tree
+
+(defun ebrowse-redraw-tree (&optional quietly)
+  "Redisplay the complete tree.
+QUIETLY non-nil means don't display progress messages."
+  (interactive)
+  (or quietly (message "Displaying..."))
+  (save-excursion
+    (ebrowse-output
+      (erase-buffer)
+      (ebrowse-draw-tree-fn)))
+  (ebrowse-update-tree-buffer-mode-line)
+  (or quietly (message nil)))
+
+
+(defun ebrowse-set-mark-props (start end tree)
+  "Set text properties for class marker signs between START and END.
+TREE denotes the class shown."
+  (add-text-properties
+   start end
+   `(mouse-face highlight ebrowse-what mark ebrowse-tree ,tree
+               help-echo "double-mouse-1: mark/unmark"))
+  (ebrowse-set-face start end 'ebrowse-tree-mark-face))
+
+
+(defun* ebrowse-draw-tree-fn (&aux stack1 stack2 start)
+  "Display a single class and recursively it's subclasses.
+This function may look weird, but this is faster than recursion."
+  (setq stack1 (make-list (length ebrowse--tree) 0)
+       stack2 (ebrowse-copy-list ebrowse--tree))
+  (loop while stack2
+       as level = (pop stack1)
+       as tree = (pop stack2)
+       as class = (ebrowse-ts-class tree) do
+       (let ((start-of-line (point))
+             start-of-class-name end-of-class-name)
+         ;; Insert mark
+         (insert (if (ebrowse-ts-mark tree) ">" " "))
+         
+         ;; Indent and insert class name
+         (indent-to (+ (* level ebrowse--indentation)
+                       ebrowse-tree-left-margin))
+         (setq start (point))
+         (insert (ebrowse-qualified-class-name class))
+         
+         ;; If template class, add <>
+         (when (ebrowse-template-p class)
+           (insert "<>"))
+         (ebrowse-set-face start (point) (if (zerop level)
+                                             'ebrowse-root-class-face
+                                           'ebrowse-default-face))
+         (setf start-of-class-name start
+               end-of-class-name (point))
+         ;; If filenames are to be displayed...
+         (when ebrowse--show-file-names-flag
+           (indent-to ebrowse-source-file-column)
+           (setq start (point))
+           (insert "("
+                   (or (ebrowse-cs-file class)
+                       "unknown")
+                   ")")
+           (ebrowse-set-face start (point) 'ebrowse-file-name-face))
+         (ebrowse-set-mark-props start-of-line (1+ start-of-line) tree)
+         (add-text-properties
+          start-of-class-name end-of-class-name
+          `(mouse-face highlight ebrowse-what class-name
+                       ebrowse-tree ,tree
+                       help-echo "double-mouse-1: (un)expand tree; mouse-2: member functions, mouse-3: menu"))
+         (insert "\n"))
+       ;; Push subclasses, if any.
+       (when (ebrowse-ts-subclasses tree)
+         (setq stack2
+               (nconc (ebrowse-copy-list (ebrowse-ts-subclasses tree)) stack2)
+               stack1
+               (nconc (make-list (length (ebrowse-ts-subclasses tree))
+                                 (1+ level)) stack1)))))
+
+
+\f
+;;; Expanding/ collapsing tree branches
+
+(defun ebrowse-expand-branch (arg)
+  "Expand a sub-tree that has been previously collapsed.
+With prefix ARG, expand all sub-trees."
+  (interactive "P")
+  (if arg
+      (ebrowse-expand-all arg)
+    (ebrowse-collapse-fn nil)))
+
+
+(defun ebrowse-collapse-branch (arg)
+  "Fold (do no longer display) the subclasses of the current class.
+\(The class cursor is on.)  With prefix ARG, fold all trees in the buffer."
+  (interactive "P")
+  (if arg
+      (ebrowse-expand-all (not arg))
+    (ebrowse-collapse-fn t)))
+
+
+(defun ebrowse-expand-all (collapse)
+  "Expand or fold all trees in the buffer.
+COLLAPSE non-nil means fold them."
+  (interactive "P")
+  (let ((line-end  (if collapse "^\n" "^\r"))
+       (insertion (if collapse "\r"  "\n")))
+    (ebrowse-output
+      (save-excursion
+       (goto-char (point-min))
+       (while (not (progn (skip-chars-forward line-end) (eobp)))
+         (when (or (not collapse)
+                   (looking-at "\n "))
+           (delete-char 1)
+           (insert insertion))
+         (when collapse
+           (skip-chars-forward "\n ")))))))
+
+
+(defun ebrowse-unhide-base-classes ()
+  "Unhide the line the cursor is on and all base classes."
+  (ebrowse-output
+    (save-excursion
+      (let (indent last-indent)
+       (skip-chars-backward "^\r\n")
+       (when (not (looking-at "[\r\n][^ \t]"))
+         (skip-chars-forward "\r\n \t")
+         (while (and (or (null last-indent) ;first time
+                         (> indent 1)) ;not root class
+                     (re-search-backward "[\r\n][ \t]*" nil t))
+           (setf indent (- (match-end 0)
+                           (match-beginning 0)))
+           (when (or (null last-indent)
+                     (< indent last-indent))
+             (setf last-indent indent)
+             (when (looking-at "\r")
+               (delete-char 1)
+               (insert 10)))
+           (backward-char 1)))))))
+
+
+(defun ebrowse-hide-line (collapse)
+  "Hide/show a single line in the tree.
+COLLAPSE non-nil means hide."
+  (save-excursion
+    (ebrowse-output
+      (skip-chars-forward "^\r\n")
+      (delete-char 1)
+      (insert (if collapse 13 10)))))
+
+
+(defun ebrowse-collapse-fn (collapse)
+  "Collapse or expand a branch of the tree.
+COLLAPSE non-nil means collapse the branch."
+  (ebrowse-output
+    (save-excursion
+      (beginning-of-line)
+      (skip-chars-forward "> \t")
+      (let ((indentation (current-column)))
+       (while (and (not (eobp))
+                   (save-excursion
+                     (skip-chars-forward "^\r\n")
+                     (goto-char (1+ (point)))
+                     (skip-chars-forward "> \t")
+                     (> (current-column) indentation)))
+         (ebrowse-hide-line collapse)
+         (skip-chars-forward "^\r\n")
+         (goto-char (1+ (point))))))))
+
+\f
+;;; Electric tree selection
+
+(defvar ebrowse-electric-list-mode-map ()
+  "Keymap used in electric Ebrowse buffer list window.")
+
+
+(unless ebrowse-electric-list-mode-map
+  (let ((map (make-keymap))
+       (submap (make-keymap)))
+    (setq ebrowse-electric-list-mode-map map)
+    (fillarray (car (cdr map)) 'ebrowse-electric-list-undefined)
+    (fillarray (car (cdr submap)) 'ebrowse-electric-list-undefined)
+    (define-key map "\e" submap)
+    (define-key map "\C-z" 'suspend-emacs)
+    (define-key map "\C-h" 'Helper-help)
+    (define-key map "?" 'Helper-describe-bindings)
+    (define-key map "\C-c" nil)
+    (define-key map "\C-c\C-c" 'ebrowse-electric-list-quit)
+    (define-key map "q" 'ebrowse-electric-list-quit)
+    (define-key map " " 'ebrowse-electric-list-select)
+    (define-key map "\C-l" 'recenter)
+    (define-key map "\C-u" 'universal-argument)
+    (define-key map "\C-p" 'previous-line)
+    (define-key map "\C-n" 'next-line)
+    (define-key map "p" 'previous-line)
+    (define-key map "n" 'next-line)
+    (define-key map "v" 'ebrowse-electric-view-buffer)
+    (define-key map "\C-v" 'scroll-up)
+    (define-key map "\ev" 'scroll-down)
+    (define-key map "\e\C-v" 'scroll-other-window)
+    (define-key map "\e>" 'end-of-buffer)
+    (define-key map "\e<" 'beginning-of-buffer)
+    (define-key map "\e>" 'end-of-buffer)))
+
+(put 'ebrowse-electric-list-mode 'mode-class 'special)
+(put 'ebrowse-electric-list-undefined 'suppress-keymap t)
+
+
+(defun ebrowse-electric-list-mode ()
+  "Mode for electric tree list mode."
+  (kill-all-local-variables)
+  (use-local-map ebrowse-electric-list-mode-map)
+  (setq mode-name "Electric Position Menu"
+       mode-line-buffer-identification "Electric Tree Menu")
+  (when (memq 'mode-name mode-line-format)
+    (setq mode-line-format (copy-sequence mode-line-format))
+    (setcar (memq 'mode-name mode-line-format) "Tree Buffers"))
+  (make-local-variable 'Helper-return-blurb)
+  (setq Helper-return-blurb "return to buffer editing"
+       truncate-lines t
+       buffer-read-only t
+       major-mode 'ebrowse-electric-list-mode)
+  (run-hooks 'ebrowse-electric-list-mode-hook))
+
+
+(defun ebrowse-list-tree-buffers ()
+  "Display a list of all tree buffers."
+  (set-buffer (get-buffer-create "*Tree Buffers*"))
+  (setq buffer-read-only nil)
+  (erase-buffer)
+  (insert "Tree\n" "----\n")
+  (dolist (buffer (ebrowse-known-class-trees-buffer-list))
+    (insert (buffer-name buffer) "\n"))
+  (setq buffer-read-only t))
+
+
+;;;###autoload
+(defun ebrowse-electric-choose-tree ()
+  "Return a buffer containing a tree or nil if no tree found or canceled."
+  (interactive)
+  (unless (car (ebrowse-known-class-trees-buffer-list))
+    (error "No tree buffers"))
+  (let (select buffer window)
+    (save-window-excursion
+      (save-window-excursion (ebrowse-list-tree-buffers))
+      (setq window (Electric-pop-up-window "*Tree Buffers*")
+           buffer (window-buffer window))
+      (shrink-window-if-larger-than-buffer window)
+      (unwind-protect
+         (progn
+           (set-buffer buffer)
+           (ebrowse-electric-list-mode)
+           (setq select
+                 (catch 'ebrowse-electric-list-select
+                   (message "<<< Press Space to bury the list >>>")
+                   (let ((first (progn (goto-char (point-min))
+                                       (forward-line 2)
+                                       (point)))
+                         (last (progn (goto-char (point-max))
+                                      (forward-line -1)
+                                      (point)))
+                         (goal-column 0))
+                     (goto-char first)
+                     (Electric-command-loop 'ebrowse-electric-list-select
+                                            nil
+                                            t
+                                            'ebrowse-electric-list-looper
+                                            (cons first last))))))
+       (set-buffer buffer)
+       (bury-buffer buffer)
+       (message nil)))
+    (when select
+      (set-buffer buffer)
+      (setq select (ebrowse-electric-get-buffer select)))
+    (kill-buffer buffer)
+    select))
+
+
+(defun ebrowse-electric-list-looper (state condition)
+  "Prevent cursor from moving beyond the buffer end.
+Don't let it move into the title lines.
+See 'Electric-command-loop' for a description of STATE and CONDITION."
+  (cond ((and condition
+             (not (memq (car condition)
+                        '(buffer-read-only end-of-buffer
+                                           beginning-of-buffer))))
+        (signal (car condition) (cdr condition)))
+       ((< (point) (car state))
+        (goto-char (point-min))
+        (forward-line 2))
+       ((> (point) (cdr state))
+        (goto-char (point-max))
+        (forward-line -1)
+        (if (pos-visible-in-window-p (point-max))
+            (recenter -1)))))
+
+
+(defun ebrowse-electric-list-undefined ()
+  "Function called for keys that are undefined."
+  (interactive)
+  (message "Type C-h for help, ? for commands, q to quit, Space to select.")
+  (sit-for 4))
+
+
+(defun ebrowse-electric-list-quit ()
+  "Discard the buffer list."
+  (interactive)
+  (throw 'ebrowse-electric-list-select nil))
+
+
+(defun ebrowse-electric-list-select ()
+  "Select a buffer from the buffer list."
+  (interactive)
+  (throw 'ebrowse-electric-list-select (point)))
+
+
+(defun ebrowse-electric-get-buffer (point)
+  "Get a buffer corresponding to the line POINT is in."
+  (let ((index (- (count-lines (point-min) point) 2)))
+    (nth index (ebrowse-known-class-trees-buffer-list))))
+
+
+;;; View a buffer for a tree.
+
+(defun ebrowse-electric-view-buffer ()
+  "View buffer point is on."
+  (interactive)
+  (let ((buffer (ebrowse-electric-get-buffer (point))))
+    (cond (buffer
+          (view-buffer buffer))
+         (t
+          (error "Buffer no longer exists")))))
+
+
+(defun ebrowse-choose-from-browser-buffers ()
+  "Read a browser buffer name from the minibuffer and return that buffer."
+  (let* ((buffers (ebrowse-known-class-trees-buffer-list)))
+    (if buffers
+       (if (not (second buffers))
+           (first buffers)
+         (or (ebrowse-electric-choose-tree) (error "No tree buffer")))
+      (let* ((insert-default-directory t)
+            (file (read-file-name "Find tree: " nil nil t)))
+       (save-excursion
+         (find-file file))
+       (find-buffer-visiting file)))))
+
+\f
+;;; Member buffers
+
+(unless ebrowse-member-mode-map
+  (let ((map (make-keymap)))
+    (setf ebrowse-member-mode-map map)
+    (suppress-keymap map)
+
+  (when window-system
+    (define-key map [down-mouse-3] 'ebrowse-member-mouse-3)
+    (define-key map [mouse-2] 'ebrowse-member-mouse-2))
+
+  (let ((map1 (make-sparse-keymap)))
+    (suppress-keymap map1 t)
+    (define-key map "C" map1)
+    (define-key map1 "b" 'ebrowse-switch-member-buffer-to-base-class)
+    (define-key map1 "c" 'ebrowse-switch-member-buffer-to-any-class)
+    (define-key map1 "d" 'ebrowse-switch-member-buffer-to-derived-class)
+    (define-key map1 "n" 'ebrowse-switch-member-buffer-to-next-sibling-class)
+    (define-key map1 "p" 'ebrowse-switch-member-buffer-to-previous-sibling-class))
+    
+  (let ((map1 (make-sparse-keymap)))
+    (suppress-keymap map1 t)
+    (define-key map "D" map1)
+    (define-key map1 "a" 'ebrowse-toggle-member-attributes-display)
+    (define-key map1 "b" 'ebrowse-toggle-base-class-display)
+    (define-key map1 "f" 'ebrowse-freeze-member-buffer)
+    (define-key map1 "l" 'ebrowse-toggle-long-short-display)
+    (define-key map1 "r" 'ebrowse-toggle-regexp-display)
+    (define-key map1 "w" 'ebrowse-set-member-buffer-column-width))
+    
+  (let ((map1 (make-sparse-keymap)))
+    (suppress-keymap map1 t)
+    (define-key map "F" map1)
+    (let ((map2 (make-sparse-keymap)))
+      (suppress-keymap map2 t)
+      (define-key map1 "a" map2)
+      (define-key map2 "i" 'ebrowse-toggle-private-member-filter)
+      (define-key map2 "o" 'ebrowse-toggle-protected-member-filter)
+      (define-key map2 "u" 'ebrowse-toggle-public-member-filter))
+    (define-key map1 "c" 'ebrowse-toggle-const-member-filter)
+    (define-key map1 "i" 'ebrowse-toggle-inline-member-filter)
+    (define-key map1 "p" 'ebrowse-toggle-pure-member-filter)
+    (define-key map1 "r" 'ebrowse-remove-all-member-filters)
+    (define-key map1 "v" 'ebrowse-toggle-virtual-member-filter))
+    
+  (let ((map1 (make-sparse-keymap)))
+    (suppress-keymap map1 t)
+    (define-key map "L" map1)
+    (define-key map1 "d" 'ebrowse-display-friends-member-list)
+    (define-key map1 "f" 'ebrowse-display-function-member-list)
+    (define-key map1 "F" 'ebrowse-display-static-functions-member-list)
+    (define-key map1 "n" 'ebrowse-display-next-member-list)
+    (define-key map1 "p" 'ebrowse-display-previous-member-list)
+    (define-key map1 "t" 'ebrowse-display-types-member-list)
+    (define-key map1 "v" 'ebrowse-display-variables-member-list)
+    (define-key map1 "V" 'ebrowse-display-static-variables-member-list))
+    
+  (let ((map1 (make-sparse-keymap)))
+    (suppress-keymap map1 t)
+    (define-key map "G" map1)
+    (define-key map1 "m" 'ebrowse-goto-visible-member/all-member-lists)
+    (define-key map1 "n" 'ebrowse-repeat-member-search)
+    (define-key map1 "v" 'ebrowse-goto-visible-member))
+    
+  (define-key map "f" 'ebrowse-find-member-declaration)
+  (define-key map "m" 'ebrowse-switch-to-next-member-buffer)
+  (define-key map "q" 'bury-buffer)
+  (define-key map "t" 'ebrowse-show-displayed-class-in-tree)
+  (define-key map "v" 'ebrowse-view-member-declaration)
+  (define-key map " " 'ebrowse-view-member-definition)
+  (define-key map "?" 'describe-mode)
+  (define-key map "\C-i" 'ebrowse-pop-from-member-to-tree-buffer)
+  (define-key map "\C-l" 'ebrowse-redisplay-member-buffer)
+  (define-key map "\C-m" 'ebrowse-find-member-definition)))
+
+
+\f
+;;; Member mode
+
+;;###autoload
+(defun ebrowse-member-mode ()
+  "Major mode for Ebrowse member buffers.
+
+\\{ebrowse-member-mode-map}"
+  (kill-all-local-variables)
+  (use-local-map ebrowse-member-mode-map)
+  (setq major-mode 'ebrowse-member-mode)
+  (mapcar 'make-local-variable
+         '(ebrowse--decl-column        ;display column
+           ebrowse--n-columns          ;number of short columns
+           ebrowse--column-width       ;width of columns above
+           ebrowse--show-inherited-flag ;include inherited members?
+           ebrowse--filters            ;public, protected, private
+           ebrowse--accessor           ;vars, functions, friends
+           ebrowse--displayed-class    ;class displayed
+           ebrowse--long-display-flag  ;display with regexps?
+           ebrowse--source-regexp-flag ;show source regexp?
+           ebrowse--attributes-flag    ;show `virtual' and `inline'
+           ebrowse--member-list        ;list of members displayed
+           ebrowse--tree               ;the class tree
+           ebrowse--member-mode-strings ;part of mode line
+           ebrowse--tags-file-name     ;
+           ebrowse--header
+           ebrowse--tree-obarray
+           ebrowse--virtual-display-flag
+           ebrowse--inline-display-flag
+           ebrowse--const-display-flag
+           ebrowse--pure-display-flag
+           ebrowse--mode-line-props
+           ebrowse--frozen-flag))      ;buffer not automagically reused
+  (setq ebrowse--mode-line-props (text-properties-at
+                                 0 (car (default-value
+                                         'mode-line-buffer-identification)))
+       mode-name "Ebrowse-Members"
+       mode-line-buffer-identification 'ebrowse--member-mode-strings
+       buffer-read-only t
+       ebrowse--long-display-flag nil
+       ebrowse--attributes-flag t
+       ebrowse--show-inherited-flag t
+       ebrowse--source-regexp-flag nil
+       ebrowse--filters [0 1 2]
+       ebrowse--decl-column ebrowse-default-declaration-column
+       ebrowse--column-width ebrowse-default-column-width
+       ebrowse--virtual-display-flag nil
+       ebrowse--inline-display-flag nil
+       ebrowse--const-display-flag nil
+       ebrowse--pure-display-flag nil)
+  (modify-syntax-entry ?_ (char-to-string (char-syntax ?a)))
+  (run-hooks 'ebrowse-member-mode-hook))
+
+
+\f
+;;; Member mode mode line
+
+(defsubst ebrowse-class-name-displayed-in-member-buffer ()
+  "Return the name of the class displayed in the member buffer."
+  (ebrowse-cs-name (ebrowse-ts-class ebrowse--displayed-class)))
+
+
+(defsubst ebrowse-member-list-name ()
+  "Return a string describing what is displayed in the member buffer."
+  (get ebrowse--accessor (if (ebrowse-globals-tree-p ebrowse--displayed-class)
+                            'ebrowse-global-title
+                          'ebrowse-title)))
+
+
+(defun ebrowse-update-member-buffer-mode-line ()
+  "Update the mode line of member buffers."
+  (let* ((name (when ebrowse--frozen-flag
+                (concat (ebrowse-class-name-displayed-in-member-buffer)
+                        " ")))
+        (ident (concat name (ebrowse-member-list-name))))
+    (setq ebrowse--member-mode-strings
+         (apply #'propertize ident ebrowse--mode-line-props))
+    (ebrowse-rename-buffer (if name ident ebrowse-member-buffer-name))
+    (force-mode-line-update)))
+
+
+;;; Misc member buffer commands
+
+(defun ebrowse-freeze-member-buffer ()
+  "Toggle frozen status of current buffer."
+  (interactive)
+  (setq ebrowse--frozen-flag (not ebrowse--frozen-flag))
+  (ebrowse-redisplay-member-buffer))
+
+
+(defun ebrowse-show-displayed-class-in-tree (arg)
+  "Show the currently displayed class in the tree window.
+With prefix ARG, switch to the tree buffer else pop to it."
+  (interactive "P")
+  (let ((class-name (ebrowse-class-name-displayed-in-member-buffer)))
+    (when (ebrowse-pop-from-member-to-tree-buffer arg)
+      (ebrowse-read-class-name-and-go class-name))))
+
+
+(defun ebrowse-set-member-buffer-column-width ()
+  "Set the column width of the member display.
+The new width is read from the minibuffer."
+  (interactive)
+  (let ((width (string-to-int
+               (read-from-minibuffer
+                (concat "Column width ("
+                        (int-to-string (if ebrowse--long-display-flag
+                                           ebrowse--decl-column
+                                         ebrowse--column-width))
+                        "): ")))))
+    (when (plusp width)
+      (if ebrowse--long-display-flag
+         (setq ebrowse--decl-column width)
+       (setq ebrowse--column-width width))
+      (ebrowse-redisplay-member-buffer))))
+
+
+(defun ebrowse-pop-from-member-to-tree-buffer (arg)
+  "Pop from a member buffer to the matching tree buffer.
+Switch to the buffer if prefix ARG.  If no tree buffer exists,
+make one."
+  (interactive "P")
+  (let ((buf (or (get-buffer (ebrowse-frozen-tree-buffer-name
+                             ebrowse--tags-file-name))
+                (get-buffer ebrowse-tree-buffer-name)
+                (ebrowse-create-tree-buffer ebrowse--tree
+                                            ebrowse--tags-file-name
+                                            ebrowse--header
+                                            ebrowse--tree-obarray
+                                            'pop))))
+    (and buf
+        (funcall (if arg 'switch-to-buffer 'pop-to-buffer) buf))
+    buf))
+
+
+\f
+;;; Switching between member lists
+
+(defun ebrowse-display-member-list-for-accessor (accessor)
+  "Switch the member buffer to display the member list for ACCESSOR."
+  (setf ebrowse--accessor accessor
+       ebrowse--member-list (funcall accessor ebrowse--displayed-class))
+  (ebrowse-redisplay-member-buffer))
+
+
+(defun ebrowse-cyclic-display-next/previous-member-list (incr)
+  "Switch buffer to INCR'th next/previous list of members."
+  (let ((index (ebrowse-position ebrowse--accessor
+                                ebrowse-member-list-accessors)))
+    (setf ebrowse--accessor
+         (cond ((plusp incr)
+                (or (nth (1+ index)
+                         ebrowse-member-list-accessors)
+                    (first ebrowse-member-list-accessors)))
+               ((minusp incr)
+                (or (and (>= (decf index) 0)
+                         (nth index
+                              ebrowse-member-list-accessors))
+                    (first (last ebrowse-member-list-accessors))))))
+    (ebrowse-display-member-list-for-accessor ebrowse--accessor)))
+
+
+(defun ebrowse-display-next-member-list ()
+  "Switch buffer to next member list."
+  (interactive)
+  (ebrowse-cyclic-display-next/previous-member-list 1))
+
+
+(defun ebrowse-display-previous-member-list ()
+  "Switch buffer to previous member list."
+  (interactive)
+  (ebrowse-cyclic-display-next/previous-member-list -1))
+
+
+(defun ebrowse-display-function-member-list ()
+  "Display the list of member functions."
+  (interactive)
+  (ebrowse-display-member-list-for-accessor 'ebrowse-ts-member-functions))
+
+
+(defun ebrowse-display-variables-member-list ()
+  "Display the list of member variables."
+  (interactive)
+  (ebrowse-display-member-list-for-accessor 'ebrowse-ts-member-variables))
+
+
+(defun ebrowse-display-static-variables-member-list ()
+  "Display the list of static member variables."
+  (interactive)
+  (ebrowse-display-member-list-for-accessor 'ebrowse-ts-static-variables))
+
+
+(defun ebrowse-display-static-functions-member-list ()
+  "Display the list of static member functions."
+  (interactive)
+  (ebrowse-display-member-list-for-accessor 'ebrowse-ts-static-functions))
+
+
+(defun ebrowse-display-friends-member-list ()
+  "Display the list of friends."
+  (interactive)
+  (ebrowse-display-member-list-for-accessor 'ebrowse-ts-friends))
+
+
+(defun ebrowse-display-types-member-list ()
+  "Display the list of types."
+  (interactive)
+  (ebrowse-display-member-list-for-accessor 'ebrowse-ts-types))
+
+
+\f
+;;; Filters and other display attributes
+
+(defun ebrowse-toggle-member-attributes-display ()
+  "Toggle display of `virtual', `inline', `const' etc."
+  (interactive)
+  (setq ebrowse--attributes-flag (not ebrowse--attributes-flag))
+  (ebrowse-redisplay-member-buffer))
+
+
+(defun ebrowse-toggle-base-class-display ()
+  "Toggle the display of members inherited from base classes."
+  (interactive)
+  (setf ebrowse--show-inherited-flag (not ebrowse--show-inherited-flag))
+  (ebrowse-redisplay-member-buffer))
+
+
+(defun ebrowse-toggle-pure-member-filter ()
+  "Toggle display of pure virtual members."
+  (interactive)
+  (setf ebrowse--pure-display-flag (not ebrowse--pure-display-flag))
+  (ebrowse-redisplay-member-buffer))
+
+
+(defun ebrowse-toggle-const-member-filter ()
+  "Toggle display of const members."
+  (interactive)
+  (setf ebrowse--const-display-flag (not ebrowse--const-display-flag))
+  (ebrowse-redisplay-member-buffer))
+
+
+(defun ebrowse-toggle-inline-member-filter ()
+  "Toggle display of inline members."
+  (interactive)
+  (setf ebrowse--inline-display-flag (not ebrowse--inline-display-flag))
+  (ebrowse-redisplay-member-buffer))
+
+
+(defun ebrowse-toggle-virtual-member-filter ()
+  "Toggle display of virtual members."
+  (interactive)
+  (setf ebrowse--virtual-display-flag (not ebrowse--virtual-display-flag))
+  (ebrowse-redisplay-member-buffer))
+
+
+(defun ebrowse-remove-all-member-filters ()
+  "Remove all filters."
+  (interactive)
+  (dotimes (i 3)
+    (aset ebrowse--filters i i))
+  (setq ebrowse--pure-display-flag nil
+       ebrowse--const-display-flag nil
+       ebrowse--virtual-display-flag nil
+       ebrowse--inline-display-flag nil)
+  (ebrowse-redisplay-member-buffer))
+
+
+(defun ebrowse-toggle-public-member-filter ()
+  "Toggle visibility of public members."
+  (interactive)
+  (ebrowse-set-member-access-visibility 0)
+  (ebrowse-redisplay-member-buffer))
+
+
+(defun ebrowse-toggle-protected-member-filter ()
+  "Toggle visibility of protected members."
+  (interactive)
+  (ebrowse-set-member-access-visibility 1)
+  (ebrowse-redisplay-member-buffer))
+
+
+(defun ebrowse-toggle-private-member-filter ()
+  "Toggle visibility of private members."
+  (interactive)
+  (ebrowse-set-member-access-visibility 2)
+  (ebrowse-redisplay-member-buffer))
+
+
+(defun ebrowse-set-member-access-visibility (vis)
+  (setf (aref ebrowse--filters vis)
+       (if (aref ebrowse--filters vis) nil vis)))
+
+
+(defun ebrowse-toggle-long-short-display ()
+  "Toggle between long and short display form of member buffers."
+  (interactive)
+  (setf ebrowse--long-display-flag (not ebrowse--long-display-flag))
+  (ebrowse-redisplay-member-buffer))
+
+
+(defun ebrowse-toggle-regexp-display ()
+  "Toggle declaration/definition regular expression display.
+Used in member buffers showing the long display form."
+  (interactive)
+  (setf ebrowse--source-regexp-flag (not ebrowse--source-regexp-flag))
+  (ebrowse-redisplay-member-buffer))
+
+
+\f
+;;; Viewing/finding members
+
+(defun ebrowse-find-member-definition (&optional prefix)
+  "Find the file containing a member definition.
+With PREFIX 4. find file in another window, with prefix 5
+find file in another frame."
+  (interactive "p")
+  (ebrowse-view/find-member-declaration/definition prefix nil t))
+
+
+(defun ebrowse-view-member-definition (prefix)
+  "View the file containing a member definition.
+With PREFIX 4. find file in another window, with prefix 5
+find file in another frame."
+  (interactive "p")
+  (ebrowse-view/find-member-declaration/definition prefix t t))
+
+
+(defun ebrowse-find-member-declaration (prefix)
+  "Find the file containing a member's declaration.
+With PREFIX 4. find file in another window, with prefix 5
+find file in another frame."
+  (interactive "p")
+  (ebrowse-view/find-member-declaration/definition prefix nil))
+
+
+(defun ebrowse-view-member-declaration (prefix)
+  "View the file containing a member's declaration.
+With PREFIX 4. find file in another window, with prefix 5
+find file in another frame."
+  (interactive "p")
+  (ebrowse-view/find-member-declaration/definition prefix t))
+
+
+(defun* ebrowse-view/find-member-declaration/definition
+    (prefix view &optional definition info header tags-file-name)
+  "Find or view a member declaration or definition.
+With PREFIX 4. find file in another window, with prefix 5
+find file in another frame.
+DEFINITION non-nil means find the definition, otherwise find the
+declaration.
+INFO is a list (TREE ACCESSOR MEMBER) describing the member to
+search.
+TAGS-FILE-NAME is the file name of the EBROWSE file."
+  (unless header
+    (setq header ebrowse--header))
+  (unless tags-file-name
+    (setq tags-file-name ebrowse--tags-file-name))
+  (let (tree member accessor file on-class
+            (where (if (= prefix 4) 'other-window
+                     (if (= prefix 5) 'other-frame 'this-window))))
+    ;; If not given as parameters, get the necessary information
+    ;; out of the member buffer.
+    (if info
+       (setq tree (first info)
+             accessor (second info)
+             member (third info))
+      (multiple-value-setq (tree member on-class)
+       (ebrowse-member-info-from-point))
+      (setq accessor ebrowse--accessor))
+    ;; View/find class if on a line containing a class name.
+    (when on-class
+      (return-from ebrowse-view/find-member-declaration/definition
+       (ebrowse-view/find-file-and-search-pattern
+        (ebrowse-ts-class tree)
+        (list ebrowse--header (ebrowse-ts-class tree) nil)
+        (ebrowse-cs-file (ebrowse-ts-class tree))
+        tags-file-name view where)))
+    ;; For some member lists, it doesn't make sense to search for
+    ;; a definition. If this is requested, silently search for the
+    ;; declaration.
+    (when (and definition
+              (eq accessor 'ebrowse-ts-member-variables))
+      (setq definition nil))
+    ;; Construct a suitable `browse' struct for definitions.
+    (when definition
+      (setf member (make-ebrowse-ms
+                   :name (ebrowse-ms-name member)
+                   :file (ebrowse-ms-definition-file member)
+                   :pattern (ebrowse-ms-definition-pattern
+                             member)
+                   :flags (ebrowse-ms-flags member)
+                   :point (ebrowse-ms-definition-point
+                           member))))
+    ;; When no file information in member, use that of the class
+    (setf file (or (ebrowse-ms-file member)
+                  (if definition
+                      (ebrowse-cs-source-file (ebrowse-ts-class tree))
+                    (ebrowse-cs-file (ebrowse-ts-class tree)))))
+    ;; When we have no regular expressions in the database the only
+    ;; indication that the parser hasn't seen a definition/declaration
+    ;; is that the search start point will be zero.
+    (if (or (null file) (zerop (ebrowse-ms-point member)))
+       (if (y-or-n-p (concat "No information about "
+                             (if definition "definition" "declaration")
+                             ".  Search for "
+                             (if definition "declaration" "definition")
+                             " of `"
+                             (ebrowse-ms-name member)
+                             "'? "))
+           (progn
+             (message nil)
+             ;; Recurse with new info.
+             (ebrowse-view/find-member-declaration/definition
+              prefix view (not definition) info header tags-file-name))
+         (error "Search canceled"))
+      ;; Find that thing.
+      (ebrowse-view/find-file-and-search-pattern
+       (make-ebrowse-bs :name (ebrowse-ms-name member)
+                       :pattern (ebrowse-ms-pattern member)
+                       :file (ebrowse-ms-file member)
+                       :flags (ebrowse-ms-flags member)
+                       :point (ebrowse-ms-point member))
+       (list header member accessor)
+       file
+       tags-file-name
+       view
+       where))))
+
+
+\f
+;;; Drawing the member buffer
+
+(defun ebrowse-redisplay-member-buffer ()
+  "Force buffer redisplay."
+  (interactive)
+  (let ((display-fn (if ebrowse--long-display-flag
+                       'ebrowse-draw-member-long-fn
+                     'ebrowse-draw-member-short-fn)))
+    (ebrowse-output
+      (erase-buffer)
+      ;; Show this class
+      (ebrowse-draw-member-buffer-class-line)
+      (funcall display-fn ebrowse--member-list ebrowse--displayed-class)
+      ;; Show inherited members if corresponding switch is on
+      (when ebrowse--show-inherited-flag
+       (dolist (super (ebrowse-base-classes ebrowse--displayed-class))
+         (goto-char (point-max))
+         (insert (if (bolp) "\n\n" "\n"))
+         (ebrowse-draw-member-buffer-class-line super)
+         (funcall display-fn (funcall ebrowse--accessor super) super)))
+      (ebrowse-update-member-buffer-mode-line))))
+
+
+(defun ebrowse-draw-member-buffer-class-line (&optional class)
+  "Display the title line for a class section in the member buffer.
+CLASS non-nil means display that class' title.  Otherwise use
+the class cursor is on."
+  (let ((start (point))
+       (tree  (or class ebrowse--displayed-class))
+       class-name-start 
+       class-name-end)
+    (insert "class ")
+    (setq class-name-start (point))
+    (insert (ebrowse-qualified-class-name (ebrowse-ts-class tree)))
+    (when (ebrowse-template-p (ebrowse-ts-class tree))
+      (insert "<>"))
+    (setq class-name-end (point))
+    (insert ":\n\n")
+    (ebrowse-set-face start (point) 'ebrowse-member-class-face)
+    (add-text-properties
+     class-name-start class-name-end
+     '(ebrowse-what class-name
+                   mouse-face highlight
+                   help-echo "mouse-3: menu"))
+    (put-text-property start class-name-end 'ebrowse-tree tree)))
+
+
+(defun ebrowse-display-member-buffer (list &optional stand-alone class)
+  "Start point for member buffer creation.
+LIST is the member list to display.  STAND-ALONE non-nil
+means the member buffer is standalone.  CLASS is its class."
+  (let* ((classes ebrowse--tree-obarray)
+        (tree ebrowse--tree)
+        (tags-file-name ebrowse--tags-file-name)
+        (header ebrowse--header)
+        temp-buffer-setup-hook
+        (temp-buffer (get-buffer ebrowse-member-buffer-name)))
+    ;; Get the class description from the name the cursor
+    ;; is on if not specified as an argument.
+    (unless class
+      (setq class (ebrowse-tree-at-point)))
+    (with-output-to-temp-buffer ebrowse-member-buffer-name
+      (save-excursion
+       (set-buffer standard-output)
+       ;; If new buffer, set the mode and initial values of locals
+       (unless temp-buffer
+         (ebrowse-member-mode))
+       ;; Set local variables
+       (setq ebrowse--member-list (funcall list class)
+             ebrowse--displayed-class class
+             ebrowse--accessor list
+             ebrowse--tree-obarray classes
+             ebrowse--frozen-flag stand-alone
+             ebrowse--tags-file-name tags-file-name
+             ebrowse--header header
+             ebrowse--tree tree
+             buffer-read-only t)
+       (ebrowse-redisplay-member-buffer)
+       (current-buffer)))))
+
+
+(defun ebrowse-member-display-p (member)
+  "Return t if MEMBER must be displayed under the current filter settings."
+  (if (and (aref ebrowse--filters (ebrowse-ms-visibility member))
+          (or (null ebrowse--const-display-flag)
+              (ebrowse-const-p member))
+          (or (null ebrowse--inline-display-flag)
+              (ebrowse-inline-p member))
+          (or (null ebrowse--pure-display-flag)
+              (ebrowse-bs-p member))
+          (or (null ebrowse--virtual-display-flag)
+              (ebrowse-virtual-p member)))
+      member))
+
+
+(defun ebrowse-draw-member-attributes (member)
+  "Insert a string for the attributes of MEMBER."
+  (insert (if (ebrowse-template-p member) "T" "-")
+         (if (ebrowse-extern-c-p member) "C" "-")
+         (if (ebrowse-virtual-p member) "v" "-")
+         (if (ebrowse-inline-p member) "i" "-")
+         (if (ebrowse-const-p member) "c" "-")
+         (if (ebrowse-pure-virtual-p member) "0" "-")
+         (if (ebrowse-mutable-p member) "m" "-")
+         (if (ebrowse-explicit-p member) "e" "-")
+         (if (ebrowse-throw-list-p member) "t" "-")))
+
+
+(defun ebrowse-draw-member-regexp (member-struc)
+  "Insert a string for the regular expression matching MEMBER-STRUC."
+  (let ((pattern (if ebrowse--source-regexp-flag
+                    (ebrowse-ms-definition-pattern
+                     member-struc)
+                  (ebrowse-ms-pattern member-struc))))
+    (cond ((stringp pattern)
+          (insert (ebrowse-trim-string pattern) "...\n")
+          (beginning-of-line 0)
+          (move-to-column (+ 4 ebrowse--decl-column))
+          (while (re-search-forward "[ \t]+" nil t)
+            (delete-region (match-beginning 0) (match-end 0))
+            (insert " "))
+          (beginning-of-line 2))
+         (t
+          (insert "[not recorded or unknown]\n")))))
+
+
+(defun ebrowse-draw-member-long-fn (member-list tree)
+  "Display member buffer for MEMBER-LIST in long form.
+TREE is the class tree of MEMBER-LIST."
+  (dolist (member-struc (mapcar 'ebrowse-member-display-p member-list))
+    (when member-struc
+      (let ((name (ebrowse-ms-name member-struc))
+           (start (point)))
+       ;; Insert member name truncated to the right length
+       (insert (substring name
+                          0
+                          (min (length name)
+                               (1- ebrowse--decl-column))))
+       (add-text-properties
+        start (point)
+        `(mouse-face highlight ebrowse-what member-name
+                     ebrowse-member ,member-struc
+                     ebrowse-tree ,tree
+                     help-echo "mouse-2: view definition; mouse-3: menu"))
+       ;; Display virtual, inline, and const status
+       (setf start (point))
+       (indent-to ebrowse--decl-column)
+       (put-text-property start (point) 'mouse-face nil)
+       (when ebrowse--attributes-flag
+         (let ((start (point)))
+           (insert "<")
+           (ebrowse-draw-member-attributes member-struc)
+           (insert ">")
+           (ebrowse-set-face start (point)
+                             'ebrowse-member-attribute-face)))
+       (insert " ")
+       (ebrowse-draw-member-regexp member-struc))))
+  (insert "\n")
+  (goto-char (point-min)))
+
+
+(defun ebrowse-draw-member-short-fn (member-list tree)
+  "Display MEMBER-LIST in short form.
+TREE is the class tree in which the members are found."
+  (let ((i 0)
+       (column-width (+ ebrowse--column-width
+                        (if ebrowse--attributes-flag 12 0))))
+    ;; Get the number of columns to draw.
+    (setq ebrowse--n-columns
+         (max 1 (/ (ebrowse-width-of-drawable-area) column-width)))
+    (dolist (member (mapcar #'ebrowse-member-display-p member-list))
+      (when member
+       (let ((name (ebrowse-ms-name member))
+             start-of-entry
+             (start-of-column (point))
+             start-of-name)
+         (indent-to (* i column-width))
+         (put-text-property start-of-column (point) 'mouse-face nil)
+         (setq start-of-entry (point))
+         ;; Show various attributes 
+         (when ebrowse--attributes-flag
+           (insert "<")
+           (ebrowse-draw-member-attributes member)
+           (insert "> ")
+           (ebrowse-set-face start-of-entry (point)
+                             'ebrowse-member-attribute-face))
+         ;; insert member name truncated to column width
+         (setq start-of-name (point))
+         (insert (substring name 0
+                            (min (length name)
+                                 (1- ebrowse--column-width))))
+         ;; set text properties
+         (add-text-properties
+          start-of-name (point)
+          `(ebrowse-what member-name
+                         ebrowse-member ,member
+                         mouse-face highlight
+                         ebrowse-tree ,tree
+                         help-echo "mouse-2: view definition; mouse-3: menu"))
+         (incf i)
+         (when (>= i ebrowse--n-columns)
+           (setf i 0)
+           (insert "\n")))))
+    (when (plusp i)
+      (insert "\n"))
+    (goto-char (point-min))))
+
+
+\f
+;;; Killing members from tree
+
+(defun ebrowse-member-info-from-point ()
+  "Ger information about the member at point.
+The result has the form (TREE MEMBER NULL-P).  TREE is the tree
+we're in, MEMBER is the member we're on.  NULL-P is t if MEMBER
+is nil."
+  (let ((tree (or (get-text-property (point) 'ebrowse-tree)
+                 (error "No information at point")))
+       (member (get-text-property (point) 'ebrowse-member)))
+    (list tree member (null member))))
+
+
+\f
+;;; Switching member buffer to display a selected member
+
+(defun ebrowse-goto-visible-member/all-member-lists (prefix)
+  "Position cursor on a member read from the minibuffer.
+With PREFIX, search all members in the tree.  Otherwise consider
+only members visible in the buffer."
+  (interactive "p")
+  (ebrowse-ignoring-completion-case
+    (let* ((completion-list (ebrowse-name/accessor-alist-for-class-members))
+          (member (completing-read "Goto member: " completion-list nil t))
+          (accessor (cdr (assoc member completion-list))))
+      (unless accessor
+       (error "`%s' not found" member))
+      (unless (eq accessor ebrowse--accessor)
+       (setf ebrowse--accessor accessor
+             ebrowse--member-list (funcall accessor ebrowse--displayed-class))
+       (ebrowse-redisplay-member-buffer))
+      (ebrowse-move-point-to-member member))))
+
+
+(defun ebrowse-goto-visible-member (repeat)
+  "Position point on a member.
+Read the member's name from the minibuffer.  Consider only members
+visible in the member buffer.
+REPEAT non-nil means repeat the search that number of times."
+  (interactive "p")
+  (ebrowse-ignoring-completion-case
+    ;; Read member name
+    (let* ((completion-list (ebrowse-name/accessor-alist-for-visible-members))
+          (member (completing-read "Goto member: " completion-list nil t)))
+      (ebrowse-move-point-to-member member repeat))))
+
+
+\f
+;;; Searching a member in the member buffer
+
+(defun ebrowse-repeat-member-search (repeat)
+  "Repeat the last regular expression search.
+REPEAT, if specified, says repeat the search REPEAT times."
+  (interactive "p")
+  (unless ebrowse--last-regexp
+    (error "No regular expression remembered"))
+  ;; Skip over word the point is on
+  (skip-chars-forward "^ \t\n")
+  ;; Search for regexp from point
+  (if (re-search-forward ebrowse--last-regexp nil t repeat)
+      (progn
+       (goto-char (match-beginning 0))
+       (skip-chars-forward " \t\n"))
+    ;; If not found above, repeat search from buffer start
+    (goto-char (point-min))
+    (if (re-search-forward ebrowse--last-regexp nil t)
+       (progn
+         (goto-char (match-beginning 0))
+         (skip-chars-forward " \t\n"))
+      (error "Not found"))))
+
+
+(defun* ebrowse-move-point-to-member (name &optional count &aux member)
+  "Set point on member NAME in the member buffer
+COUNT, if specified, says search the COUNT'th member with the same name."
+  (goto-char (point-min))
+  (widen)
+  (setq member
+       (substring name 0 (min (length name) (1- ebrowse--column-width)))
+       ebrowse--last-regexp
+       (concat "[ \t\n]" (regexp-quote member) "[ \n\t]"))
+  (if (re-search-forward ebrowse--last-regexp nil t count)
+      (goto-char (1+ (match-beginning 0)))
+    (error "Not found")))
+
+
+\f
+;;; Switching member buffer to another class.
+
+(defun ebrowse-switch-member-buffer-to-other-class (title compl-list)
+  "Switch member buffer to a class read from the minibuffer.
+Use TITLE as minibuffer prompt.
+COMPL-LIST is a completion list to use."
+  (let* ((initial (unless (second compl-list)
+                   (first (first compl-list))))
+        (class (or (ebrowse-completing-read-value title compl-list initial)
+                   (error "Not found"))))
+    (setf ebrowse--displayed-class class
+         ebrowse--member-list (funcall ebrowse--accessor ebrowse--displayed-class))
+    (ebrowse-redisplay-member-buffer)))
+
+
+(defun ebrowse-switch-member-buffer-to-any-class ()
+  "Switch member buffer to a class read from the minibuffer."
+  (interactive)
+  (ebrowse-switch-member-buffer-to-other-class
+   "Goto class: " (ebrowse-tree-obarray-as-alist)))
+
+
+(defun ebrowse-switch-member-buffer-to-base-class (arg)
+  "Switch buffer to ARG'th base class."
+  (interactive "P")
+  (let ((supers (or (ebrowse-direct-base-classes ebrowse--displayed-class)
+                   (error "No base classes"))))
+    (if (and arg (second supers))
+       (let ((alist (loop for s in supers
+                          collect (cons (ebrowse-qualified-class-name
+                                         (ebrowse-ts-class s))
+                                        s))))
+         (ebrowse-switch-member-buffer-to-other-class
+          "Goto base class: " alist))
+      (setq ebrowse--displayed-class (first supers)
+           ebrowse--member-list
+           (funcall ebrowse--accessor ebrowse--displayed-class))
+      (ebrowse-redisplay-member-buffer))))
+
+(defun ebrowse-switch-member-buffer-to-next-sibling-class (arg)
+  "Move to ARG'th next sibling."
+  (interactive "p")
+  (ebrowse-switch-member-buffer-to-sibling-class arg))
+
+
+(defun ebrowse-switch-member-buffer-to-previous-sibling-class (arg)
+  "Move to ARG'th previous sibling."
+  (interactive "p")
+  (ebrowse-switch-member-buffer-to-sibling-class (- arg)))
+
+
+(defun ebrowse-switch-member-buffer-to-sibling-class (inc)
+  "Switch member display to nth sibling class.
+Prefix arg INC specifies which one."
+  (interactive "p")
+  (let ((containing-list ebrowse--tree)
+       index cls
+       (supers (ebrowse-direct-base-classes ebrowse--displayed-class)))
+    (flet ((trees-alist (trees)
+                       (loop for tr in trees
+                             collect (cons (ebrowse-cs-name
+                                            (ebrowse-ts-class tr)) tr))))
+      (when supers
+       (let ((tree (if (second supers)
+                       (ebrowse-completing-read-value
+                        "Relative to base class: "
+                        (trees-alist supers) nil)
+                     (first supers))))
+         (unless tree (error "Not found"))
+         (setq containing-list (ebrowse-ts-subclasses tree)))))
+    (setq index (+ inc (ebrowse-position ebrowse--displayed-class
+                                        containing-list)))
+    (cond ((minusp index) (message "No previous class"))
+         ((null (nth index containing-list)) (message "No next class")))
+    (setq index (max 0 (min index (1- (length containing-list)))))
+    (setq cls (nth index containing-list))
+    (setf ebrowse--displayed-class cls
+         ebrowse--member-list (funcall ebrowse--accessor cls))
+    (ebrowse-redisplay-member-buffer)))
+
+
+(defun ebrowse-switch-member-buffer-to-derived-class (arg)
+  "Switch member display to nth derived class.
+Prefix arg ARG says which class should be displayed.  Default is
+the first derived class."
+  (interactive "P")
+  (flet ((ebrowse-tree-obarray-as-alist () 
+                                       (loop for s in (ebrowse-ts-subclasses
+                                                       ebrowse--displayed-class)
+                                             collect (cons (ebrowse-cs-name
+                                                            (ebrowse-ts-class s)) s))))
+    (let ((subs (or (ebrowse-ts-subclasses ebrowse--displayed-class)
+                   (error "No derived classes"))))
+      (if (and arg (second subs))
+         (ebrowse-switch-member-buffer-to-other-class
+          "Goto derived class: " (ebrowse-tree-obarray-as-alist))
+       (setq ebrowse--displayed-class (first subs)
+             ebrowse--member-list
+             (funcall ebrowse--accessor ebrowse--displayed-class))
+       (ebrowse-redisplay-member-buffer)))))
+
+
+\f
+;;; Member buffer mouse functions
+
+(defun ebrowse-displaying-functions ()
+  (eq ebrowse--accessor 'ebrowse-ts-member-functions))
+(defun ebrowse-displaying-variables ()
+  (eq ebrowse--accessor 'ebrowse-ts-member-variables))
+(defun ebrowse-displaying-static-functions ()
+  )
+(defun ebrowse-displaying-static-variables ()
+  )
+(defun ebrowse-displaying-types ()
+  (eq ebrowse--accessor 'ebrowse-ts-types))
+(defun ebrowse-displaying-friends ()
+  (eq ebrowse--accessor 'ebrowse-ts-friends))
+
+(easy-menu-define
+ ebrowse-member-buffer-object-menu ebrowse-member-mode-map
+ "Object menu for the member buffer itself."
+ '("Members"
+   ("Members List"
+    ["Functions" ebrowse-display-function-member-list
+     :help "Show the list of member functions"
+     :style radio
+     :selected (eq ebrowse--accessor 'ebrowse-ts-member-functions)
+     :active t]
+    ["Variables" ebrowse-display-variables-member-list
+     :help "Show the list of member variables"
+     :style radio
+     :selected (eq ebrowse--accessor 'ebrowse-ts-member-variables)
+     :active t]
+    ["Static Functions" ebrowse-display-static-functions-member-list
+     :help "Show the list of static member functions"
+     :style radio
+     :selected (eq ebrowse--accessor 'ebrowse-ts-static-functions)
+     :active t]
+    ["Static Variables" ebrowse-display-static-variables-member-list
+     :help "Show the list of static member variables"
+     :style radio
+     :selected (eq ebrowse--accessor 'ebrowse-ts-static-variables)
+     :active t]
+    ["Types" ebrowse-display-types-member-list
+     :help "Show the list of nested types"
+     :style radio
+     :selected (eq ebrowse--accessor 'ebrowse-ts-types)
+     :active t]
+    ["Friends/Defines" ebrowse-display-friends-member-list
+     :help "Show the list of friends or defines"
+     :style radio
+     :selected (eq ebrowse--accessor 'ebrowse-ts-friends)
+     :active t])
+   ("Class"
+    ["Up" ebrowse-switch-member-buffer-to-base-class
+     :help "Show the base class of this class"
+     :active t]
+    ["Down" ebrowse-switch-member-buffer-to-derived-class
+     :help "Show a derived class class of this class"
+     :active t]
+    ["Next Sibling" ebrowse-switch-member-buffer-to-next-sibling-class
+     :help "Show the next sibling class"
+     :active t]
+    ["Previous Sibling" ebrowse-switch-member-buffer-to-previous-sibling-class
+     :help "Show the previous sibling class"
+     :active t])
+   ("Member"
+    ["Show in Tree" ebrowse-show-displayed-class-in-tree
+     :help "Show this class in the class tree"
+     :active t]
+    ["Find in this Class" ebrowse-goto-visible-member
+     :help "Search for a member of this class"
+     :active t]
+    ["Find in Tree" ebrowse-goto-visible-member/all-member-lists
+     :help "Search for a member in any class"
+     :active t])
+   ("Display" 
+    ["Inherited" ebrowse-toggle-base-class-display
+     :help "Toggle display of inherited members"
+     :style toggle
+     :selected ebrowse--show-inherited-flag
+     :active t]
+    ["Attributes" ebrowse-toggle-member-attributes-display
+     :help "Show member attributes"
+     :style toggle
+     :selected ebrowse--attributes-flag
+     :active t]
+    ["Long Display" ebrowse-toggle-long-short-display
+     :help "Toggle the member display format"
+     :style toggle
+     :selected ebrowse--long-display-flag
+     :active t]
+    ["Column Width" ebrowse-set-member-buffer-column-width
+     :help "Set the display's column width"
+     :active t])
+   ("Filter"
+    ["Public" ebrowse-toggle-public-member-filter
+     :help "Toggle the visibility of public members"
+     :style toggle
+     :selected (not (aref ebrowse--filters 0))
+     :active t]
+    ["Protected" ebrowse-toggle-protected-member-filter
+     :help "Toggle the visibility of protected members"
+     :style toggle
+     :selected (not (aref ebrowse--filters 1))
+     :active t]
+    ["Private" ebrowse-toggle-private-member-filter
+     :help "Toggle the visibility of private members"
+     :style toggle
+     :selected (not (aref ebrowse--filters 2))
+     :active t]
+    ["Virtual" ebrowse-toggle-virtual-member-filter
+     :help "Toggle the visibility of virtual members"
+     :style toggle
+     :selected ebrowse--virtual-display-flag
+     :active t]
+    ["Inline" ebrowse-toggle-inline-member-filter
+     :help "Toggle the visibility of inline members"
+     :style toggle
+     :selected ebrowse--inline-display-flag
+     :active t]
+    ["Const" ebrowse-toggle-const-member-filter
+     :help "Toggle the visibility of const members"
+     :style toggle
+     :selected ebrowse--const-display-flag
+     :active t]
+    ["Pure" ebrowse-toggle-pure-member-filter
+     :help "Toggle the visibility of pure virtual members"
+     :style toggle
+     :selected ebrowse--pure-display-flag
+     :active t]
+    "-----------------"
+    ["Show all" ebrowse-remove-all-member-filters
+     :help "Remove any display filters"
+     :active t])
+   ("Buffer"
+    ["Tree" ebrowse-pop-from-member-to-tree-buffer
+     :help "Pop to the class tree buffer"
+     :active t]
+    ["Next Member Buffer" ebrowse-switch-to-next-member-buffer
+     :help "Switch to the next member buffer of this class tree"
+     :active t]
+    ["Freeze" ebrowse-freeze-member-buffer
+     :help "Freeze (do not reuse) this member buffer"
+     :active t])))
+
+
+(defun ebrowse-on-class-name ()
+  "Value is non-nil if point is on a class name."
+  (eq (get-text-property (point) 'ebrowse-what) 'class-name))
+
+
+(defun ebrowse-on-member-name ()
+  "Value is non-nil if point is on a member name."
+  (eq (get-text-property (point) 'ebrowse-what) 'member-name))
+
+
+(easy-menu-define
+ ebrowse-member-class-name-object-menu ebrowse-member-mode-map
+ "Object menu for class names in member buffer."
+ '("Class"
+   ["Find" ebrowse-find-member-definition
+    :help "Find this class in the source files"
+    :active (eq (get-text-property (point) 'ebrowse-what) 'class-name)]
+   ["View" ebrowse-view-member-definition
+    :help "View this class in the source files"
+    :active (eq (get-text-property (point) 'ebrowse-what) 'class-name)]))
+
+
+(easy-menu-define 
+ ebrowse-member-name-object-menu ebrowse-member-mode-map
+ "Object menu for member names"
+ '("Ebrowse"
+   ["Find Definition" ebrowse-find-member-definition
+    :help "Find this member's definition in the source files"
+    :active (ebrowse-on-member-name)]
+   ["Find Declaration" ebrowse-find-member-declaration
+    :help "Find this member's declaration in the source files"
+    :active (ebrowse-on-member-name)]
+   ["View Definition" ebrowse-view-member-definition
+    :help "View this member's definition in the source files"
+    :active (ebrowse-on-member-name)]
+   ["View Declaration" ebrowse-view-member-declaration
+    :help "View this member's declaration in the source files"
+    :active (ebrowse-on-member-name)]))
+
+
+(defun ebrowse-member-mouse-3 (event)
+  "Handle `mouse-3' events in member buffers.
+EVENT is the mouse event."
+  (interactive "e")
+  (mouse-set-point event)
+  (case (event-click-count event)
+    (2 (ebrowse-find-member-definition))
+    (1 (case (get-text-property (posn-point (event-start event))
+                               'ebrowse-what)
+        (member-name
+         (ebrowse-popup-menu ebrowse-member-name-object-menu event))
+        (class-name
+         (ebrowse-popup-menu ebrowse-member-class-name-object-menu event))
+        (t
+         (ebrowse-popup-menu ebrowse-member-buffer-object-menu event))))))
+
+
+(defun ebrowse-member-mouse-2 (event)
+  "Handle `mouse-2' events in member buffers.
+EVENT is the mouse event."
+  (interactive "e")
+  (mouse-set-point event)
+  (case (event-click-count event)
+    (2 (ebrowse-find-member-definition))
+    (1 (case (get-text-property (posn-point (event-start event))
+                               'ebrowse-what)
+        (member-name
+         (ebrowse-view-member-definition 0))))))
+
+
+\f
+;;; Tags view/find
+
+(defun ebrowse-class-alist-for-member (tree-header name)
+  "Return information about a member in a class tree.
+TREE-HEADER is the header structure of the class tree.
+NAME is the name of the member.
+Value is an alist of elements (CLASS-NAME . (CLASS LIST NAME)),
+where each element describes one occurrence of member NAME in the tree.
+CLASS-NAME is the qualified name of the class in which the 
+member was found.  The CDR of the acons is described in function
+`ebrowse-class/index/member-for-member'."
+  (let ((table (ebrowse-member-table tree-header))
+       known-classes
+       alist)
+    (when name
+      (dolist (info (gethash name table) alist)
+       (unless (memq (first info) known-classes)
+         (setf alist (acons (ebrowse-qualified-class-name
+                             (ebrowse-ts-class (first info)))
+                            info alist)
+               known-classes (cons (first info) known-classes)))))))
+
+
+(defun ebrowse-choose-tree ()
+  "Choose a class tree to use.
+If there's more than one class tree loaded, let the user choose
+the one he wants.  Value is (TREE HEADER BUFFER), with TREE being
+the class tree, HEADER the header structure of the tree, and BUFFER
+being the tree or member buffer containing the tree."
+  (let* ((buffer (ebrowse-choose-from-browser-buffers)))
+    (if buffer (list (ebrowse-value-in-buffer 'ebrowse--tree buffer)
+                    (ebrowse-value-in-buffer 'ebrowse--header buffer)
+                    buffer))))
+
+
+(defun ebrowse-tags-read-name (header prompt)
+  "Read a C++ identifier from the minibuffer.
+HEADER is the `ebrowse-hs' structure of the class tree.
+Prompt with PROMPT.  Insert into the minibuffer a C++ identifier read
+from point as default.  Value is a list (CLASS-NAME MEMBER-NAME)."
+  (save-excursion
+    (let* (start member-info (members (ebrowse-member-table header)))
+      (multiple-value-bind (class-name member-name)
+         (ebrowse-tags-read-member+class-name)
+       (unless member-name
+         (error "No member name at point"))
+       (if members
+           (let* ((alist (ebrowse-hash-table-to-alist members))
+                  (name (ebrowse-ignoring-completion-case
+                          (completing-read prompt alist nil nil member-name)))
+                  (completion-result (try-completion name alist)))
+             ;; Cannot rely on `try-completion' returning T for exact
+             ;; matches!  it returns the the name as a string.
+             (unless (setq member-info (gethash name members))
+               (if (y-or-n-p "No exact match found.  Try substrings? ")
+                   (setq name 
+                         (or (first (ebrowse-list-of-matching-members 
+                                     members (regexp-quote name) name))
+                             (error "Sorry, nothing found")))
+                 (error "Canceled")))
+             (list class-name name))
+         (list class-name (read-from-minibuffer prompt member-name)))))))
+
+
+(defun ebrowse-tags-read-member+class-name ()
+  "Read a C++ identifier from point.
+Value is (CLASS-NAME MEMBER-NAME).
+CLASS-NAME is the name of the class if the identifier was qualified.
+It is nil otherwise.
+MEMBER-NAME is the name of the member found."
+  (save-excursion
+    (skip-chars-backward "a-zA-Z0-9_")
+    (let* ((start (point))
+          (name (progn (skip-chars-forward "a-zA-Z0-9_")
+                       (buffer-substring start (point))))
+          class)
+      (list class name))))
+
+
+(defun ebrowse-tags-choose-class (tree header name initial-class-name)
+  "Read a class name for a member from the minibuffer.
+TREE is the class tree we operate on.
+HEADER is its header structure.
+NAME is the name of the member.
+INITIAL-CLASS-NAME is an initial class name to insert in the minibuffer.
+Value is a list (TREE ACCESSOR MEMBER) for the member."
+  (let ((alist (or (ebrowse-class-alist-for-member header name)
+                  (error "No classes with member `%s' found" name))))
+    (ebrowse-ignoring-completion-case
+      (if (null (second alist))
+         (cdr (first alist))
+       (push ?\? unread-command-events)
+       (cdr (assoc (completing-read "In class: "
+                                    alist nil t initial-class-name)
+                   alist))))))
+
+
+(defun* ebrowse-tags-view/find-member-decl/defn
+    (prefix &key view definition member-name)
+  "If VIEW is t, view, else find an occurrence of MEMBER-NAME.
+
+If DEFINITION is t, find or view the member definition else its
+declaration.  This function reads the member's name from the
+current buffer like FIND-TAG.  It then prepares a completion list
+of all classes containing a member with the given name and lets
+the user choose the class to use.  As a last step, a tags search
+is performed that positions point on the member declaration or
+definition."
+  (multiple-value-bind
+      (tree header tree-buffer) (ebrowse-choose-tree)
+    (unless tree (error "No class tree"))
+    (let* ((marker (point-marker))
+          class-name
+          (name member-name)
+          info)
+      (unless name
+       (multiple-value-setq (class-name name)
+         (ebrowse-tags-read-name
+          header
+          (concat (if view "View" "Find") " member "
+                  (if definition "definition" "declaration") ": "))))
+      (setq info (ebrowse-tags-choose-class tree header name class-name))
+      (ebrowse-push-position marker info)
+      ;; Goto the occurrence of the member
+      (ebrowse-view/find-member-declaration/definition
+       prefix view definition info
+       header
+       (ebrowse-value-in-buffer 'ebrowse--tags-file-name tree-buffer))
+      ;; Record position jumped to
+      (ebrowse-push-position (point-marker) info t))))
+
+
+;;###autoload
+(defun ebrowse-tags-view-declaration ()
+  "View declaration of member at point."
+  (interactive)
+  (ebrowse-tags-view/find-member-decl/defn 0 :view t :definition nil))
+
+
+;;###autoload
+(defun ebrowse-tags-find-declaration ()
+  "Find declaration of member at point."
+  (interactive)
+  (ebrowse-tags-view/find-member-decl/defn 0 :view nil :definition nil))
+
+
+;;###autoload
+(defun ebrowse-tags-view-definition ()
+  "View definition of member at point."
+  (interactive)
+  (ebrowse-tags-view/find-member-decl/defn 0 :view t :definition t))
+
+
+;;###autoload
+(defun ebrowse-tags-find-definition ()
+  "Find definition of member at point."
+  (interactive)
+  (ebrowse-tags-view/find-member-decl/defn 0 :view nil :definition t))
+
+
+(defun ebrowse-tags-view-declaration-other-window ()
+  "View declaration of member at point in other window."
+  (interactive)
+  (ebrowse-tags-view/find-member-decl/defn 4 :view t :definition nil))
+
+
+;;###autoload
+(defun ebrowse-tags-find-declaration-other-window ()
+  "Find declaration of member at point in other window."
+  (interactive)
+  (ebrowse-tags-view/find-member-decl/defn 4 :view nil :definition nil))
+
+
+;;###autoload
+(defun ebrowse-tags-view-definition-other-window ()
+  "View definition of member at point in other window."
+  (interactive)
+  (ebrowse-tags-view/find-member-decl/defn 4 :view t :definition t))
+
+
+;;###autoload
+(defun ebrowse-tags-find-definition-other-window ()
+  "Find definition of member at point in other window."
+  (interactive)
+  (ebrowse-tags-view/find-member-decl/defn 4 :view nil :definition t))
+
+
+(defun ebrowse-tags-view-declaration-other-frame ()
+  "View definition of member at point in other frame."
+  (interactive)
+  (ebrowse-tags-view/find-member-decl/defn 5 :view t :definition nil))
+
+
+;;###autoload
+(defun ebrowse-tags-find-declaration-other-frame ()
+  "Find definition of member at point in other frame."
+  (interactive)
+  (ebrowse-tags-view/find-member-decl/defn 5 :view nil :definition nil))
+
+
+;;###autoload
+(defun ebrowse-tags-view-definition-other-frame ()
+  "View definition of member at point in other frame."
+  (interactive)
+  (ebrowse-tags-view/find-member-decl/defn 5 :view t :definition t))
+
+
+;;###autoload
+(defun ebrowse-tags-find-definition-other-frame ()
+  "Find definition of member at point in other frame."
+  (interactive)
+  (ebrowse-tags-view/find-member-decl/defn 5 :view nil :definition t))
+
+
+(defun ebrowse-tags-select/create-member-buffer (tree-buffer info)
+  "Select or create member buffer.
+TREE-BUFFER specifies the tree to use.  INFO describes the member.
+It is a list (TREE ACCESSOR MEMBER)."
+  (let ((buffer (get-buffer ebrowse-member-buffer-name)))
+    (cond ((null buffer)
+          (set-buffer tree-buffer)
+          (switch-to-buffer (ebrowse-display-member-buffer
+                             (second info) nil (first info))))
+         (t
+          (switch-to-buffer buffer)
+          (setq ebrowse--displayed-class (first info)
+                ebrowse--accessor (second info)
+                ebrowse--member-list (funcall ebrowse--accessor ebrowse--displayed-class))
+          (ebrowse-redisplay-member-buffer)))
+    (ebrowse-move-point-to-member (ebrowse-ms-name (third info)))))
+
+
+(defun ebrowse-tags-display-member-buffer (&optional fix-name)
+  "Display a member buffer for a member.
+FIX-NAME non-nil means display the buffer for that member.
+Otherwise read a member name from point."
+  (interactive)
+  (multiple-value-bind
+      (tree header tree-buffer) (ebrowse-choose-tree)
+    (unless tree (error "No class tree"))
+    (let* ((marker (point-marker)) class-name (name fix-name) info)
+      (unless name
+       (multiple-value-setq (class-name name)
+         (ebrowse-tags-read-name header
+                                 (concat "Find member list of: "))))
+      (setq info (ebrowse-tags-choose-class tree header name class-name))
+      (ebrowse-push-position marker info)
+      (ebrowse-tags-select/create-member-buffer tree-buffer info))))
+
+
+(defun ebrowse-list-of-matching-members (members regexp &optional name)
+  "Return a list of members in table MEMBERS matching REGEXP or NAME.
+Both NAME and REGEXP may be nil in which case exact or regexp matches
+are not performed."
+  (let (list)
+    (when (or name regexp)
+      (maphash #'(lambda (member-name info)
+                  (when (or (and name (string= name member-name))
+                            (and regexp (string-match regexp member-name)))
+                    (setq list (cons member-name list))))
+              members))
+    list))
+
+
+(defun ebrowse-tags-apropos ()
+  "Display a list of members matching a regexp read from the minibuffer."
+  (interactive)
+  (let* ((buffer (or (ebrowse-choose-from-browser-buffers)
+                    (error "No tree buffer")))
+        (header (ebrowse-value-in-buffer 'ebrowse--header buffer))
+        (members (ebrowse-member-table header))
+        temp-buffer-setup-hook
+        (regexp (read-from-minibuffer "List members matching regexp: ")))
+    (with-output-to-temp-buffer (concat "*Apropos Members*")
+      (set-buffer standard-output)
+      (erase-buffer)
+      (insert "Members matching `" regexp "'\n\n")
+      (loop for s in (ebrowse-list-of-matching-members members regexp) do
+           (loop for info in (gethash s members) do
+                 (ebrowse-draw-file-member-info info))))))
+
+
+(defun ebrowse-tags-list-members-in-file ()
+  "Display a list of members found in a file.
+The file name is read from the minibuffer."
+  (interactive)
+  (let* ((buffer (or (ebrowse-choose-from-browser-buffers)
+                    (error "No tree buffer")))
+        (files (save-excursion (set-buffer buffer) (ebrowse-files-table)))
+        (alist (ebrowse-hash-table-to-alist files))
+        (file (completing-read "List members in file: " alist nil t))
+        (header (ebrowse-value-in-buffer 'ebrowse--header buffer))
+        temp-buffer-setup-hook
+        (members (ebrowse-member-table header)))
+    (with-output-to-temp-buffer (concat "*Members in file " file "*")
+      (set-buffer standard-output)
+      (maphash
+       #'(lambda (member-name list)
+          (loop for info in list
+                as member = (third info)
+                as class = (ebrowse-ts-class (first info))
+                when (or (and (null (ebrowse-ms-file member))
+                              (string= (ebrowse-cs-file class) file))
+                         (string= file (ebrowse-ms-file member)))
+                do (ebrowse-draw-file-member-info info "decl.")
+                when (or (and (null (ebrowse-ms-definition-file member))
+                              (string= (ebrowse-cs-source-file class) file))
+                         (string= file (ebrowse-ms-definition-file member)))
+                do (ebrowse-draw-file-member-info info "defn.")))
+       members))))
+
+
+(defun* ebrowse-draw-file-member-info (info &optional (kind ""))
+  "Display a line in an the members per file info buffer.
+INFO describes the member.  It has the form (TREE ACCESSOR MEMBER).
+TREE is the class of the member to display.
+ACCESSOR is the accessor symbol of its member list.
+MEMBER is the member structure.
+KIND is a an additional string printed in the buffer."
+  (let* ((tree (first info))
+        (globals-p (ebrowse-globals-tree-p tree)))
+    (unless globals-p
+      (insert (ebrowse-cs-name (ebrowse-ts-class tree))))
+    (insert "::" (ebrowse-ms-name (third info)))
+    (indent-to 40)
+    (insert kind)
+    (indent-to 50)
+    (insert (case (second info)
+             ('ebrowse-ts-member-functions "member function")
+             ('ebrowse-ts-member-variables "member variable")
+             ('ebrowse-ts-static-functions "static function")
+             ('ebrowse-ts-static-variables "static variable")
+             ('ebrowse-ts-friends (if globals-p "define" "friend"))
+             ('ebrowse-ts-types "type")
+             (t "unknown"))
+           "\n")))
+
+(defvar ebrowse-last-completion nil
+  "Text inserted by the last completion operation.")
+
+
+(defvar ebrowse-last-completion-start nil
+  "String which was the basis for the last completion operation.")
+
+
+(defvar ebrowse-last-completion-location nil
+  "Buffer position at which the last completion operation was initiated.")
+
+
+(defvar ebrowse-last-completion-obarray nil
+  "Member used in last completion operation.")
+
+
+(make-variable-buffer-local 'ebrowse-last-completion-obarray)
+(make-variable-buffer-local 'ebrowse-last-completion-location)
+(make-variable-buffer-local 'ebrowse-last-completion)
+(make-variable-buffer-local 'ebrowse-last-completion-start)
+
+
+\f
+(defun ebrowse-some-member-table ()
+  "Return a hash table containing all member of a tree.
+If there's only one tree loaded, use that.  Otherwise let the
+use choose a tree."
+  (let* ((buffers (ebrowse-known-class-trees-buffer-list))
+        (buffer (cond ((and (first buffers) (not (second buffers)))
+                       (first buffers))
+                      (t (or (ebrowse-electric-choose-tree)
+                             (error "No tree buffer")))))
+        (header (ebrowse-value-in-buffer 'ebrowse--header buffer)))
+    (ebrowse-member-table header)))
+
+
+(defun ebrowse-hash-table-to-alist (table)
+  "Return an alist holding all key/value pairs of hash table TABLE."
+  (let ((list))
+    (maphash #'(lambda (key value)
+                (setq list (cons (cons key value) list)))
+            table)
+    list))
+  
+
+(defun ebrowse-cyclic-successor-in-string-list (string list)
+  "Return the item following STRING in LIST.
+If STRING is the last element, return the first element as successor."
+  (or (nth (1+ (ebrowse-position string list 'string=)) list)
+      (first list)))
+
+\f
+;;; Symbol completion
+
+;;;###autoload
+(defun* ebrowse-tags-complete-symbol (prefix)
+  "Perform completion on the C++ symbol preceding point.
+A second call of this function without changing point inserts the next match. 
+A call with prefix PREFIX reads the symbol to insert from the minibuffer with
+completion."
+  (interactive "P")
+  (let* ((end (point))
+        (begin (save-excursion (skip-chars-backward "a-zA-Z_0-9") (point)))
+        (pattern (buffer-substring begin end))
+        list completion)
+    (cond
+     ;; With prefix, read name from minibuffer with completion.
+     (prefix
+      (let* ((members (ebrowse-some-member-table))
+            (alist (ebrowse-hash-table-to-alist members))
+            (completion (completing-read "Insert member: "
+                                         alist nil t pattern)))
+       (when completion
+         (setf ebrowse-last-completion-location nil)
+         (delete-region begin end)
+         (insert completion))))
+     ;; If this function is called at the same point the last
+     ;; expansion ended, insert the next expansion.
+     ((eq (point) ebrowse-last-completion-location)
+      (setf list (all-completions ebrowse-last-completion-start
+                                 ebrowse-last-completion-obarray)
+           completion (ebrowse-cyclic-successor-in-string-list
+                       ebrowse-last-completion list))
+      (cond ((null completion)
+            (error "No completion"))
+           ((string= completion pattern)
+            (error "No further completion"))
+           (t
+            (delete-region begin end)
+            (insert completion)
+            (setf ebrowse-last-completion completion
+                  ebrowse-last-completion-location (point)))))
+     ;; First time the function is called at some position in the
+     ;; buffer: Start new completion.
+     (t
+      (let* ((members (ebrowse-some-member-table))
+            (completion (first (all-completions pattern members nil))))
+       (cond ((eq completion t))
+             ((null completion)
+              (error "Can't find completion for `%s'" pattern))
+             (t
+              (delete-region begin end)
+              (insert completion)
+              
+              (setf ebrowse-last-completion-location (point)
+                    ebrowse-last-completion-start pattern
+                    ebrowse-last-completion completion
+                    ebrowse-last-completion-obarray members))))))))
+
+\f
+;;; Tags query replace & search
+
+(defvar ebrowse-tags-loop-form ()
+  "Form for `ebrowse-loop-continue'.
+Evaluated for each file in the tree.  If it returns nil, proceed
+with the next file.")
+
+(defvar ebrowse-tags-next-file-list ()
+  "A list of files to be processed.")
+
+
+(defvar ebrowse-tags-next-file-path nil
+  "The path relative to which files have to be searched.")
+
+
+(defvar ebrowse-tags-loop-last-file nil
+  "The last file visited via `ebrowse-tags-loop'.")
+
+
+(defun ebrowse-tags-next-file (&optional initialize tree-buffer)
+  "Select next file among files in current tag table.
+Non-nil argument INITIALIZE (prefix arg, if interactive) initializes
+to the beginning of the list of files in the tag table.
+TREE-BUFFER specifies the class tree we operate on."
+  (interactive "P")
+  ;; Call with INITIALIZE non-nil initializes the files list.
+  ;; If more than one tree buffer is loaded, let the user choose
+  ;; on which tree (s)he wants to operate.
+  (when initialize
+    (let ((buffer (or tree-buffer (ebrowse-choose-from-browser-buffers))))
+      (save-excursion
+       (set-buffer buffer)
+       (setq ebrowse-tags-next-file-list
+             (ebrowse-files-list (ebrowse-marked-classes-p))
+             ebrowse-tags-loop-last-file
+             nil
+             ebrowse-tags-next-file-path
+             (file-name-directory ebrowse--tags-file-name)))))
+  ;; End of the loop if the stack of files is empty.
+  (unless ebrowse-tags-next-file-list
+    (error "All files processed"))
+  ;; ebrowse-tags-loop-last-file is the last file that was visited due
+  ;; to a call to BROWSE-LOOP (see below). If that file is still
+  ;; in memory, and it wasn't modified, throw its buffer away to
+  ;; prevent cluttering up the buffer list.
+  (when ebrowse-tags-loop-last-file
+    (let ((buffer (get-file-buffer ebrowse-tags-loop-last-file)))
+      (when (and buffer
+                (not (buffer-modified-p buffer)))
+       (kill-buffer buffer))))
+  ;; Remember this buffer file name for later deletion, if it
+  ;; wasn't visited by other means.
+  (let ((file (expand-file-name (car ebrowse-tags-next-file-list)
+                               ebrowse-tags-next-file-path)))
+    (setq ebrowse-tags-loop-last-file (if (get-file-buffer file) nil file))
+    ;; Find the file and pop the file list. Pop has to be done
+    ;; before the file is loaded because FIND-FILE might encounter
+    ;; an error, and we want to be able to proceed with the next
+    ;; file in this case.
+    (pop ebrowse-tags-next-file-list)
+    (find-file file)))
+
+
+;;;###autoload
+(defun ebrowse-tags-loop-continue (&optional first-time tree-buffer)
+  "Repeat last operation on files in tree.
+FIRST-TIME non-nil means this is not a repetition, but the first time.
+TREE-BUFFER if indirectly specifies which files to loop over."
+  (interactive)
+  (when first-time
+    (ebrowse-tags-next-file first-time tree-buffer)
+    (goto-char (point-min)))
+  (while (not (eval ebrowse-tags-loop-form))
+    (ebrowse-tags-next-file)
+    (message "Scanning file `%s'..." buffer-file-name)
+    (goto-char (point-min))))
+
+
+;;###autoload
+(defun ebrowse-tags-search (regexp)
+  "Search for REGEXP in all files in a tree.
+If marked classes exist, process marked classes, only.
+If regular expression is nil, repeat last search."
+  (interactive "sTree search (regexp): ")
+  (if (and (string= regexp "")
+          (eq (car ebrowse-tags-loop-form) 're-search-forward))
+      (ebrowse-tags-loop-continue)
+    (setq ebrowse-tags-loop-form (list 're-search-forward regexp nil t))
+    (ebrowse-tags-loop-continue 'first-time)))
+
+
+;;;###autoload
+(defun ebrowse-tags-query-replace (from to)
+  "Query replace FROM with TO in all files of a class tree.
+With prefix arg, process files of marked classes only."
+  (interactive 
+   "sTree query replace (regexp): \nsTree query replace %s by: ")
+  (setq ebrowse-tags-loop-form
+       (list 'and (list 'save-excursion
+                        (list 're-search-forward from nil t))
+             (list 'not (list 'perform-replace from to t t nil))))
+  (ebrowse-tags-loop-continue 'first-time))
+
+
+;;; ###autoload
+(defun ebrowse-tags-search-member-use (&optional fix-name)
+  "Search for call sites of a member.
+If FIX-NAME is specified, search uses of that member.
+Otherwise, read a member name from the minibuffer.
+Searches in all files mentioned in a class tree for something that
+looks like a function call to the member."
+  (interactive)
+  ;; Choose the tree to use if there is more than one.
+  (multiple-value-bind (tree header tree-buffer)
+      (ebrowse-choose-tree)
+    (unless tree
+      (error "No class tree"))
+    ;; Get the member name NAME (class-name is ignored).
+    (let ((name fix-name) class-name regexp)
+      (unless name
+       (multiple-value-setq (class-name name)
+         (ebrowse-tags-read-name header "Find calls of: ")))
+      ;; Set tags loop form to search for member and begin loop.
+      (setq regexp (concat "\\<" name "[ \t]*(")
+           ebrowse-tags-loop-form (list 're-search-forward regexp nil t))
+      (ebrowse-tags-loop-continue 'first-time tree-buffer))))
+
+
+\f
+;;; Tags position management
+
+;;; Structures of this kind are the elements of the position stack.
+
+(defstruct (ebrowse-position (:type vector) :named)
+  file-name                            ; in which file
+  point                                        ; point in file
+  target                               ; t if target of a jump
+  info)                                        ; (CLASS FUNC MEMBER) jumped to
+
+
+(defvar ebrowse-position-stack ()
+  "Stack of `ebrowse-position' structured.")
+
+
+(defvar ebrowse-position-index 0
+  "Current position in position stack.")
+
+
+(defun ebrowse-position-name (position)
+  "Return an identifying string for POSITION.
+The string is printed in the electric position list buffer."
+  (let ((info (ebrowse-position-info position)))
+    (concat (if (ebrowse-position-target position) "at " "to ")
+           (ebrowse-cs-name (ebrowse-ts-class (first info)))
+           "::" (ebrowse-ms-name (third info)))))
+
+
+(defun ebrowse-view/find-position (position &optional view)
+  "Position point on POSITION.
+If VIEW is non-nil, view the position, otherwise find it."
+  (cond ((not view)
+        (find-file (ebrowse-position-file-name position))
+        (goto-char (ebrowse-position-point position)))
+       (t
+        (unwind-protect
+            (progn
+              (push (function 
+                     (lambda ()
+                       (goto-char (ebrowse-position-point position))))
+                    view-mode-hook)
+              (view-file (ebrowse-position-file-name position)))
+          (pop view-mode-hook)))))
+
+
+(defun ebrowse-push-position (marker info &optional target)
+  "Push current position on position stack.
+MARKER is the marker to remember as position.
+INFO is a list (CLASS FUNC MEMBER) specifying what we jumped to.
+TARGET non-nil means we performed a jump.
+Positions in buffers that have no file names are not saved."
+  (when (buffer-file-name (marker-buffer marker))
+    (let ((too-much (- (length ebrowse-position-stack)
+                      ebrowse-max-positions)))
+      ;; Do not let the stack grow to infinity.
+      (when (plusp too-much)
+       (setq ebrowse-position-stack
+             (butlast ebrowse-position-stack too-much)))
+      ;; Push the position.
+      (push (make-ebrowse-position
+            :file-name (buffer-file-name (marker-buffer marker))
+            :point (marker-position marker)
+            :target target
+            :info info) 
+           ebrowse-position-stack))))
+
+
+(defun ebrowse-move-in-position-stack (increment)
+  "Move by INCREMENT in the position stack."
+  (let ((length (length ebrowse-position-stack)))
+    (when (zerop length)
+      (error "No positions remembered"))
+    (setq ebrowse-position-index
+         (mod (+ increment ebrowse-position-index) length))
+    (message "Position %d of %d " ebrowse-position-index length)
+    (ebrowse-view/find-position (nth ebrowse-position-index
+                                    ebrowse-position-stack))))
+
+
+;;; ###autoload
+(defun ebrowse-back-in-position-stack (arg)
+  "Move backward in the position stack.
+Prefix arg ARG says how much."
+  (interactive "p")
+  (ebrowse-move-in-position-stack (max 1 arg)))
+
+
+;;; ###autoload
+(defun ebrowse-forward-in-position-stack (arg)
+  "Move forward in the position stack.
+Prefix arg ARG says how much."
+  (interactive "p")
+  (ebrowse-move-in-position-stack (min -1 (- arg))))
+
+
+\f
+;;; Electric position list
+
+(defvar ebrowse-electric-position-mode-map ()
+  "Keymap used in electric position stack window.")
+
+
+(defvar ebrowse-electric-position-mode-hook nil
+  "If non-nil, its value is called by ebrowse-electric-position-mode.")
+
+
+(unless ebrowse-electric-position-mode-map
+  (let ((map (make-keymap))
+       (submap (make-keymap)))
+    (setq ebrowse-electric-position-mode-map map)
+    (fillarray (car (cdr map)) 'ebrowse-electric-position-undefined)
+    (fillarray (car (cdr submap)) 'ebrowse-electric-position-undefined)
+    (define-key map "\e" submap)
+    (define-key map "\C-z" 'suspend-emacs)
+    (define-key map "\C-h" 'Helper-help)
+    (define-key map "?" 'Helper-describe-bindings)
+    (define-key map "\C-c" nil)
+    (define-key map "\C-c\C-c" 'ebrowse-electric-position-quit)
+    (define-key map "q" 'ebrowse-electric-position-quit)
+    (define-key map " " 'ebrowse-electric-select-position)
+    (define-key map "\C-l" 'recenter)
+    (define-key map "\C-u" 'universal-argument)
+    (define-key map "\C-p" 'previous-line)
+    (define-key map "\C-n" 'next-line)
+    (define-key map "p" 'previous-line)
+    (define-key map "n" 'next-line)
+    (define-key map "v" 'ebrowse-electric-view-position)
+    (define-key map "\C-v" 'scroll-up)
+    (define-key map "\ev" 'scroll-down)
+    (define-key map "\e\C-v" 'scroll-other-window)
+    (define-key map "\e>" 'end-of-buffer)
+    (define-key map "\e<" 'beginning-of-buffer)
+    (define-key map "\e>" 'end-of-buffer)))
+
+(put 'ebrowse-electric-position-mode 'mode-class 'special)
+(put 'ebrowse-electric-position-undefined 'suppress-keymap t)
+
+
+(defun ebrowse-electric-position-mode ()
+  "Mode for electric position buffers.
+Runs the hook `ebrowse-electric-position-mode-hook'."
+  (kill-all-local-variables)
+  (use-local-map ebrowse-electric-position-mode-map)
+  (setq mode-name "Electric Position Menu"
+       mode-line-buffer-identification "Electric Position Menu")
+  (when (memq 'mode-name mode-line-format)
+    (setq mode-line-format (copy-sequence mode-line-format))
+    (setcar (memq 'mode-name mode-line-format) "Positions"))
+  (make-local-variable 'Helper-return-blurb)
+  (setq Helper-return-blurb "return to buffer editing"
+       truncate-lines t
+       buffer-read-only t
+       major-mode 'ebrowse-electric-position-mode)
+  (run-hooks 'ebrowse-electric-position-mode-hook))
+
+
+(defun ebrowse-draw-position-buffer ()
+  "Display positions in buffer *Positions*."
+  (set-buffer (get-buffer-create "*Positions*"))
+  (setq buffer-read-only nil)
+  (erase-buffer)
+  (insert "File           Point  Description\n"
+         "----           -----  -----------\n")
+  (dolist (position ebrowse-position-stack)
+    (insert (file-name-nondirectory (ebrowse-position-file-name position)))
+    (indent-to 15)
+    (insert (int-to-string (ebrowse-position-point position)))
+    (indent-to 22)
+    (insert (ebrowse-position-name position) "\n"))
+  (setq buffer-read-only t))
+
+
+;;; ###autoload
+(defun ebrowse-electric-position-menu ()
+  "List positions in the position stack in an electric buffer."
+  (interactive)
+  (unless ebrowse-position-stack
+    (error "No positions remembered"))
+  (let (select buffer window)
+    (save-window-excursion
+      (save-window-excursion (ebrowse-draw-position-buffer))
+      (setq window (Electric-pop-up-window "*Positions*")
+           buffer (window-buffer window))
+      (shrink-window-if-larger-than-buffer window)
+      (unwind-protect
+         (progn
+           (set-buffer buffer)
+           (ebrowse-electric-position-mode)
+           (setq select
+                 (catch 'ebrowse-electric-select-position
+                   (message "<<< Press Space to bury the list >>>")
+                   (let ((first (progn (goto-char (point-min))
+                                       (forward-line 2)
+                                       (point)))
+                         (last (progn (goto-char (point-max))
+                                      (forward-line -1)
+                                      (point)))
+                         (goal-column 0))
+                     (goto-char first)
+                     (Electric-command-loop 'ebrowse-electric-select-position
+                                            nil t
+                                            'ebrowse-electric-position-looper
+                                            (cons first last))))))
+       (set-buffer buffer)
+       (bury-buffer buffer)
+       (message nil)))
+    (when select
+      (set-buffer buffer)
+      (ebrowse-electric-find-position select))
+    (kill-buffer buffer)))
+
+
+(defun ebrowse-electric-position-looper (state condition)
+  "Prevent moving point on invalid lines.
+Called from `Electric-command-loop'.  See there for the meaning
+of STATE and CONDITION."
+  (cond ((and condition
+             (not (memq (car condition) '(buffer-read-only
+                                          end-of-buffer
+                                          beginning-of-buffer))))
+        (signal (car condition) (cdr condition)))
+       ((< (point) (car state))
+        (goto-char (point-min))
+        (forward-line 2))
+       ((> (point) (cdr state))
+        (goto-char (point-max))
+        (forward-line -1)
+        (if (pos-visible-in-window-p (point-max))
+            (recenter -1)))))
+
+
+(defun ebrowse-electric-position-undefined ()
+  "Function called for undefined keys."
+  (interactive)
+  (message "Type C-h for help, ? for commands, q to quit, Space to execute")
+  (sit-for 4))
+
+
+(defun ebrowse-electric-position-quit ()
+  "Leave the electric position list."
+  (interactive)
+  (throw 'ebrowse-electric-select-position nil))
+
+
+(defun ebrowse-electric-select-position ()
+  "Select a position from the list."
+  (interactive)
+  (throw 'ebrowse-electric-select-position (point)))
+
+
+(defun ebrowse-electric-find-position (point &optional view)
+  "View/find what is described by the line at POINT.
+If VIEW is non-nil, view else find source files."
+  (let ((index (- (count-lines (point-min) point) 2)))
+    (ebrowse-view/find-position (nth index
+                                    ebrowse-position-stack) view)))
+
+
+(defun ebrowse-electric-view-position ()
+  "View the position described by the line point is in."
+  (interactive)
+  (ebrowse-electric-find-position (point) t))
+
+
+\f
+;;; Saving trees to disk
+
+(defun ebrowse-write-file-hook-fn ()
+  "Write current buffer as a class tree.
+Installed on `local-write-file-hooks'."
+  (ebrowse-save-tree)
+  t)
+
+
+;;; ###autoload
+(defun ebrowse-save-tree ()
+  "Save current tree in same file it was loaded from."
+  (interactive)
+  (ebrowse-save-tree-as (or buffer-file-name ebrowse--tags-file-name)))
+
+
+;;;###autoload
+(defun ebrowse-save-tree-as (&optional file-name)
+  "Write the current tree data structure to a file.
+Read the file name from the minibuffer if interactive.
+Otherwise, FILE-NAME specifies the file to save the tree in."
+  (interactive "FSave tree as: ")
+  (let ((temp-buffer (get-buffer-create "*Tree Output"))
+       (old-standard-output standard-output)
+       (header (copy-ebrowse-hs ebrowse--header))
+       (tree ebrowse--tree))
+    (unwind-protect
+       (save-excursion
+         (set-buffer (setq standard-output temp-buffer))
+         (erase-buffer)
+         (setf (ebrowse-hs-member-table header) nil)
+         (insert (prin1-to-string header) " ")
+         (mapcar 'ebrowse-save-class tree)
+         (write-file file-name)
+         (message "Tree written to file `%s'" file-name))
+      (kill-buffer temp-buffer)
+      (set-buffer-modified-p nil)
+      (ebrowse-update-tree-buffer-mode-line)
+      (setq standard-output old-standard-output))))
+
+
+(defun ebrowse-save-class (class)
+  "Write single class CLASS to current buffer."
+  (message "%s..." (ebrowse-cs-name (ebrowse-ts-class class)))
+  (insert "[ebrowse-ts ")
+  (prin1 (ebrowse-ts-class class))     ;class name
+  (insert "(")                         ;list of subclasses
+  (mapcar 'ebrowse-save-class (ebrowse-ts-subclasses class))
+  (insert ")")
+  (dolist (func ebrowse-member-list-accessors)
+    (prin1 (funcall func class))
+    (insert "\n"))
+  (insert "()")                                ;base-classes slot
+  (prin1 (ebrowse-ts-mark class))
+  (insert "]\n"))
+
+
+\f
+;;; Statistics
+
+;;; ###autoload
+(defun ebrowse-statistics ()
+  "Display statistics for a class tree."
+  (interactive)
+  (let ((tree-file (buffer-file-name))
+       temp-buffer-setup-hook)
+    (with-output-to-temp-buffer "*Tree Statistics*"
+      (multiple-value-bind (classes member-functions member-variables
+                                   static-functions static-variables)
+         (ebrowse-gather-statistics)
+       (set-buffer standard-output)
+       (erase-buffer)
+       (insert "STATISTICS FOR TREE " (or tree-file "unknown") ":\n\n")
+       (ebrowse-print-statistics-line "Number of classes:" classes)
+       (ebrowse-print-statistics-line "Number of member functions:"
+                                      member-functions)
+       (ebrowse-print-statistics-line "Number of member variables:"
+                                      member-variables)
+       (ebrowse-print-statistics-line "Number of static functions:"
+                                      static-functions)
+       (ebrowse-print-statistics-line "Number of static variables:"
+                                      static-variables)))))
+
+
+(defun ebrowse-print-statistics-line (title value)
+  "Print a line in the statistics buffer.
+TITLE is the title of the line, VALUE is number to be printed
+after that."
+  (insert title)
+  (indent-to 40)
+  (insert (format "%d\n" value)))
+
+
+(defun ebrowse-gather-statistics ()
+  "Return statistics for a class tree.
+The result is a list (NUMBER-OF-CLASSES NUMBER-OF-MEMBER-FUNCTIONS
+NUMBER-OF-INSTANCE-VARIABLES NUMBER-OF-STATIC-FUNCTIONS
+NUMBER-OF-STATIC-VARIABLES:"
+  (let ((classes 0) (member-functions 0) (member-variables 0)
+       (static-functions 0) (static-variables 0))
+    (ebrowse-for-all-trees (tree ebrowse--tree-obarray)
+      (incf classes)
+      (incf member-functions (length (ebrowse-ts-member-functions tree)))
+      (incf member-variables (length (ebrowse-ts-member-variables tree)))
+      (incf static-functions (length (ebrowse-ts-static-functions tree)))
+      (incf static-variables (length (ebrowse-ts-static-variables tree))))
+    (list classes member-functions member-variables
+         static-functions static-variables)))
+
+
+\f
+;;; Global key bindings
+
+;;; The following can be used to bind key sequences starting with
+;;; prefix `\C-cb' to browse commands.
+
+(defvar ebrowse-global-map nil
+  "*Keymap for Ebrowse commands.")
+
+
+(defvar ebrowse-global-prefix-key "\C-cb"
+  "Prefix key for Ebrowse commands.")
+
+
+(defvar ebrowse-global-submap-4 nil
+  "Keymap used for `ebrowse-global-prefix' followed by `4'.")
+
+
+(defvar ebrowse-global-submap-5 nil
+  "Keymap used for `ebrowse-global-prefix' followed by `5'.")
+
+
+(unless ebrowse-global-map
+  (setq ebrowse-global-map (make-sparse-keymap))
+  (setq ebrowse-global-submap-4 (make-sparse-keymap))
+  (setq ebrowse-global-submap-5 (make-sparse-keymap))
+  (define-key ebrowse-global-map "a" 'ebrowse-tags-apropos)
+  (define-key ebrowse-global-map "b" 'ebrowse-pop-to-browser-buffer)
+  (define-key ebrowse-global-map "-" 'ebrowse-back-in-position-stack)
+  (define-key ebrowse-global-map "+" 'ebrowse-forward-in-position-stack)
+  (define-key ebrowse-global-map "l" 'ebrowse-tags-list-members-in-file)
+  (define-key ebrowse-global-map "m" 'ebrowse-tags-display-member-buffer)
+  (define-key ebrowse-global-map "n" 'ebrowse-tags-next-file)
+  (define-key ebrowse-global-map "p" 'ebrowse-electric-position-menu)
+  (define-key ebrowse-global-map "s" 'ebrowse-tags-search)
+  (define-key ebrowse-global-map "u" 'ebrowse-tags-search-member-use)
+  (define-key ebrowse-global-map "v" 'ebrowse-tags-view-definition)
+  (define-key ebrowse-global-map "V" 'ebrowse-tags-view-declaration)
+  (define-key ebrowse-global-map "%" 'ebrowse-tags-query-replace)
+  (define-key ebrowse-global-map "." 'ebrowse-tags-find-definition)
+  (define-key ebrowse-global-map "f" 'ebrowse-tags-find-definition)
+  (define-key ebrowse-global-map "F" 'ebrowse-tags-find-declaration)
+  (define-key ebrowse-global-map "," 'ebrowse-tags-loop-continue)
+  (define-key ebrowse-global-map " " 'ebrowse-electric-buffer-list)
+  (define-key ebrowse-global-map "\t" 'ebrowse-tags-complete-symbol)
+  (define-key ebrowse-global-map "4" ebrowse-global-submap-4)
+  (define-key ebrowse-global-submap-4 "." 'ebrowse-tags-find-definition-other-window)
+  (define-key ebrowse-global-submap-4 "f" 'ebrowse-tags-find-definition-other-window)
+  (define-key ebrowse-global-submap-4 "v" 'ebrowse-tags-find-declaration-other-window)
+  (define-key ebrowse-global-submap-4 "F" 'ebrowse-tags-view-definition-other-window)
+  (define-key ebrowse-global-submap-4 "V" 'ebrowse-tags-view-declaration-other-window)
+  (define-key ebrowse-global-map "5" ebrowse-global-submap-5)
+  (define-key ebrowse-global-submap-5 "." 'ebrowse-tags-find-definition-other-frame)
+  (define-key ebrowse-global-submap-5 "f" 'ebrowse-tags-find-definition-other-frame)
+  (define-key ebrowse-global-submap-5 "v" 'ebrowse-tags-find-declaration-other-frame)
+  (define-key ebrowse-global-submap-5 "F" 'ebrowse-tags-view-definition-other-frame)
+  (define-key ebrowse-global-submap-5 "V" 'ebrowse-tags-view-declaration-other-frame)
+  (define-key global-map ebrowse-global-prefix-key ebrowse-global-map))
+
+
+\f
+;;; Electric C++ browser buffer menu
+
+;;; Electric buffer menu customization to display only some buffers
+;;; (in this case Tree buffers).  There is only one problem with this:
+;;; If the very first character typed in the buffer menu is a space,
+;;; this will select the buffer from which the buffer menu was
+;;; invoked.  But this buffer is not displayed in the buffer list if
+;;; it isn't a tree buffer.  I therefore let the buffer menu command
+;;; loop read the command `p' via `unread-command-char'.  This command
+;;; has no effect since we are on the first line of the buffer.
+
+(defvar electric-buffer-menu-mode-hook nil)
+
+
+(defun ebrowse-hack-electric-buffer-menu ()
+  "Hack the electric buffer menu to display browser buffers."
+  (let (non-empty)
+    (unwind-protect
+       (save-excursion
+         (setq buffer-read-only nil)
+         (goto-char 1)
+         (forward-line 2)
+         (while (not (eobp))
+           (let ((b (Buffer-menu-buffer nil)))
+             (if (or (ebrowse-buffer-p b)
+                     (string= (buffer-name b) "*Apropos Members*"))
+                 (progn (forward-line 1)
+                        (setq non-empty t))
+               (delete-region (point)
+                              (save-excursion (end-of-line)
+                                              (min (point-max)
+                                                   (1+ (point)))))))))
+      (unless non-empty
+       (error "No tree buffers"))
+      (setf unread-command-events (listify-key-sequence "p"))
+      (shrink-window-if-larger-than-buffer (selected-window))
+      (setq buffer-read-only t))))
+
+
+(defun ebrowse-select-1st-to-9nth ()
+  "Select the nth entry in the list by the keys 1..9."
+  (interactive)
+  (let* ((maxlin (count-lines (point-min) (point-max)))
+        (n (min maxlin (+ 2 (string-to-int (this-command-keys))))))
+    (goto-line n)
+    (throw 'electric-buffer-menu-select (point))))
+
+
+(defun ebrowse-install-1-to-9-keys ()
+  "Define keys 1..9 to select the 1st to 0nth entry in the list."
+  (dotimes (i 9)
+    (define-key (current-local-map) (char-to-string (+ i ?1))
+      'ebrowse-select-1st-to-9nth)))
+
+
+(defun ebrowse-electric-buffer-list ()
+  "Display an electric list of Ebrowse buffers."
+  (interactive)
+  (unwind-protect
+      (progn
+       (add-hook 'electric-buffer-menu-mode-hook
+                 'ebrowse-hack-electric-buffer-menu)
+       (add-hook 'electric-buffer-menu-mode-hook
+                 'ebrowse-install-1-to-9-keys)
+       (call-interactively 'electric-buffer-list))
+    (remove-hook 'electric-buffer-menu-mode-hook
+                'ebrowse-hack-electric-buffer-menu)))
+
+\f
+;;; Mouse support
+
+(defun ebrowse-mouse-find-member (event)
+  "Find the member clicked on in another frame.
+EVENT is a mouse button event."
+  (interactive "e")
+  (mouse-set-point event)
+  (let (start name)
+    (save-excursion
+      (skip-chars-backward "a-zA-Z0-9_")
+      (setq start (point))
+      (skip-chars-forward "a-zA-Z0-9_")
+      (setq name (buffer-substring start (point))))
+    (ebrowse-tags-view/find-member-decl/defn
+     5 :view nil :definition t :member-name name)))
+
+
+(defun ebrowse-popup-menu (menu event)
+  "Pop up MENU and perform an action if something was selected.
+EVENT is the mouse event."
+  (save-selected-window
+    (select-window (posn-window (event-start event)))
+    (let ((selection (x-popup-menu event menu)) binding)
+      (while selection
+       (setq binding (lookup-key (or binding menu) (vector (car selection)))
+             selection (cdr selection)))
+      (when binding
+       (call-interactively binding)))))
+
+
+(easy-menu-define
+ ebrowse-tree-buffer-class-object-menu ebrowse-tree-mode-map 
+ "Object menu for classes in the tree buffer"
+ '("Class"
+   ["Functions" ebrowse-tree-command:show-member-functions
+    :help "Display a list of member functions"
+    :active t]
+   ["Variables" ebrowse-tree-command:show-member-variables
+    :help "Display a list of member variables"
+    :active t]
+   ["Static Functions" ebrowse-tree-command:show-static-member-functions
+    :help "Display a list of static member functions"
+    :active t]
+   ["Static Variables" ebrowse-tree-command:show-static-member-variables
+    :help "Display a list of static member variables"
+    :active t]
+   ["Friends/ Defines" ebrowse-tree-command:show-friends
+    :help "Display a list of friends of a class"
+    :active t]
+   ["Types" ebrowse-tree-command:show-types
+    :help "Display a list of types defined in a class"
+    :active t]
+   "-----------------"
+   ["View" ebrowse-view-class-declaration
+    :help "View class declaration"
+    :active (eq (get-text-property (point) 'ebrowse-what) 'class-name)]
+   ["Find" ebrowse-find-class-declaration
+    :help "Find class declaration in file"
+    :active (eq (get-text-property (point) 'ebrowse-what) 'class-name)]
+   "-----------------"
+   ["Mark" ebrowse-toggle-mark-at-point
+    :help "Mark class point is on"
+    :active (eq (get-text-property (point) 'ebrowse-what) 'class-name)]
+   "-----------------"
+   ["Collapse" ebrowse-collapse-branch
+    :help "Collapse subtree under class point is on"
+    :active (eq (get-text-property (point) 'ebrowse-what) 'class-name)]
+   ["Expand" ebrowse-expand-branch
+    :help "Expand subtree under class point is on"
+    :active (eq (get-text-property (point) 'ebrowse-what) 'class-name)]))
+
+
+(easy-menu-define
+ ebrowse-tree-buffer-object-menu ebrowse-tree-mode-map 
+ "Object menu for tree buffers"
+ '("Ebrowse"
+   ["Filename Display" ebrowse-toggle-file-name-display
+    :help "Toggle display of source files names"
+    :style toggle
+    :selected ebrowse--show-file-names-flag
+    :active t]
+   ["Tree Indentation" ebrowse-set-tree-indentation
+    :help "Set the tree's indentation"
+    :active t]
+   ["Unmark All Classes" ebrowse-mark-all-classes
+    :help "Unmark all classes in the class tree"
+    :active t]
+   ["Expand All" ebrowse-expand-all
+    :help "Expand all subtrees in the class tree"
+    :active t]
+   ["Statistics" ebrowse-statistics
+    :help "Show a buffer with class hierarchy statistics"
+    :active t]
+   ["Find Class" ebrowse-read-class-name-and-go
+    :help "Find a class in the tree"
+    :active t]
+   ["Member Buffer" ebrowse-pop/switch-to-member-buffer-for-same-tree
+    :help "Show a member buffer for this class tree"
+    :active t]))
+
+
+(defun ebrowse-mouse-3-in-tree-buffer (event)
+  "Perform mouse actions in tree buffers.
+EVENT is the mouse event."
+  (interactive "e")
+  (mouse-set-point event)
+  (let* ((where (posn-point (event-start event)))
+        (property (get-text-property where 'ebrowse-what)))
+    (case (event-click-count event)
+      (1
+       (case property
+        (class-name
+         (ebrowse-popup-menu ebrowse-tree-buffer-class-object-menu event))
+        (t
+         (ebrowse-popup-menu ebrowse-tree-buffer-object-menu event)))))))
+
+
+(defun ebrowse-mouse-2-in-tree-buffer (event)
+  "Perform mouse actions in tree buffers.
+EVENT is the mouse event."
+  (interactive "e")
+  (mouse-set-point event)
+  (let* ((where (posn-point (event-start event)))
+        (property (get-text-property where 'ebrowse-what)))
+    (case (event-click-count event)
+      (1 (case property
+          (class-name
+           (ebrowse-tree-command:show-member-functions)))))))
+
+
+(defun ebrowse-mouse-1-in-tree-buffer (event)
+  "Perform mouse actions in tree buffers.
+EVENT is the mouse event."
+  (interactive "e")
+  (mouse-set-point event)
+  (let* ((where (posn-point (event-start event)))
+        (property (get-text-property where 'ebrowse-what)))
+    (case (event-click-count event)
+      (2 (case property
+          (class-name
+           (let ((collapsed (save-excursion (skip-chars-forward "^\r\n")
+                                            (looking-at "\r"))))
+             (ebrowse-collapse-fn (not collapsed))))
+          (mark
+           (ebrowse-toggle-mark-at-point 1)))))))
+
+
+\f
+;;; Hooks installed
+
+(add-hook 'find-file-hooks 'ebrowse-find-file)
+
+\f
+(provide 'ebrowse)
+
+;;; Local variables:
+;;; eval:(put 'ebrowse-output 'lisp-indent-hook 0)
+;;; eval:(put 'ebrowse-ignoring-completion-case 'lisp-indent-hook 0)
+;;; eval:(put 'ebrowse-save-selective 'lisp-indent-hook 0)
+;;; eval:(put 'ebrowse-for-all-trees 'lisp-indent-hook 1)
+;;; End:
+
+;;; ebrowse.el ends here.    
+
index 2e2358e067993dc82ec8ec97aef9aa596b1428a7..6efbd878aee0cd4d9a96f3ec28a9c5bd81d0a2e5 100644 (file)
@@ -1,3 +1,8 @@
+2000-04-09  Gerd Moellmann  <gerd@gnu.org>
+
+       * buffer.c (Frestore_buffer_modified_p): New function.
+       (syms_of_buffer): Defsubr it.
+
 2000-04-08  Ken Raeburn  <raeburn@gnu.org>
 
        * charset.c (Fmake_char_internal): CHAR_COMPONENTS_VALID_P takes a